According to the documentation, https://doc.sitecore.com/developers/sxa/17/sitecore-experience-accelerator/en/trigger-creative-exchange-from-your-ci-server.html, as of SXA 1.7 there is support to import front end assets into Sitecore.
- First create a user with full admin access that is used to run SPE RestfulV2 services, then patch as follows
<?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:env="http://www.sitecore.net/xmlconfig/env/" xmlns:role="http://www.sitecore.net/xmlconfig/role/">
<sitecore>
<powershell>
<services>
<restfulv2 role:require="ContentManagement or Standalone">
<patch:attribute name="enabled">true</patch:attribute>
<authorization>
<add Permission="Allow" IdentityType="User" Identity="sitecore\CI.User" />
</authorization>
</restfulv2>
</services>
</powershell>
</sitecore>
</configuration>
- Then create a Powershell script in your solution to trigger SPE scripts inside Sitecore externally.
For SPE 5.0
<#
.SYNOPSIS
This script triggers any SPE scripts that are available to be used through the Restful V2 service. Sitecore Powershell Extensions 5.0 is required. Please use RestfulV2-BasicAuth.ps1 instead if Sitecore Powershell Extensions 5.1 or above is installed in the Sitecore Instance.
.DESCRIPTION
This script triggers any SPE scripts that are available to be used through the Restful V2 service. Sitecore Powershell Extensions 5.0 is required. Please use RestfulV2-BasicAuth.ps1 instead if Sitecore Powershell Extensions 5.1 or above is installed in the Sitecore Instance. This can be used on developer PC's, as well as during CI / CD process. RestfulV2 must be enabled in Sitecore Powershell Extensions for this to work. https://doc.sitecorepowershell.com/security
For Sitecore 9.1 and above that uses the Identity Server, Cognified.PowerShell.IdentityServer.config must be enabled. This config patch is available from SPE 5.1 and above.
.PARAMETER Username
The Sitecore username. Uses sitecore\admin by default.
.PARAMETER Password
The Sitecore user's password. Uses 'b' by default, because 'b' is cool.
.PARAMETER SitecoreHost
The Content Management Sitecore Host. eg. https://sc911xc91xp0.dev.local
.PARAMETER ScriptSitecoreHost
The SPE script located within /sitecore/system/Modules/PowerShell/Script Library
.PARAMETER Parameters
Powershell parameters to be executed against
.INPUTS
None.
.OUTPUTS
None.
.EXAMPLE
PS> .\RestfulV2-QueryStringAuth.ps1 -SitecoreHost https://sc911xc91xp0.dev.local -ScriptSitecorePath /ce-import -Parameters offset=3&limit=2&fields=(Name,ItemPath,Id)
#>
param(
[string] $Username = "sitecore\admin",
[string] $Password = "b",
[Parameter(Mandatory=$true)]
[string] $SitecoreHost,
[Parameter(Mandatory=$true)]
[string] $ScriptSitecorePath,
[string] $Parameters
)
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$url = ("$SitecoreHost/-/script/v2/master{0}?user={1}&password={2}&{3}" -f $ScriptSitecorePath, $Username, $Password, $Parameters)
Write-Host $url
Invoke-WebRequest -URI $url
For SPE 5.1 and newer
<#
.SYNOPSIS
This script triggers any SPE scripts that are available to be used through the Restful V2 service. Sitecore Powershell Extensions 5.1 or above is required.
.DESCRIPTION
This script triggers any SPE scripts that are available to be used through the Restful V2 service. Sitecore Powershell Extensions 5.1 or above is required. This can be used on developer PC's, as well as during CI / CD process. RestfulV2 must be enabled in Sitecore Powershell Extensions for this to work. https://doc.sitecorepowershell.com/security
For Sitecore 9.1 and above that uses the Identity Server, Cognified.PowerShell.IdentityServer.config must be enabled. This config patch is available from SPE 5.1 and above.
.PARAMETER Username
The Sitecore username. Uses sitecore\admin by default.
.PARAMETER Password
The Sitecore user's password. Uses 'b' by default, because 'b' is cool.
.PARAMETER SitecoreHost
The Content Management Sitecore Host. eg. https://sc911xc91xp0.dev.local
.PARAMETER ScriptSitecoreHost
The SPE script located within /sitecore/system/Modules/PowerShell/Script Library, add query string parameters as the Powershell parameters
.INPUTS
None.
.OUTPUTS
None.
.EXAMPLE
PS> .\RestfulV2-BasicAuth.ps1 -SitecoreHost https://sc911xc91xp0.dev.local -ScriptSitecorePath /ce-import?offset=3&limit=2&fields=(Name,ItemPath,Id)
#>
param(
[string] $Username = "sitecore\admin",
[string] $Password = "b",
[Parameter(Mandatory=$true)]
[string] $SitecoreHost,
[Parameter(Mandatory=$true)]
[string] $ScriptSitecorePath
)
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$pair = "$($Username):$($Password)"
$encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($pair))
$basicAuthValue = "Basic $encodedCreds"
$headers = @{
Authorization = $basicAuthValue
}
Invoke-RestMethod -Headers $headers -Uri "$SitecoreHost/-/script/v2/master$ScriptSitecorePath"
- Perform a full Creative Exchange Export with Agency drop as an option, and Export to Folder on server.
- Clean up all the folders that are not required for the front end build produced assets, you should end up with something like below. The front end build should produce all the distributed files into a folder structure that can easily be dropped into App_Data/packages/CreativeExchange/FileStorage//-/media/Themes//
- Sitecore provides a sample Creative Exchange Import script located at /sitecore/system/Modules/PowerShell/Script Library/SXA/SXA — Creative Exchange/Web API/ce-import. In my opinion the sample script is nowhere near good enough. I have enhanced it
<#
.SYNOPSIS
Performs a Sitecore Creative Exchange Import from the files on disk
.DESCRIPTION
This script utilises the Sitecore Creative Exchange to import front end static assets into the media library. This gives the front end development team full flexibility to use the tools
they are needed to produce the final output. This is a Sitecore Powershell Extension script that should be stored where all the other Script Libraries are placed in Sitecore.
.PARAMETER scriptArguments.DeviceId
The Device ID. If left empty, it will use the Sitecore Default Device. Please note that a Full Sitecore ID with curly braces are expected.
.PARAMETER scriptArguments.SitePath
The full Sitecore Item path to the Site in SXA. eg. /sitecore/content/Habitat SXA Sites/Habitat Home
.INPUTS
None. You cannot pipe objects to this script.
.OUTPUTS
None. This script performs the Creative Exchange Import
.EXAMPLE
N/A. This Sitecore Powershell Extensions script is triggered using Sitecore Powershell Extensions Restful API v2.
#>
Import-Function Get-CreativeExchangeImportResponse
if ($scriptArguments.ContainsKey("SitePath"))
{
if (-Not $scriptArguments.ContainsKey("DeviceId"))
{
# Defaults to the Default Device in Sitecore
$deviceId = "{FE5D7FDF-89C0-4D99-9AA3-B5FBD009C9F3}"
}
else {
$deviceId = $scriptArguments.DeviceId
}
$database = Get-Database -Name "master"
$siteItem = Get-Item -Path $scriptArguments.SitePath
$themesFolder = $siteItem.ThemesFolder
$pageDesignItem = Get-Item -Path "$($siteItem.ItemPath)/Presentation/Page Designs"
$globalScriptsOptimizerItem = Get-Item -Path "master:/system/Settings/Foundation/Experience Accelerator/Theming/Optimiser/Scripts"
$globalStylesOptimizerItem = Get-Item -Path "master:/system/Settings/Foundation/Experience Accelerator/Theming/Optimiser/Styles"
if (-Not [string]::IsNullOrEmpty($themesFolder) -and $pageDesignItem -ne $null -and $globalScriptsOptimizerItem -ne $null -and $globalStylesOptimizerItem -ne $null)
{
$themesFolderItem = Get-Item -Path "master:" -ID $themesFolder
# Check to see if asset optimizer is used for the site
$globalStylesOptimizerEnum = Get-Item -Path master: -ID $globalStylesOptimizerItem.Fields["Mode"].Value
$globalScriptsOptimizerEnum = Get-Item -Path master: -ID $globalScriptsOptimizerItem.Fields["Mode"].Value
$globalStylesOptimizerValue = $globalStylesOptimizerEnum.Fields["Value"].Value
$globalScriptsOptimizerValue = $globalScriptsOptimizerEnum.Fields["Value"].Value
$isStylesOptimizingEnabled = $pageDesignItem.Fields["Styles Optimizing Enabled"].Value
$isScriptsOptimizingEnabled = $pageDesignItem.Fields["Scripts Optimizing Enabled"].Value
if ($globalStylesOptimizerValue -eq "Disabled")
{
$isStylesOptimizingEnabled = "0"
}
elseif ([string]::IsNullOrEmpty($isStylesOptimizingEnabled))
{
$isStylesOptimizingEnabled = "1"
}
if ($globalScriptsOptimizerValue -eq "Disabled")
{
$isScriptsOptimizingEnabled = "0"
}
elseif ([string]::IsNullOrEmpty($isScriptsOptimizingEnabled ))
{
$isScriptsOptimizingEnabled = "1"
}
Write-Host "Begin: Cleaning up theme folders..."
$ScriptsFolders = Get-ChildItem -ID $themesFolder -Recurse | Where-Object { $_.Name -eq 'Scripts' } | Get-ChildItem -ID $_.ID -Recurse | Where-Object { (($isScriptsOptimizingEnabled -eq "1" -and -not $_.Name.StartsWith("optimized")) -or ($isScriptsOptimizingEnabled -ne "1")) } | Remove-Item -Confirm:$false -Force -Recurse
$StylesFolders = Get-ChildItem -ID $themesFolder -Recurse | Where-Object { $_.Name -eq 'styles' } | Get-ChildItem -ID $_.ID -Recurse | Where-Object { (($isStylesOptimizingEnabled -eq "1" -and -not $_.Name.StartsWith("optimized")) -or ($isStylesOptimizingEnabled -ne "1")) } | Remove-Item -Confirm:$false -Force -Recurse
$FontsFolders = Get-ChildItem -ID $themesFolder -Recurse | Where-Object { $_.Name -eq 'fonts' } | Get-ChildItem -ID $_.ID -Recurse | Remove-Item -Confirm:$false -Force -Recurse
$imagesFolders = Get-ChildItem -ID $themesFolder -Recurse | Where-Object { $_.Name -eq 'images' } | Get-ChildItem -ID $_.ID -Recurse | Remove-Item -Confirm:$false -Force -Recurse
Write-Host "End: Cleaning up theme folders..."
Write-Host "Begin: Import Creative Exchange into Sitecore"
$homeItem = Get-Item -Path "$($siteItem.ItemPath)/Home"
$importArgs = Get-CreativeExchangeImportResponse -CurrentItem $homeItem -DeviceId $deviceId
$responseObject = @{
"Messages" = $importArgs.Messages
}
Set-HostProperty -HostWidth 9999
[Newtonsoft.Json.JsonConvert]::SerializeObject($responseObject)
Write-Host "End: Import Creative Exchange into Sitecore"
Write-Host "Begin: Publish all front end assets"
$targets = @()
foreach($publishingTarget in [Sitecore.Publishing.PublishManager]::GetPublishingTargets($database)) {
$targets += Get-Database -Name $publishingTarget[[Sitecore.FieldIDs]::PublishingTargetDatabase]
}
$languages = [Sitecore.Data.Managers.LanguageManager]::GetLanguages($database)
# Item publish with children
[Sitecore.Publishing.PublishManager]::PublishItem($themesFolderItem,$targets,$languages,$true,$true,$true)
Write-Host "End: Publish all front end assets"
}
}
I will quickly step through my process.
- Both the Site item path and Device ID can be passed into the
script.
- There is a simple logic to determine whether asset
optimisation is enabled for the site and globally on the Sitecore
instance.
- Delete all the Styles, Scripts, Fonts and Images under
the specified Theme folder from the Site Theme Folder setting, but
leaving the folder structure in tact (Styles, Scripts, Fonts,
Images). If Asset Optimisation is used, the optimized files will not
be deleted.
- Runs the Creative Exchange import process
- Publishes the entire theme folder to all publishing targets, for all
languages that are available to the site. Utilising
PublishingManager which will work for traditional publishing, as
well as Publishing Service
- Here is a sample Front End Build script that can also be used on local developer instance and in the build pipeline.
<#
.SYNOPSIS
This script runs the Front End Build, with an option to trigger Creative Exchange Import to the Web Site
.DESCRIPTION
This script runs the Front End Build, with an option to trigger Creative Exchange Import process to the specified SXA web site. This can be used on both developer PC's, mainly a back end developer as a back end developer do not neccessarily require the most up to date front end build assets at all times.
.PARAMETER SitecoreWebSiteCreativeExchangeFolderPath
The main Creative Exchange web site folder path
.PARAMETER CreativeExchangeMediaLibraryPath
The Media Library path inside Creative Exchange web site folder, mainly where the final front end build assets will be located
.PARAMETER Username
The Sitecore username. Uses sitecore\admin by default.
.PARAMETER Password
The Sitecore user's password. Uses 'b' by default, because 'b' is cool.
.PARAMETER RunCreativeExchangeImport
Whether the Creative Exchange Import should be run. This should be false when run in the Build pipeline, as Creative Exchange Import should only be run in Release pipeline.
When running on a local developer PC, this can be set to true, as the compiled front end static assets will copy into the Creative Exchange folder of the web site.
.PARAMETER RunYarn
Whether the Yarn install and build should be run. This is false when calling from the FED webpack build.
.PARAMETER SitecoreHost
The Content Management Sitecore Host. eg. https://sc911xc91xp0.dev.local
.PARAMETER ScriptSitecorePath
The SPE script located within /sitecore/system/Modules/PowerShell/Script Library/SXA/SXA - Creative Exchange/Web API/ for Creative Exchange Import
.INPUTS
None. You cannot pipe objects to this script.
.OUTPUTS
None.
.EXAMPLE
PS> .\FrontEndBuild.ps1 -SitecoreWebSiteCreativeExchangeFolderPath C:\inetpub\wwwroot\acme.sc\App_Data\packages\CreativeExchange\ -CreativeExchangeMediaLibraryPath FileStorage\AU\-\media\Themes\Acme\Explosives\AU\Master -Username sitecore\admin -Password b -RunCreativeExchangeImport $true -RunYarn $true -SitecoreHost https://acme.sc -ScriptSitecorePath /enhanced-ce-import
#>
param(
[string] $SitecoreWebSiteCreativeExchangeFolderPath = "C:\inetpub\wwwroot\acme-sc911xc91xp0-cm.dev.local\App_Data\packages\CreativeExchange\",
[string] $CreativeExchangeMediaLibraryPath = "FileStorage\acme\-\media\Themes\ACME\Explosives\Master",
[string] $Username = "sitecore\admin",
[string] $Password = "b",
[bool] $RunCreativeExchangeImport = $true,
[bool] $RunYarn = $true,
[string] $SitecoreHost = "https://acme.sc",
[string] $ScriptSitecorePath = "/enhanced-ce-import"
)
Push-Location $PSScriptRoot
if ($RunYarn) {
try {
yarn install
if($LASTEXITCODE -eq 1)
{
Write-Error "Yarn Install Failed"
exit 1
}
yarn build
if($LASTEXITCODE -eq 1)
{
Write-Error "Yarn Build Failed"
exit 1
}
}
catch {
exit 1
}
}
if ($RunCreativeExchangeImport) {
Write-Host "Build finished. Starting Creative Exchange Import."
Pop-Location
if ([System.IO.Directory]::Exists($SitecoreWebSiteCreativeExchangeFolderPath)) {
Remove-Item -Recurse -Force $SitecoreWebSiteCreativeExchangeFolderPath
}
$FinalFrontEndPath = $SitecoreWebSiteCreativeExchangeFolderPath + $CreativeExchangeMediaLibraryPath
$FinalFrontEndScriptsPath = $FinalFrontEndPath + "\Scripts"
$FinalFrontEndSXAScriptsPath = $FinalFrontEndPath + "\SXA Scripts"
$FinalFrontEndStylesPath = $FinalFrontEndPath + "\Styles"
$FinalFrontEndFontsPath = $FinalFrontEndPath + "\fonts"
New-Item -ItemType Directory -Path $FinalFrontEndPath -Force
New-Item -ItemType Directory -Path $FinalFrontEndScriptsPath -Force
New-Item -ItemType Directory -Path $FinalFrontEndStylesPath -Force
New-Item -ItemType Directory -Path $FinalFrontEndSXAScriptsPath -Force
New-Item -ItemType Directory -Path $FinalFrontEndFontsPath -Force
# Theme JavaScript
Copy-Item -Path "$PSScriptRoot\dist\js\*.js" -Destination $FinalFrontEndScriptsPath -Recurse -Force
# SXA scripts that have been customised
Copy-Item -Path "$PSScriptRoot\src\js\sxa\custom\*" -Destination $FinalFrontEndSXAScriptsPath -Recurse -Force
# Theme styles
Copy-Item -Path "$PSScriptRoot\dist\themes\Master\*.css" -Destination $FinalFrontEndStylesPath -Recurse -Force
# Theme fonts
Copy-Item -Path "$PSScriptRoot\src\fonts" -Destination $FinalFrontEndPath -Recurse -Force
# Trigger the Creative Exchange Import Sitecore Powershell Extension Script
& "$PSScriptRoot\RestfulV2-QueryStringAuth.ps1" -Username $Username -Password $Password -SitecoreHost $SitecoreHost -ScriptSitecorePath $ScriptSitecorePath -Parameters SitePath=/sitecore/content/Habitat SXA Sites/Habitat Home
}
- In the release pipeline, trigger the SPE to import the front end files as provided in Step 2.
For more information, please visit my blog post on https://medium.com/@Vincent_Lui/sitecore-experience-accelerator-creative-exchange-import-b166c24671da