Case: Upgrade Litium 5 to Litium 7

This document details an upgrade of Litium 5 to Litium 7 performed by Litium staff. Here you can review our findings and apply them to your own upgrade project.

Note! The intention is not to provide step-by-step instructions, since the upgrade process will be unique for the customer project, but instead to give directions on what you need to change.

Scope

The scope of the project was to have a running solution of a standard Litium 5 accelerator upgraded to Litium 7.2 with the following configuration.

  • Standard accelerator
  • Integration kit with customization
  • Product media mapper
  • Custom panel
  • Custom page type
  • Custom field type
  • One B2C website

Upgrade approach

  • Upgrade your solution and your database to latest version
  • Exclude all C# files and views from all projects
  • Unload any integrations projects
  • Implement the routing changes. See the Routing section of Litium.Accelerator below
  • Include only the StartPageController, StartPageViewModel and the index.cshtml and try to compile
  • Add all the required views, classes in order to be able to compile the StartPage
  • Do the same procedure for all other page types
  • Load the integration projects one by one and ensure that It will compile

Sample code

Click here to download sample code

Click here to download complete project source code

Note! The source code is proved as is and should be used at your own risk. The lessons learned are in this documentation, not necessarily in the source code. Areas and functionality specifically related to B2B were not upgraded.

General information

Page templates

The name of the page field templates is changed after the upgrade. Before Litium 7, a page consisted of a page type (which defined the fields) and one or more templates to display the fields. When upgrading to Litium 7 each template is converted to a unique field template.

The name of the page type is used in the code and since the upgrade has created the new field templates with the old template name, use the following scripts in the database to correct the field template id. In this example the new field template was created with the name of the old template “StartPage1”. Since the name of the page type “StartPage” is used in the code we must change “StartPage1” to “StartPage”.

UPDATE FieldFramework.FieldTemplate SET Id = 'StartPage' WHERE AreaType = 'Litium.Websites.WebsiteArea, Litium.Abstractions' AND Id = 'StartPage1'

Channel domain prefix

If you're getting unexplainable 404 errors, it could be because the channel domains were not upgraded properly (you have an empty string instead of null for prefix in the database). In such a case, you need to reset the channel domain prefix. You can do that from the back office by adding a temporary value for the prefix and then removing it.

DomainPrefix.png

Currency symbol

Currency symbol is not upgraded properly. You need to set symbol, separator and position manually in the back office.

Currency.png

Left menu

ShowInMenu is removed from the page object. Left navigation settings will be moved to the website and you will define only the pages that will have the left menu. All other pages will not show the left menu.

Litium.Accelerator

In this section we will go through all the default folders in the accelerator project one by one.

Attributes

Insert the necessary classes (that you have excluded before) back to solution.

Caching

Instead of rewriting the Litium.Accelerator.Caching.ItemFromPageTypeCache insert Litium.Accelerator.Caching.PageFieldTemplateCache from the latest accelerator. Litium.Accelerator.Caching.ItemFromPageTypeCache will wrap that one. In order to cache a page using a field template create a class for the field template inheriting the PageByFieldTemplateCacheType in the Litium.Accelerator.Caching.PageFieldTemplateCache class. Use Litium.Accelerator.Caching.PageFieldTemplateCache for sections also and remove Litium.Accelerator.Caching.SectionCategoryPageType cache. See the included folder Caching for examples.

CMS

Panels

Litium 7 does not have support for the edit panels that were used for editing complex data (a group of related fields) in previous versions. These complex data must be converted to multi-fields if it is necessary to keep them on the page object.

Usually specific settings pages would contain this type of data. In Litium 7, market, channel and websites support the field framework. It is required to move settings information to appropriate globalization entity or website and remove the setting pages since it is not possible to edit setting pages using panels. However, in order to be able to launch the start page you can keep the settings pages in the solution and insert their data objects back to the solution. See the included folder Settings for examples.

When you are going to move setting fields to appropriate globalization entity or website keep the following in mind

  • Payment and delivery options are now set on the channel
  • Logos, page pointers, general site settings, header and footer settings should be set on the website
  • This means that any view that references any settings needs to get that information from the view model instead (for example NavigationType)

In general, any setting that can be shared between channels should be added to the Website entity while anything unique for the channel should be modeled on the Channel entity.

Examples:

