How to get CART of a session in Sitecore CDP & Personalize
In order to get cart information such as total cart price, items in the cart etc.. in a guest session in Sitecore CDP & Personalize, there is no cart object to use directly. Instead, ADD events in the session need to be aggregated until a CLEAR_CART event is reached. And note that for multiple ADD events with the same Product item id, only the first one is counted.Below is the code example that can be used in Audience Filters or Decision Models to get the total cart price.if (entity && guest && guest.sessions) {var currentWebSession = null;// Find current sessionguest.sessions.forEach((session) => {if (session.sessionType === 'WEB' && session.ref == entity.sessionRef) {currentWebSession = session;return;}});if (currentWebSession !== null) {// Get cart pricevar cartPrice = 0;var itemIds = [];for (var i = 0; i < currentWebSession.events.length; i++) {var e = currentWebSession.events[i];if (e.type === "CLEAR_CART") {break;}if (e.type === "ADD" && e.arbitraryData && e.arbitraryData.product &&e.arbitraryData.product.item_id && e.arbitraryData.product.price && e.arbitraryData.product.quantity) {if (itemIds.indexOf(e.arbitraryData.product.item_id) == -1) { // Only the first ADD event of the same product item id is countedcartPrice = cartPrice + (e.arbitraryData.product.price * e.arbitraryData.product.quantity);itemIds.push(e.arbitraryData.product.item_id);}}}}}Note: entity is normally the object that have the trigger entity information such as when triggered by a custom event etc...403 forbidden errors when deploying JSS apps to Sitecore
Getting started with Headless development using Sitecore Headless SDKs and Docker are very easy. All you need to do is to- Create a Headless app using command "npx create-sitecore-jss@ver20" following the instructions here
- When you want to connect with a real Sitecore instance running on Docker, just use one of the docker-examples then connect following the examples here (It is a remote Sitecore instance in this case)
The Issue
When deploying the JSS app to Sitecore using the commandjss deploy app -c -dYou may get the following errors:Wrote sitecore\package\sitecore-jss-app.xxxxxxxxx.manifest.zipSending package sitecore\package\sitecore-jss-app.xxxxxxxxx.manifest.zip to https://cm.dockerexamples.localhost/sitecore/api/jss/import...Unexpected response from import service:Status message: ForbiddenStatus: 403Troubleshooting
- Set "debugSecurity" to true in "\App_Config\Include\zzz\sitecore-jss-app.deploysecret.config" file that is deployed to your Sitecore instance.
- Then run the jss deploy app command again with "--debugSecurity" like below
jss deploy app -c -d --debugSecurityYou can then check the logs in jss deploy app command comparing to the logs from the Sitecore instance. You will see the difference here is caused by "Deployment security factors" in jss and "Server-side security factors" in Sitecore CM have the URLs in https vs http.This is a security feature and the reason to our issue being that instances in docker are exposed via traefik on https when accessed from host machine, but within docker the request is received on http.The fix is to make the "security factors" match from the both sides.Solutions
There are 2 solutions to this. One is more hacky but quicker than the other one.Solution 1: The hacky and quick one. Change the npm package so it is requesting on https but generates the security headers in http.
- In your JSS app
- Find file "\node_modules\@sitecore-jss\sitecore-jss-dev-tools\dist\cjs\package-deploy.js",
- Find the 2 lines that start with "const factors ="
- Append ".replace("https","http")" after "options.importServiceUrl"
Solution 2: The more proper one. Enable http on traefik for CM in docker setup.
- In your Sitecore "docker-compose.override.yml" file
- Add below to cm, then restart docker containers
ports:- 80:80- In the JSS app, scjssconfig.json file
- Change the "deployUrl" to http://localhost/sitecore/api/jss/import
The solution requires port 80 not being used in your local machine. Which should be the case if you have worked with Sitecore in docker for a while locally or planning to do so. If not, using a different port number is actually very tricky and can easily go down a rabbit hole. Some readings here:Which I'd rather not to because the point of using Sitecore with docker is to allow quick and efficient ways to set up local environments and to start development right away.Notes: All above are based on Sitecore 10.2 XP0 and Sitecore JSS NextJS 20.1.3Sitecore CDP Batch API Import with PowerShell scripts
To import a batch file into Sitecore CDP using the Batch API, these are the steps- Create a batch file following the correct format
- No "Insert" is support, only "Upsert"!
- The file itself is not a valid JSON! It's multiple JSON in a single file, each JSON content are on a single line!
- "gzip" the batch file
- On Windows, can just use 7zip
- Or the PowerShell scripts below
- Get MD5 checksum and the file size in byte
- Use the PowerShell scripts below
- Or use this online tool: https://emn178.github.io/online-tools/md5_checksum.html
- Batch API call: Create the batch and get the AWS upload URL using the Batch API
- PUT method
- Generate a new UUID for identifying your import and use this UUID on your API call: https://api.boxever.com/v2/batches/[[Your UUID]]
- Use your api key and secret in basic authentication as username and password
- MD5 checksum value should be lower case!
- Request details can be found here: https://doc.sitecore.com/cdp/en/developers/sitecore-customer-data-platform--data-model-2-1/importing-a-batch-file-into-sitecore-cdp.html
- Get base64 string of the MD5 checksum from HEX value
- Use the PowerShell scripts below
- Or use this online tool: https://base64.guru/converter/encode/hex
- AWS API call: Upload the gzip file to AWS upload URL
- Headers:
- Content-Md5: use the value from the previous step
- x-amz-server-side-encryption: AES256
- Batch API call: Check the status of the batch import process
- View error logs if the import failed
- Open the link in browser, it downloads as "gz" file. Unzip and can check individual errors
- Common Errors
- {"ref":"1e20d1e1-07f0-4863-92f1-4d2e15c86a59","code":"400","message":"Not enough identifying information"}
- Enough fields must be provided so the customer can be identified
- {"ref":"null","code":"400","message":"Failed to parse import line"}
- Ensure that one JSON is flatted out in a single line, do not beautify the JSON!
- If the batch status says corrupted, ensure that MD5 checksum is correct and lower case!
Below is a PowerShell Scripts that- Generates a new UUID
- "gzip" the batch file
- Outputs the file size of the gzip file in bytes
- Outputs the lower case version of the MD5 checksum of the gzip file
- Outputs the base64 value from the HEX value of the MD5 checksum
[CmdletBinding()]param ([string]$filePath)Function Gzip-File([ValidateScript({ Test-Path $_ })][string]$File) {$srcFile = Get-Item -Path $File$newFileName = "$($srcFile.FullName).gz"try {$srcFileStream = New-Object System.IO.FileStream($srcFile.FullName, ([IO.FileMode]::Open), ([IO.FileAccess]::Read), ([IO.FileShare]::Read))$dstFileStream = New-Object System.IO.FileStream($newFileName, ([IO.FileMode]::Create), ([IO.FileAccess]::Write), ([IO.FileShare]::None))$gzip = New-Object System.IO.Compression.GZipStream($dstFileStream, [System.IO.Compression.CompressionMode]::Compress)$srcFileStream.CopyTo($gzip)}catch {Write-Host "$_.Exception.Message" -ForegroundColor Red}finally {$gzip.Dispose()$srcFileStream.Dispose()$dstFileStream.Dispose()}}Write-Host "UUID: " -ForegroundColor Green(New-Guid).GuidGzip-File $filePath$gfilePath = $filePath + ".gz"Write-Host "File Size: " -ForegroundColor Green(Get-Item $gfilePath ).length$md5 = Get-FileHash -Path $gfilePath -Algorithm MD5$hash = $md5.Hash.ToLower()Write-Host "MD5 Hash: " -ForegroundColor Green$hash$bytes = [byte[]] -split ($hash -replace '..', '0x$& ')Write-Host "Content-Md5: " -ForegroundColor Green[System.Convert]::ToBase64String($bytes)Save above as PrepBatch.ps1 and invoke like:.\PrepBatch.ps1 .\input.batchWith the values generated, you can use them with tools like Postman to send all requests.Internal authority URL with OpenID Connect in docker for ASP.NET Core
When setting up OpenID Connect with ASP.NET Core in a docker container environment, you may encounter issues with the public authority URL being not accessible inside of the container, causing the setup to fail.In my test case, it's a Sitecore 10.1 XP environment with Sitecore Identity. Internal Identity URL is http://id but the external Identity URL is https://identity.clientname.com.This is only one of the possible solutions.In summary, this solution is to hijack when requests are made in the application related to OpenID connect, and replace external authority URLs with internal ones.I am not covering the overall setup of OpenID connect in ASP.NET Core, there are enough articles about it on the internet already.Create a custom HttpClientHandler like below:using System;using System.Net.Http;using System.Threading;using System.Threading.Tasks;namespace Custom{/// <summary>/// Handles all the backend channel communications to Identity and replace any public identity url with internal url/// </summary>public class CustomHttpMessageHandler : HttpClientHandler{private string _authority { get; set; }private string _internalAuthority { get; set; }public CustomHttpMessageHandler(string authority, string internalAuthority){_authority = authority;_internalAuthority = internalAuthority;}protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken){request.RequestUri = new Uri(request.RequestUri.OriginalString.Replace(_authority, _internalAuthority));return base.SendAsync(request, cancellationToken);}}}In the startup.cs, replace the default BackchannelHttpHandler with the custom one:...public void ConfigureServices(IServiceCollection services){var internalAuthority = "http://id";var authority = "https://identity.clientname.com";...services....AddOpenIdConnect(cfg =>{cfg.Authority = authority;...cfg.RequireHttpsMetadata = false;cfg.BackchannelHttpHandler = new CustomHttpMessageHandler(authority, internalAuthority);...cfg.Events.OnRedirectToIdentityProvider = async context =>{if (context.ProtocolMessage.RedirectUri.StartsWith("http://")){context.ProtocolMessage.RedirectUri = context.ProtocolMessage.RedirectUri.Replace("http://", "https://");}await Task.FromResult(0);};...}...}...Replace the URLs, ideally, get them from configurations. Note that the handling of http vs https since internal is normally http and external is https.After the setup, all the requests inside of docker related to OpenID connect will use the internal URL and browser URLs will be proper public https versions.Switching to FastMail after 15 years of Gmail
If you are not in the mood for a story, feel free to jump to the FastMail review section down below.Since I had my first Gmail account in 2006, over the years I have registered many more. Plus other Outlook and Office365 emails. I always wanted a centralised place online to access all the accounts but I never found a solution that I liked.Recently a Telegram bot became popular among online Chinese communities, it can query all the leaked personal data based on emails, website usernames, and mobile numbers etc. I have always known that things are being leaked online via various hacks but I never thought it is this serious.It had everything! My passwords, security questions, emails, mobile numbers, national identity number (of China) and rough locations I have surfed the internet over the years when I was in China.The conclusion I came to after that: to guarantee personal data security in this era- For every service and website, different emails and passwords shall be used.
- Security questions should also be dynamically generated passwords that are stored in a password manager.
I have been using LastPass for a long time so the password part is solved already. When it comes to email, not so much.I have about 15 emails addresses currently with Gmail and Outlook/Office365. One of the Gmail accounts is still the free legacy GSuite plan and is bind to a custom domain of mine. The problem with that domain is that it is a .US domain that doesn't support privacy protection and has to expose my personal contact information in WHOIS databases.Therefore the plan was to- Get a new, not-too-long, cheap domain that supports WHOIS guard
- Switch to a PAID email service for the custom domains and manage all email accounts in one single place. Then gradually change all emails to the custom ones.
FastMail seems to be a good choice. After trialling it for less than a day, I select the Standard plan and started migrating all my email accounts and setup.Here are my takes:Migration
- Emails from 15 Gmail and Outlook/Office365 accounts were imported. Can not import iCloud emails, only contacts and calendars. The migration processes were smooth.
Using Other Emails in One Place
- IMAP and SMTP can be set up automatically when importing email accounts.
- IMAP default poll interval is 1 hour when the web interface is not open. It's a bit long. So I changed all email accounts to forwarding, this allows me to set up rules to mark them as read as well.
- IMAP settings can be disabled without deleting them.
- Note that if an email is marked as spam in the original email account, it won't be forwarded. Since normally spam are deleted in 30 days, there is a slight chance of losing important emails. Very slight...
- Since SMTP was set up during import, you still can send emails with those accounts.
FastMail Features
Speed- FastMail is not fast... It is not slow, but the web interface is sometimes stuck. iOS app seems to be an advanced web app so sometimes it loads for a while. That being said, it's not an issue. It's fast enough, just not lightning fast.
Custom Domains- I set up 2 custom domains. One of them has DNS servers set to FastMail's so the setup was really easy. And since they manage the DNS, it makes setup easier for other features too like sub-domains and websites etc...
Websites- This was a surprise to me since I didn't know they had this feature. Basically allows you to host static websites under your custom domains or your FastMail accounts. When DNS is managed by them as mentioned above, no CNAME setup is required at all. With just a few clicks, the website is up.
- Not much use for me at this point, but I am sure this would come in handy one day.
Labels v.s. Folders- You can choose to use Labels or Folders. I chose Labels since I like to mark emails with multiple labels and labels can be nested too so still support a tree structure.
Storage- I used about 6GB for all emails. The Standard plan comes with 30GB, which should be enough for me for the next few decades...
Email Clients- The setup on iPhone was just a scan of a QR code! It sets up email, calendar, contacts, notes and reminders altogether. So I can replace all the Google services on the phone which is great. However I couldn't find reminders from iOS on the web interface, so I disabled the feature on iPhone.
- Haven't tested other email clients yet but I guess they should all work fine?
Labelling wildcard emails use To address- One of the main benefits of having a custom domain email for me is to be able to use any email address with that domain and all the emails go into one single mailbox. It is very handy so I can use different email addresses for different services/websites without any pre-setup required.It does create a challenge to identify which email is for which address when looking at the mailbox. So in Google, I built App Scripts to automatically label incoming emails with the email address from To address.FastMail doesn't support this out of the box. However, they do support the Sieve code and it seems to be powerful enough to do the things I wanted. The problem is that I have never heard of it before and am still figuring out how to write the script. I am also trying to get help from their Support for this. Anyway, this wouldn't be a big issue if it can't be done automatically. I can always manually set up the rules afterwards.
Updates: Fastmail support actually helped me with the Sieve code that's required and it does exactly what I wanted!if header :matches "To" "*@domain.tld"{set "foldername" "${1}";fileinto :copy :create "INBOX.${foldername}";stop;}SearchCouldn't figure out a way to search for emails without any labels assigned yet...Use "in:Archive" in search would give all emails with any labels!- There was one time in iOS, old emails that are irrelevant show up in front in search results. Couldn't replicate on the web or the 2nd day in iOS. Could be a bug...
Support- Got a reply overnight, although my question was misunderstood, which could be my bad... Anyway, the response time is good.
- Updates: they actually helped me with the Sieve code that implements the custom feature I wanted, very nice and helpful!
Overall, I am happy with what I have so far. The Standard plan costs about 76 AUD a year which is very inexpensive. I didn't mention anything about security and privacy because, to be honest, there is no way for me to verify, just have to take their words for it.The only thing left for me to do now is to change all emails in different services and websites. I think this is going to take a while, 10 years maybe...Updates: been using it for almost two months now, still happy.Sitecore SXA Sites Generate URLs with Incorrect Hostnames
Hands on with Sitecore Experience Edge for Content Hub
2021
Sitecore Application Insights Setup Checklist in Azure App Services
Sitecore Installation Framework and Sitecore Versions Compatibility Table
Synonyms Support with Azure Search in Sitecore SXA Search Component
Sitecore Website Federated Authentication with Azure AD B2C with OpenID Connect
Evernote, Jekyll Static Site, Azure Storage, Azure CDN, Cloudflare and Azure DevOps
Cannot disable QoS in ASUS Wireless Router Web GUI
Azure DevOps - Cloud Warmup for Sitecore
Sitecore Patch Finder
Sitecore Helix Documentation version 2 in ePub format
Use one license.xml for all local Sitecore instances with SymbolicLink
Where is Site Order stored in Sitecore SXA Site Manager Powershell tool
Model Rendering Variant doesnt work with custom Sitecore SXA rendering
Common Causes and Troubleshooting Sitecore App Restarts on Azure Web App Services
Sitecore 9.1 Initial Release with Identify Server User Boost Feature Patch
Sitecore Solr Linq Error RANGE_GOOP while using String CompareTo
Missing sxacontent_txm in Sitecore SXA Solr Index with Commerce Installed
Generating static 500 error html pages using SXA in Sitecore
Whitelist Hosted Agent IP address in AzureDevOps pipeline (For Sitecore Unicorn Sync)
Setup Postman with Local Sitecore xConnect services
Evil Inline Aspx Pages for Sitecore Development
Fast Query return null in Sitecore instances
Sitecore Campaign Creator API 500 error caused by Sitecore Instance Manager
Delivery instance doesn't clear cache with Sitecore Publishing Service
Use the same session database for both Priavte and Shared Session States in Sitecore
How to handle different Sitecore Module instllation scenarios with WebDeploy approach
All about Sitecore.Cloud.Integration.Bootload
Blogging from Evernote
TTS with Google Home on Home Assistant stopped working
Deploy Sitecore Azure AppService Packages on Local IIS
Google Home working with Yeelight with Xiaomi Smart Wireless Switch
Thoughts on Sitecore Experience Accelerator
Sitecore Consulting in the most down-to-earth way
Issues while setting up Continuous Integration and Delivery with Visual Studio Team Services for Xamarin.iOS Apps
Warning in Sitecore Media Library Folder Grid View
Sitecore Content/MVT Testing doesn't update in Delivery Instances
HockeyApp, Azure Mobile Engagement and Visual Studio Application Insights
1 MB file upload size limit for WFFM on CD instance
Sitecore web.config include patching sub-folder orders
Sitecore Shell Content Testing Web API returns 404/403 error while creating page tests/content tests
Hello From The Engine
A New Back Engine for Blog
AnyoneHue? - Monitor multiple people with WIFI for Philips Hue controlling
Converting Chinese to Pinyin/Quanpin in Xamarin IOS development
Get Sitecore Item SiteInfo
Root Australia Vodafone Samsung Galaxy Note 4(N910G) Android Lollipop 5.0.1
Input Method On Galaxy Note 4 Keep Reverting Back To Default
Onda v820w flash os - Booting to droidboot.img failed
Amazon Unlimited Cloud, Windows Home Server, Netflix and VPN
Sitecore session expires(timeout) quickly(1 min) when not logged in
Browser requests page twice
From mobile
Focusing on content
Section references in Sitecore config files with ref attribute
Powerline Ethernet Adapters
TDS project build fail in Teamcity with VS 2012 runner
Sonos with Airsonos on Windows and Mac
Google Tag Manager causing placeholder issues in IE 8
Must have softwares after reinstalling windows
Multi-Site solution deployments in Azure
Schedule tasks not running on CDS in Sitecore
IncludeTemplates and ExcludeTemplates warning for custom index of Sitecore
Sitecore Redirect Manager
Sitecore Indexes media url with /sitecore/shell
IIS Http error 500.19 with web.config
subscribe via RSS