SiteGenesis JavaScript Controllers (SGJC) Standards Compliance

This topic covers the following standards and supported browser versions:

Cookies Notification/Opt-in for European Cookie Law

European Cookie Law requires websites to notify customers that cookies are being used and how. The SiteGenesis application uses an optional content asset, called cookie_hint, to contain this notice.

If this asset is... Then...
Missing or offline No notice is given. The cookies are set as they always have been. This approach is used in the USA, for example.
Present and online* The cookie_hint content appears. Clicking I ACCEPT sets the cookies and causes the popup to not appear again.
Note: * If customers want a more relaxed interpretation of the Cookie Law, the can add a Close button. We exclude the Privacy page so that it can be read without seeing the notification.

Browsers

SGJC officially supports the following browsers, and one version earlier.

Desktop

  • Chrome 53+
  • Firefox 49+ (including the latest Extended Support Release (ESR) that is, 45)
  • Microsoft Edge 38+
  • Microsoft Internet Explorer 11
  • Safari 10+

Mobile

See Business Manager User Interface.

Supported Browsers Desktop (supporting the latest browser version):

Commerce Cloud Store supports the latest version of the browsers listed above, as well as one version earlier. Refer to documentation for more details. The plus(+) sign signifies support of both major and minor browser releases following the listed browser version. End of Support for Opera As of January 1, 2017, Commerce Cloud will no longer support Opera due to infrequent client use. We continue to evaluate browser usage throughout 2018, and if we see changes with browser adoption, we consider supporting it. Implications of These Changes To ensure optimal performance, we encourage all clients to upgrade to a supported browser prior to January 1, 2017. Commerce Cloud continues to test and fix bugs in all supported browsers. Customers are welcome to use any browser they want, but there can be noticeable performance issues with unsupported browsers. If you encounter any issues with a supported browser, open a ticket with Commerce Cloud Support.

CSS Input Field Types

The SiteGenesis application supports HTML5 input field types:
  • color
  • date
  • datetime
  • datetime-local
  • email
  • month
  • number
  • range
  • search
  • tel
  • time
  • url
  • week
Note: Not all of these types are relevant for the SiteGenesis application.

For more information, see http://www.w3schools.com/html/html5_form_input_types.asp.

SiteGenesis and Web Content Accessibility Guidelines (WCAG)

The Web Content Accessibility Guidelines (WCAG) provide a single shared standard for web content accessibility that meets the needs of individuals, organizations, and governments internationally. WCAG documents explain how to make web content more accessible to people with disabilities. See http://www.w3.org/WAI/intro/wcag.

Note: Salesforce B2C Commerce does not guarantee or certify compliance of SiteGenesis with any WCAG level.

The SGJC application was changed to better conform to the WCAG guidelines. The list of changes shown here is intended to provide examples of how you can make your storefront application more accessible:

The WCAG guidelines followed were:

The title-related corrections to SiteGenesis use technique H33 (http://www.w3.org/TR/2014/NOTE-WCAG20-TECHS-20140916/H33). The link text is supplemented with the title attribute to add more context, making it easier for people with disabilities to determine the purpose of the link.

Consent Tracking in SGJC

Commerce Cloud enables merchants to track personal information about their shoppers, and use this information to improve their shoppers’ overall shopping experience. Some merchants can decide to provide their shoppers a way to deny or grant their consent to such tracking.

This topic describes a sample implementation for consent tracking in SGJC. The purpose of this sample implementation is to suggest how you can implement this capability on a storefront adapted from SGJC. This sample implementation is meant to be informative, but not prescriptive.

This sample implementation uses:

More details about the sample implementation are provided later in this document.

Content Asset for the Consent Request Message

To display a consent message to the shopper, the sample implementation uses a content asset whose internal ID is consent_tracking_hint. This content asset contains meaningless text, which you can replace with your own message.

Site-Specific Preference (Tracking)

The Tracking site preference determines the default tracking behavior for a site. If set to Opt-in, personal information is not tracked by default for all shoppers visiting the site; otherwise, personal information is tracked.

To set this preference, select Merchant Tools > site > Site Preferences > Privacy.

The sample implementation assumes that the Tracking preference is set to Opt-In.

Session Tracking Flag

The sample implementation presents a consent request message to the shopper, who can choose to allow tracking. If the shopper allows tracking, the sample implementation enables tracking during the shopper’s session.

You can enable tracking on a session by calling the following method:

You can determine the current value of the tracking flag by calling the following method:

Extra Implementation Details

The sample implementation uses a server-side endpoint to set the session tracking flag based on the value of a URL parameter. The shopper’s choice to grant or deny consent determines the value of this parameter.

The server-side endpoint is named Account-consentTracking:

function consentTracking() {
   var consent = request.httpParameterMap.consentTracking.value == 'true';
   session.custom.consentTracking = consent;
   session.setTrackingAllowed(consent);
}

The result of the shopper’s decision is stored in the server-side variable session.custom.consentTracking. It is important to communicate the state of this variable with the client-side code running on your storefront, so the sample implementation places this information in a global location that is accessible to all SGJC-based storefront pages (footer_UI.isml).

The client-side implementation can check the state of this variable and store it in a client-side variable (consent). The client-side implementation can then use the value of the variable to determine whether to display the consent request message to the shopper.

Several other changes to the client side complete the sample implementation.

The following properties are added to the ‘resources’ object in the Resource.ds file:

TRACKING_CONSENT: Resource.msg('global.tracking_consent', 'locale', null),
TRACKING_NO_CONSENT: Resource.msg('global.tracking_no_consent', 'locale', null),

The following properties are added to the ‘urls’ object in the Resource.ds file:

consentTracking: URLUtils.url('Page-Show', 'cid', 'consent_tracking_hint').toString(),
consentTrackingSetSession: URLUtils.url('Account-ConsentTracking').toString(),

The client-side variable (consent) is set before app.js is included in the footer_UI.isml template:

<script>var consent = ${session.custom.consentUser};</script>
<script src="${URLUtils.staticURL('/js/app.js')}"></script>

A consentTracking module is required in app.js:

consentTracking = require('./consentTracking);
...
consentTracking.init();
...
$('.consent-tracking-policy').on('click', function (e) {
        e.preventDefault();
        consentTracking.show();
    });

Lastly, the consentTracking module is implemented:

function getConsent() {
    dialog.open({
        url: Urls.consentTracking,
        options: {
            closeOnEscape: false,
            dialogClass: 'no-close',
            buttons: [{
                text: Resources.TRACKING_CONSENT,
                click: function () {
                    $(this).dialog('close');
                    $.ajax({
                        type: 'GET',
                        url: util.appendParamToURL(Urls.consentTrackingSetSession, 'consentTracking', true),
                        success: function () {
                            showPrivacyDialog();
                        },
                        error: function () {
                            showPrivacyDialog();
                        }
                    })
                }
            }, {
                text: Resources.TRACKING_NO_CONSENT,
                click: function () {
                    $(this).dialog('close');
                    $.ajax({
                        type: 'GET',
                        url: util.appendParamToURL(Urls.consentTrackingSetSession, 'consentTracking', false),
                        success: function () {
                            showPrivacyDialog();
                        },
                        error: function () {
                            showPrivacyDialog();
                        }
                    })
                }
            }]
        }
    });
}

function enablePrivacyCookies() {
    if (document.cookie.indexOf('dw=1') < 0) {
        document.cookie = 'dw=1; path=/';
    }
    if (document.cookie.indexOf('dw_cookies_accepted') < 0) {
        document.cookie = 'dw_cookies_accepted=1; path=/';
    }
}
function showPrivacyDialog(){

    if (SitePreferences.COOKIE_HINT === true && document.cookie.indexOf('dw_cookies_accepted') < 0) {
        // check for privacy policy page
        if ($('.privacy-policy').length === 0) {
            dialog.open({
                url: Urls.cookieHint,
                options: {
                    closeOnEscape: false,
                    dialogClass: 'no-close',
                    buttons: [{
                        text: Resources.I_AGREE,
                        click: function () {
                            $(this).dialog('close');
                            enablePrivacyCookies();
                        }
                    }]
                }
            });
        }
    } else {
        // Otherwise, we don't need to show the asset, just enable the cookies
        enablePrivacyCookies();
    }
}
var consentTracking = {
    init: function () {
        if (consent == null && SitePreferences.CONSENT_TRACKING_HINT) { // eslint-disable-line no-undef
            getConsent();
        }
        
        if (consent != null && SitePreferences.CONSENT_TRACKING_HINT){ // eslint-disable-line no-undef
            showPrivacyDialog();
        }
        
    },
    show: function () {
        getConsent();
    }
};
module.exports = consentTracking;

Downloading a Shopper's Information in SGJC

When you implement your SGJC-based storefront, consider providing your shoppers with a mechanism to download their data.

Registered users see a Download my data button on the My Account page. When a shopper clicks this button, SGJC downloads a JSON file to the shopper's browser with the following information:

The JSON file illustrates the type of information to provide to your shoppers. Your business can provide different data in a different format.

Sample JSON file

This sample file shows the type of information you can provide to the shopper.

{
  "profile": {
    "birthday": "1988-10-21T00:00:00.000Z",
    "companyName": "",
    "customerNo": "D00000001",
    "email": "[email protected]",
    "fax": "",
    "firstName": "Test1",
    "gender": "Female",
    "jobTitle": "",
    "lastLoginTime": "2018-02-14T20:07:31.074Z",
    "lastName": "Doe",
    "lastVisitTime": "2018-02-14T20:07:31.074Z",
    "phoneBusiness": "",
    "phoneHome": "",
    "phoneMobile": "",
    "preferredLocale": "",
    "previousLoginTime": "2015-05-18T20:43:17.000Z",
    "previousVisitTime": "2015-05-18T20:43:17.000Z",
    "salutation": "",
    "secondName": "",
    "suffix": "",
    "taxID": null,
    "taxIDMasked": null,
    "taxIDType": null,
    "title": "",
    "male": false,
    "female": true,
    "nextBirthday": "2018-10-21T00:00:00.000Z"
  },
  "addressbook": [
    {
      "address1": "104 Presidential Way",
      "address2": null,
      "city": "Woburn",
      "companyName": null,
      "countryCode": "us",
      "firstName": "Test1",
      "fullName": "Test1 User1",
      "id": "Home",
      "jobTitle": null,
      "lastName": "User1",
      "phone": "781-555-1212",
      "postalCode": "01801",
      "postBox": null,
      "salutation": null,
      "secondName": null,
      "stateCode": "MA",
      "suffix": null,
      "suite": null,
      "title": null
    },
    {
      "address1": "91 Middlesex Tpke",
      "address2": null,
      "city": "Burlington",
      "companyName": null,
      "countryCode": "us",
      "firstName": "Jane",
      "fullName": "Jane Doe",
      "id": "Work",
      "jobTitle": null,
      "lastName": "Doe",
      "phone": "781-555-1212",
      "postalCode": "01803",
      "postBox": null,
      "salutation": null,
      "secondName": null,
      "stateCode": "MA",
      "suffix": null,
      "suite": null,
      "title": null
    }
  ],
  "wallet": [],
  "orders": [],
  "productList": {
    "whishlists": [],
    "giftregistries": [],
    "shoppinglists": []
  },
  "thirdpartydata": {}
}

Implementation Details

This example conditionally provides a Download my data button in the accountoverview.isml template.

<isif condition="${pdict.downloadAvailable}">
   <a class="profile-data-download button" 
      href="${URLUtils.url('Account-DataDownload')}">  ${Resource.msg('account.landing.databutton','account',null)}</a>
</isif>

When the user clicks the button, the Account-DataDownload controller is called. The Account.js controller file applies the following guard to the datadownload() function, exporting it as DataDownload.

/** returns customer data in json format.
 * @see {@link module:controllers/Account~datadownload} */
exports.DataDownload = guard.ensure(['get', 'https', 'loggedIn'], datadownload);

The body of the datadownload() function is implemented in Account.js as follows.

/**
 * Allows a logged in user to download the data from their profile in a json file.
 */
function datadownload() {
    var profile = customer.profile;
    var profileDataHelper = require('~/cartridge/scripts/profileDataHelper');
    let response = require('~/cartridge/scripts/util/Response');
    var site = require('dw/system/Site');
    var fileName = site.current.name + '_' + profile.firstName + '_' + profile.lastName + '.json';
    response.renderData(profileDataHelper.getProfileData(profile), fileName);
    return;
}

This function relies on the helper script profileDataHelper.js, which provides several helper functions, such as getWallet(profile), getWishlists(ProductListMgr), and so on. The helper script exports getProfileData, which calls the helper functions, constructs the JSON string, and returns the result to the shopper's browser.


exports.getProfileData = function (profile) {
    var ProductListMgr = require('dw/customer/ProductListMgr');
    var downloadJSONObj = {};

    downloadJSONObj.profile = getProfile(profile);
    downloadJSONObj.addressbook = getAddressBook(profile);
    downloadJSONObj.wallet = getWallet(profile);
    downloadJSONObj.orders = getOrders(profile);
    downloadJSONObj.productList = {
        whishlists: getWishLists(ProductListMgr),
        giftregistries: getGiftregistries(ProductListMgr),
        shoppinglists: getShoppingLists(ProductListMgr)
    };

    downloadJSONObj.thirdpartydata = {};
    return JSON.stringify(downloadJSONObj, null, 2);
};
X Privacy Update: We use cookies to make interactions with our websites and services easy and meaningful, to better understand how they are used and to tailor advertising. By continuing to use this site you are giving us your consent to do this.