Get all the payment method system ids on a channel

currentChannel?.CountryLinks?.FirstOrDefault()?.PaymentMethodSystemIds

Get all the delivery method system ids on a channel

currentChannel?.CountryLinks?.FirstOrDefault()?.DeliveryMethodSystemIds

Get a field value from channel, market or website. See also Litium.Accelerator.Extensions.WebsiteExtensions from the latest accelerator.

var fieldValue = currentObject.Fields.GetValue<typeOfProperty>(NameOfTheProperty)

Settings

We recommend that you move settings fields to appropriate globalization entity instead of setting pages and remove the setting pages. If you want to use the setting pages in order to launch the start page immediately during the upgrade insert settings classes back to the solution. They have to use the new Litium.Accelerator.Caching.ItemFromPageTypeCache.ItemFromPageTypeCache, channelSystemId instead of website id and the use Litium.Accelerator.Services.PropertyService instead of Litium.Accelerator.Utilities.PropertyUtilities. See the Services section and the included folder Settings for examples.

Constants

Since the definition files are not used anymore, new constants must be added for website field names, page field names, product field names, navigation and search query. PageTypes constants will have the name of all field template types that are used. See the included folder Constants for examples.

ContentProviders

ContentProviderContext

CurrentState, PageRequestData must be removed from the Litium.Accelerator.ContentProviders.ContentProviderContext since they are deprecated. Extend the Litium.Accelerator.ContentProviders.ContentProviderContext with properties Channel, IsInAdministration, Page, Website, Currency, Cart and Data. All the content providers get this information from the Litium.Accelerator.ContentProviders.ContentProviderContext. See the included folder Content providers for example.

IContentProvider

Add the property Litium.Accelerator.ContentProviders.ContentProviderContext.

ContentProvider

Implements the new interface property.

Providers

Insert the necessary classes (that you have excluded before) back to solution and ensure that they use the new API. See the quick reference

Sections

Insert the necessary classes (that you have excluded before) back to solution and ensure that they use the new API. See the quick reference

Definitions

This folder and its sub folders can be removed from the project.

Deployments

This folder and its sub folders can be removed from the project.

DynamicRows

DynamicRowData must be updated so it uses the new API. See the quick reference

Ecommerce

Insert the necessary classes (that you have excluded before) back to solution and ensure that they use the new API. See the quick reference

Email

Insert the necessary classes (that you have excluded before) back to solution and ensure that they use the new API. See the quick reference

Exceptions

Insert the necessary classes (that you have excluded before) back to solution and ensure that they use the new API. See the quick reference

Extensions

Extensions must be updated so that they use the new API. See the quick reference. Add Litium.Accelerator.Extensions.MemberConfigurationExpression from the latest accelerator. It is used to map fields from the fields of the pages. See the included folder Article page for example.

Add Litium.Accelerator.Extensions.WebsiteExtensions from the latest accelerator if the setting fields are moved to website. Similar extensions must be developed for other globalization entities if it is necessary.

FieldFramework

Insert the necessary classes (that you have excluded before) back to solution and ensure that they use the new API. See the quick reference

Mailing

Insert the necessary classes (that you have excluded before) back to solution and ensure that they use the new API. See the quick reference

Mvc

Insert the necessary classes (that you have excluded before) back to solution and ensure that they use the new API. See the quick reference

Routing

Add Litium.Accelerator.Routing.RequestModel and Litium.Accelerator.Routing.RequestModelAcessor from the latest accelerator. RequestModel of the RequestModelAccessor is set by a globally registered action filter Litium.Accelerator.Mvc.Routing.RequestModelActionFilter. Change the routing configuration in Litium.Accelerator.Mvc/App_Start to add the filter.

RequestModelAccessor can be used in any action to get information of the current channel, current page, current website and current search query. In case of ajax background calls it must be set in Litium.Accelerator.Mvc.Controllers.LitiumController and the call must have channel id as a query parameter. See the included folder Routing for example.

Search

Insert the necessary classes (that you have excluded before) back to solution and ensure that they use the new API. See the quick reference

Remove the Parse method of the Litium.Accelerator.Search.SearchQueryParser. Add Litium.Accelerator.Mvc.Routing.SearchQueryMapper from the latest accelerator. ChannelId and PageId must be ignored with parsing. See the included folder Search for example.

Services

Insert the necessary classes (that you have excluded before) back to solution and ensure that they use the new API. See the quick reference

Add a service named Litium.Accelerator.Services.PropertyService to get different type of the page field values. See the included folder Services for example

You can also use the field framework directly to get the field values directly.

var fieldValue = currentObject.Fields.GetValue<typeOfProperty>(NameOfTheProperty) 

StateTransition

Insert the necessary classes (that you have excluded before) back to solution and ensure that they use the new API. See the quick reference

Utilities

Insert the necessary classes (that you have excluded before) back to solution and ensure that they use the new API. See the quick reference

Remove Litium.Accelerator.Utilities.PropertyUtilities and use Litium.Accelerator.Services.PropertyService instead if you want. See the Services section and the included folder Services for example.

Litium.Accelerator.Mvc

In this section we will go through all the default folders in the MVC project one by one.

App_Start

Insert Litium.Accelerator.Mvc.RouteConfig (that you have excluded before) back to the solution. Add a global action filter RequestModelActionFilter to the RouteConfig. See the Routing section in Litium.Accelerator and the included folder Routing for examples.

Controllers

Insert the necessary classes (that you have excluded before) back to solution and ensure that they use the new API. See the quick reference

Framework

Insert the necessary classes (that you have excluded before) back to solution and ensure that they use the new API. See the quick reference

Actions can have most of the models injected: ChannelModel, WebsiteModel, PageModel etc. Otherwise they use the data from RequestModelAccessor injected in the constructor of the controller. Inject RouteRequestLookupInfoAccessor and RouteRequestInfoAccessor if necessary or extend the RequestModelAccessor with the necessary data.

Pages

Extend Litium.Accelerator.Mvc.Controllers.LitiumController with InitializeRequestModel in order to set the RouteRequestLookupInfo of the RouteRequestLookupInfoAccessor since the ajax calls are coming with empty RouteRequestLookupInfo. InitializeRequestModel also sets RequestModel of the RequestModelAccessor. See the included folder Routing for example.

Litium

Insert entire folder (that you have excluded before) back to the solution.

Routing

Add Litium.Accelerator.Mvc.Routing.RequestModelActionFilter and Litium.Accelerator.Mvc.Routing.SearchQueryMapper from the latest accelerator. See the Routing and Search sections in Litium.Accelerator.

UI

Insert entire folder (that you have excluded before) back to the solution.

ViewModels

Insert the necessary view models (that you have excluded before) back to solution and ensure that they use the new API. See the quick reference

Views

Insert the necessary views (that you have excluded before) back to solution and ensure that they use the new API. See the quick reference

Updating pages

General information

Here are some common changes in how you handle working with pages.

Page name

page.ShortName => page.Localizations.CurrentUICulture.Name

Get pages with id

page.GetFromIds => pageService.Get

Check if the page is published

page.IsPublished => x.Status == Common.ContentStatus.Published

Get top page

var topPage = webSite.GetTopPage(_token) => var topPage = _pageService.GetChildPages(Guid.Empty, webSite.SystemId).FirstOrDefault();

Get url to top page

var channel = _channelService.Get(channelSystemId);
var urlTopPage = _urlService.GetUrl(channel);

Check read/write permissions

_authorizationService.HasOperation<Page>(Operations.Entity.Read, pageSystemId)

Note! In the old API a token was sent to Get method for pages, and it checked the access rights. In the new API we must check the access rights explicitly after getting the page.

Get url to page

page.GetUrlToPage(_webSiteId, _isInAdministrationMode)  => _urlService.GetUrl(page, new PageUrlArgs(channelSystemId) { AbsoluteUrl = false})

Things to keep in mind

  • Converted fields will not support multiple languages automatically, you must identify which ones that should and update them accordingly
    • Update the field definition in the database and set MultiCulture to 1, then add the correct culture to the current value stored in Sites.PageFieldData
    • You can also delete the original field and re-create it, but that will also delete the data, so it is not recommended
  • Fields that used custom panels in the old solution – like Section Products – will not be converted automatically
  • A lot of texts that are shown on the site are saved on the page in the old solution (like the Checkout page), you can either add mapping configurations for all of them in your ViewModel or consider moving them to website texts
  • StandardSetting is obsolete, you instead need to handle which fields are shown and required in code
  • Custom fields on the person are obsolete, you should use the field framework to create your person data model
  • You can print website strings by using the string extension .AsWebSiteString()

Converting controllers and view models to Litium 7

General tips

  • Start with one page type and for its controller, start with one Action
  • Content is published for the channel, not the website, so you should look at ChannelLinks when deciding if for example a category or page should be included in a list
  • If you need the current Channel, Website, Country or SearchQuery, inject the RequestModelAccessor.

Documentation on model binding and dependency injection in Litium 7

The page definition is no longer injected into the Action, instead you will use an object of type PageModel. From this you can map the page data to the view model. Mapping is not done automatically and each field you want to map needs to be configured on the view model.

Updating the Article page

Here’s an example of what to do when converting the standard Article page from Litium 5.

  • Litium.Accelerator.Mvc.Controllers.Pages.ArticleController
    • Remove the PageProperties and PageTemplate attributes
    • Update the method to receive a PageModel instead of the page definition
  • Litium.Accelerator.Mvc.ViewModels.ArticleViewModel
    • The mapping should be done from PageModel to the view model
    • Update the AutoMapper configuration for each property (suggestions below)
  • View
    • No updates necessary to the Article view
    • You need to update FileModel.cshtml since FileExtension is obsoleted, the related parts can be removed if you don’t have any need to display icons together with files (the standard Accelerator only uses one icon anyway)

Mapping the properties of ArticleViewModel

The examples below assume you have added the MemberConfigurationExpression extension class from a Litium 7 Accelerator, so you can use the MapFromField extension. This extension simplifies mapping when the type can be inferred from the property.

It is recommended to create a definition of constants that can be re-used when mapping fields. Make sure the field names are correct for your solution.

Title

.ForMember(x => x.Title, m => m.MapFromField(PageFieldNameConstants.Title))

Introduction

.ForMember(x => x.Introduction, m => m.MapFromField(PageFieldNameConstants.Introduction))

Text1

.ForMember(x => x.Text1, m => m.MapFrom(articlePage => articlePage.GetValue<string>(PageFieldNameConstants.Text)))

Links

.ForMember(x => x.Links, m => m.MapFrom(articlePage => articlePage.GetValue<IList<PointerItem>>(PageFieldNameConstants.Links) != null ? articlePage.GetValue<IList<PointerItem>>(PageFieldNameConstants.Links).OfType<PointerPageItem>().ToList().Select(x => x.MapTo<LinkModel>()) : new List<LinkModel>()))

Files

.ForMember(x => x.Files, m => m.MapFrom(articlePage => articlePage.GetValue<IList<Guid>>(PageFieldNameConstants.Files).Select(x => x.MapTo<FileModel>()))

Image1

The old image panel is converted to a multi-field in the upgrade process, so you first need to retrieve the multifield and then retrieve the field data you want.

Read more about multi-fields here

It’s helpful to create a resolver class that you can re-use in other mappings.

public class ImageResolver : IValueResolver<PageModel, object, ImageModel>
{
    public ImageModel Resolve(PageModel source, object destination, ImageModel destMember, ResolutionContext context)
    {
        var imageMultiField = source.GetValue<IList<MultiFieldItem>>(PageFieldNameConstants.Image);

        if (imageMultiField == null)
            return null;

        var image = imageMultiField.FirstOrDefault()?.Fields.GetValue<Guid>(PageFieldNameConstants.ImageMultiField).MapTo<ImageModel>();

        if (image == null)
            return null;

    image.Alt = imageMultiField.FirstOrDefault()?.Fields.GetValue<string>(PageFieldNameConstants.ImageAlternativeTextMultiField);

        return image;
    }
}

You can then use the resolver in your mapping.

.ForMember(x => x.Image1, m => m.MapFrom<ImageResolver>())

See the included folder Article page for example.

Add block support

  1. Copy _BlockContainer.cshtml from the latest accelerator and add to the project in Litium.Accelerator.Mvc/Views/Shared/Framework/
  2. Copy BlockContainerHelperExtension from the latest accelerator and add to the project in the root of Litium.Accelerator.Mvc
  3. To avoid having to add using statements for the extension, add this to your Litium.Accelerator.Mvc.Views/Web.config file, under namespaces
    <add namespace="Litium.Accelerator.Mvc"/>
  4. Add the following to the View of the page type you want to add block support to
    @Html.BlockContainer(Model.Blocks, "Name of container")
  5. Update the ViewModel with a Blocks property
    public Dictionary<string, List<BlockModel>> Blocks { get; set; }
  6. Add a Block container to the page you're working on. You can do this by editing the field template under Settings > Websites and editing the field Containers. You'll reference the name of the container in your code, see "Name of container" above)

After that you need to set up the blocks you want to use. You can create the templates by code (check the Litium 7 accelerator for examples) or create them manually in the back office.

Read more about the blocks architecture here

If you want to create block versions of your sections, consider re-using the same view models and views. You will need to add a new controller, add mappings on the view model if necessary and to update any builders used. See the included folder Converted sections for examples.

Upgrade integration kit

  1. Backup your integration kit add-on code
  2. Copy the latest integration kit add-on code
  3. Move your custom changes in the old add-on code to the new one.
  4. Adjust ProductData.xml
    1. Tax class concept is new in Litium 7. Base product will have a tax class and taxes are calculated according to tax value of the tax class defined on the country. If you are going to import tax class information via integration put the tax class information to your ProductData.xml. See the examples below.
      To create the tax class in Litium:
      <taxClass>
          <id>Clothes</id>
          <fields>
            <field culture="en-US" name="_name">Clothes US</field>
            <field culture="sv-SE" name="_name">Clothes SE</field>
          </fields>
      </taxClass>
      
      To apply a tax class to a base product:
      <baseProduct>
         <id>BP_1</id>
         <fieldTemplateId>ProductWithVariants</fieldTemplateId>
         <taxClassId>Clothes</taxClassId>
         .
         .
    2. Channel concept is new in Litium 7. Products, Categories and Pages are published on channels now not on the websites.
      Change the publish node of the variants in your ProductData.xml so that the variants are published one channels. Ensure that the channel ids are the website names if you are not going to change publishing process. See the example below.
      Change
      <publish>
         <add webSite="B2C"/>
         <remove webSite="B2B"/>
      </publish>
      
      to
      <publish>
         <add channel="B2C"/>
         <remove channel="B2B"/>
      </publish>
    3. Handling of the inventory items has been changed in Litium 7. Move the unit of measurements outside the inventory items node. See the example below.
      <inventoryItems>
        <inventoryItem>
          <inventoryId>SampleInventory</inventoryId>
          <inStockQuantity>100</inStockQuantity>
          <erpStatus>1</erpStatus>
        </inventoryItem>
      </inventoryItems>
      <unitOfmeasurementId>st</unitOfmeasurementId>
    4. Add the category information to variant nodes also if it is missing. See example below.
      <categories>
        <add categoryId="Diamonds"/>
        <remove categoryId="Saphir"/>
      </categories>
    5. Vat percentage is not necessary anymore in the price node, remove it. See example below.
      <prices>
       <add priceListId="iSEK" price="200" minQuantity = "10"/>
  5. Adjust price list import file. Remove the vat percentage. See example below.
    636903202588649221-23788283	80	1
    636903202588649221-23788283	78	50
    636903202588649221-23788283	75	100
    636903202588649221-23788284	250
    636903202588649221-23788282	500
  6. Adjust stock balance import file. Remove the unit of measurements. See example below.
    636903202588649221-23788283	800
    636903202588649221-23788284	900
    636903202588649221-23788282	1000

Upgrade Klarna Checkout

  1. Update the GetPaymentMethodsSettingsFromPanel method of Litium.Accelerator.Services.PaymentService.PaymentMethodDataCacheService so that it gathers the information from the country that is connected to the current channel.
  2. Move the IPaymentWidget implementation from Litium.Accelerator.Mvc.Controllers.Pages.KlarnaPaymentWidgetController to the Litium.Accelerator.Mvc.KlarnaPaymentWidgetConfigX class and adjust it by comparing with the one from the latest accelerator.
  3. Adjust the Litium.Accelerator.Mvc.Controllers.Pages.KlarnaPaymentWidgetController by comparing Litium.Accelerator.Services.Payments.KlarnaPaymentService from the latest accelerator.

See the included folder Klarna for examples.

Recommendations

We recommend merging several websites with different languages to one website if they have a similar structure.

Is this page helpful?
Thank you for your feedback!