Friday, March 26, 2021

Is There Still a Place for Dianoga on Your Sitecore Website in 2021?

This is a copy of my article, originally placed here to keep all things in one place.

Some time ago, I worked with Akamai CDN, which was configured on the top of the Sitecore project. I was impressed by the different features provided on the edge. One of these features was image optimization. Image optimization is a feature that allows you to decrease image size without losing quality. I was able to turn on/off it with one click in the admin console.

Dianoga is an open-source module that is responsible for image optimization in the Sitecore world. But the more and more Sitecore websites use CDN. Let's find out what should be used and when: Dianoga or CDN Image Optimization

Before writing this article, I was under impression that all modern CDNs have image optimization features. But it is not the main and must-have feature. Let's compare the most-popular CDNs to find out, do they provide this feature?

Name Support Image Optimization Links
Cloudflare CDN Yes 1. 2. 3.
Google Cloud CDN No 1.
Amazon CloudFront No 1. 2.
F5 Yes 1.
Fastly Yes 1. 2.
Akamai Yes 1. 2. 3.
Microsoft Azure CDN Yes 1. 2.
Incapsula CND Yes 1. 2.

The comparison showed that almost all CDNs could be configured to provide image compression. But not all of them. Also, almost all of them provide this feature included in pricing plans. You don't need to pay additional money to get it. However, there are other limitations. Not all CDNs support all image formats. And you don't have the flexibility to configure as you want, but for 95% of cases, you would not need it.

Let's back to our initial question:

When do we need to use Dianoga and when CDN image optimization?

Each individual project is different. Before making choice you will need to get more information about image optimization on your CDN: pricing, formats, features, limits, etc. If you are happy with that list then it would be better to configure image optimization on CDN level and forget about it.

But there are still many cases when Dianoga will be a better choice:

  1. Your CDN doesn't have image optimization feature
  2. Your CDN image optimization feature is not free for charge
  3. Your CDN image optimization feature doesn't work with formats that are widely used on your website
  4. You need flexibility in configuration, how images should be optimized
  5. You need not only optimization, but compression as well

Monday, March 22, 2021

Sitecore SXA CLI: Theme Setup Error

 It is a copy of my article placed here to keep all my posts in one place.

A few days ago I was setting a new SXA Theme for the website. I used SXA CLI and followed official documentation, but faced with the error:

PS C:\Source\test> sxa new test
? The theme will be created for instance based on url http://localhost/. Do you want to specify a different url? no
? Enter your login sitecore\test
? Enter your password [hidden]
? Specify theme path. Root is Themes folder(format <RootFolderName/ChildFolderName>) CustomThemePath
? Do you want to set up theme config file? yes
C:\Users\Anton\AppData\Roaming\nvm\v10.15.1\node_modules\@sxa\CLI\util\serverRequests.js:26
                    _6bc‍.g.console.log(chalk.bold.red(body.match(/<li>([\d\w\s\.]*)<\/li>/)[1]))
                                                                                            ^

TypeError: Cannot read property '1' of null
    at Request.request [as _callback] (C:\Users\Anton\AppData\Roaming\nvm\v10.15.1\node_modules\@sxa\CLI\util\serverRequests.js:26:93)
    at Request.self.callback (C:\Users\Anton\AppData\Roaming\nvm\v10.15.1\node_modules\@sxa\CLI\node_modules\request\request.js:188:22)
    at Request.emit (events.js:189:13)
    at Request.EventEmitter.emit (domain.js:441:20)
    at Request.<anonymous> (C:\Users\Anton\AppData\Roaming\nvm\v10.15.1\node_modules\@sxa\CLI\node_modules\request\request.js:1171:10)
    at Request.emit (events.js:189:13)
    at Request.EventEmitter.emit (domain.js:441:20)
    at IncomingMessage.<anonymous> (C:\Users\Anton\AppData\Roaming\nvm\v10.15.1\node_modules\@sxa\CLI\node_modules\request\request.js:1091:12)
    at Object.onceWrapper (events.js:277:13)
    at IncomingMessage.emit (events.js:194:15)

Not too user-friendly error. Let's figure out what went wrong. First of all, we need to open SXA CLI NPM source and see what happens in serverRequests.js on line 26.

try {
    let response = JSON.parse(body);
    if (err) {
        console.log(chalk.red.bold('getting of Module list failed:' + err));
        return reject(err);
    }

    if (httpResponse.statusCode !== 200) {
        console.log(chalk.red.bold('Status code:' + httpResponse.statusCode));
        console.log(chalk.red.bold('Answer:' + httpResponse.body));
    }
    return resolve(response);
} catch (e) {
    if (typeof httpResponse == "undefined") {
        console.log(chalk.red.bold('Server did not provide any response'));
    } else {
        /* !!!!! This line is causing errror:  */
        console.log(chalk.bold.red(body.match(/<li>([\d\w\s\.]*)<\/li>/)[1]))
    }
    return reject(new Error('Error during parsing an answer from the server'))
}

Ok, SXA CLI got an exception and tries to parse the response body. Let's see what response do we get by adding console.log(body); to SXA CLI code. Now I have more information:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"/>
<title>404 - File or directory not found.</title>
</head>
<body>
<div id="header"><h1>Server Error</h1></div>
<div id="content">
 <div class="content-container"><fieldset>
  <h2>404 - File or directory not found.</h2>
  <h3>The resource you are looking for might have been removed, had its name changed, or is temporarily unavailable.</h3>
 </fieldset></div>
</div>
</body>
</html>

Now, we know that we get 404 on http://localhost/-/script/v2/master/Get-NewThemeModules request. And after further troubleshooting, we figure out that the user that is used with SXA CLI should have sitecore\PowerShell Extensions Remoting role. It is not enough to be the only administrator.

Conclusions:

  1. Write carefully code inside your catch blocks. It also could throw exceptions and make troubleshooting harder than it could be.
  2. User that is used for SXA CLI should have sitecore\PowerShell Extensions Remoting role.

Friday, March 19, 2021

Using ngrok with Sitecore

It is a copy of my article placed here to keep all my posts in one place.

Have you ever need to show something to client, which is configured only on your local instance of Sitecore(e.g. PoC)? And for some reason, you were not able to use QA or UAT environment due to it is busy by someone else. You often needed to wait or setup one more environment for it. 

Or, have you ever need to help your colleague to do something with items on his machine(e.g. presentation)? You options were "pair content editing" via Skype/Teams/Slack/RDP/TeamViewer (underline your choice). 

If it does sound familiar to you, welcome here. I will describe one technique, how you can share your local environment with clients and colleagues. Please take into account that it is not only a Sitecore-specific thing. You can share any local environment. 

We will use ngrok tool. Ngrok allows you to expose a web server running on your local machine to the internet. We just need to set up and run it. 

  1. First of all, you need ngrok account. Open https://dashboard.ngrok.com/login and login or register a new user.
  2. Download and unzip ngrok.exe. (Also, if you prefer then you can use chocolatey) 
  3. Connect local ngrok to your account. You will need to run .\ngrok.exe authtoken {Your token should be here} 
  4. Expose your local instance with command: .\ngrok http --host-header=yourLocalInstance 443 Where yourLocalInstance should be your local address that you are using 
  5. You will get a public address that could be shared and used by anyone.


P.S. Remember that the described above approach shares your local instance publicly. If you have anything sensitive in your local environment then this approach should not be used. Also exposing Sitecore out from your local environment and sharing URL with the customer should be counted as +1 a non-production server.

Wednesday, March 10, 2021

Understanding XML in Sitecore Renderings Field

It is a copy of my article initially placed here to keep all things in one place.

Sitecore has many interfaces to work with presentation. You can change it via Experience Editor, or via Layout>Details dialog from Content Editor. And you are able to do all that you want. But sometimes, it is not enogh. Sometimes, when you have too many renderings on your page that it is much easier to look inside XML, in which presentation details are serialized, and do everything there. Caution: It is not recommended and you should not touch this XML directly unless you are not 100% sure what are you doing.

Let's dive deep and understand, what is present in that XML. For example, I will take a piece of the presentation of the Habitat home item.

<r xmlns:p="p"
    xmlns:s="s" p:p="1">
    <d id="{FE5D7FDF-89C0-4D99-9AA3-B5FBD009C9F3}">
        <r uid="{96440B7A-F411-4281-B690-302C5468E355}" p:before="*" s:ds="{658E89F6-3F0C-4072-88CA-50CE81757A9D}" s:id="{5A21C781-8E74-4DD0-8261-8CCDA1BF0454}" s:par="GridParameters=%7B113BEF7C-97E6-46CA-8CF3-916F7FAA8DE2%7D&amp;Styles=%7B5F1937B9-3A84-4F4B-A96E-C60419A06667%7D&amp;Navigation=%7BF5A400A0-C183-4A46-892A-F4DE38A5ECDE%7D&amp;Transition=%7BD4B706CC-7EEC-4DB9-A87D-123B29803490%7D&amp;Timeout=2000&amp;PauseOnHover=1&amp;Reset Caching Options&amp;RenderingIdentifier&amp;DynamicPlaceholderId=13" s:ph="/main/sxa-content-container/container-1">
            <rls>
                <ruleset>
                    <rule uid="{B3EE3027-9A67-4696-800B-2F7B4D619DD4}" s:name="Foodie">
                        <conditions>
                            <or uid="58709978E4BB4AF7A9814D1CD8EF7EC6">
                                <or uid="4F0B55B8EAA94AE284B2DE656A69F16B">
                                    <condition uid="73058D8C9428448DACD02E3D6D630283" s:id="{A0CDF81A-7727-4418-B331-E1AEC80EFAB0}" s:ExpectedAncestorTaxonId="{67BA62FE-7F1A-4BA1-B244-79160A0893C4}" s:CustomFieldId="{336BA862-E974-4CD6-BE4D-4BFDD78994E1}" />
                                    <condition uid="A64E09ABEEB7406AA29207D109D92D5E" s:id="{E00DB4F0-B206-4544-AD90-25D201CFB62C}" s:ProfileName="Topic Interest Category" s:PatternName="Cooking" />
                                </or>
                                <condition uid="21A4C1FE5B7145069C0B322AD2936B82" s:id="{08A70C4F-93B5-409E-9967-3892AAEB3BC2}" s:PatternList="{B7A02EED-A729-42B0-978B-2C6EF479FC8F}" />
                            </or>
                        </conditions>
                        <actions>
                            <action uid="EB19DB65DA0A4273B21828B383D0B574" s:id="{0F3C6BEC-E56B-4875-93D7-2846A75881D2}" s:DataSource="{C531430C-246D-4AC0-86D2-61B834F07269}" />
                        </actions>
                    </rule>
                </ruleset>
            </rls>
        </r>
        <r uid="{C3B8C2EE-F9DC-44C4-8837-93681B962081}" p:after="r[@uid='{96440B7A-F411-4281-B690-302C5468E355}']" s:ds="{874ED455-AF6F-438D-BEA8-1916B7FAB5BD}" s:id="{00E2C7CF-2247-4596-B878-E6BB3850A77F}" s:par="DynamicPlaceholderId=12&amp;GridParameters=%7B113BEF7C-97E6-46CA-8CF3-916F7FAA8DE2%7D" s:ph="/main/sxa-content-container/container-1/container-11" />
        <r uid="{1C0F3249-32A1-475D-8E69-8B2111ED267D}" p:after="r[@uid='{C3B8C2EE-F9DC-44C4-8837-93681B962081}']" s:ds="local:/Data/CTA - The Perfect Home Theater" s:id="{51C13F03-8364-4F61-B860-2EC6CA7439B3}" s:par="DynamicPlaceholderId=3&amp;Caching=0%7C0%7C0%7C0%7C0%7C0%7C0%7C0&amp;FieldNames=%7BB8DCE263-F093-4EF3-9B53-B4A4B919A337%7D" s:ph="/main/sxa-content-container/container-1/container-10"/>
        <r uid="{C03747C0-B0DC-467F-AA8D-126A4FFFABE9}" p:after="r[@uid='{6B1C256F-3488-4908-BB96-B7746E471A36}']" s:ds="" s:id="{896A2C68-1362-4E88-8BA0-1805AE6D4837}" s:par="Styles=%7B37959667-EE0C-4E0F-8A37-5687BB143510%7D&amp;GridParameters=%7B113BEF7C-97E6-46CA-8CF3-916F7FAA8DE2%7D&amp;DynamicPlaceholderId=9" s:ph="/main/sxa-content-container/container-1" />
        <r uid="{81BDD83F-35B1-49D1-9B7C-8C0BE1189F83}" s:ds="" s:id="{896A2C68-1362-4E88-8BA0-1805AE6D4837}" s:par="Styles=%7BB32CF8A3-6E9B-4D8B-992C-91B8C667FFFA%7D%7C%7B9CB76B6C-2B77-4728-8508-CF7041C7B69C%7D&amp;GridParameters=%7B113BEF7C-97E6-46CA-8CF3-916F7FAA8DE2%7D&amp;DynamicPlaceholderId=11" s:ph="/main/sxa-content-container/container-1" />
    </d>
</r>

Let's go one by one in the tree:

  • Parent r element — root element of presentation definition
  • d element — d stays for device. It could have next attributes:
    • id — identifier of device. This valued corresponds to ID of Device item.
    • l — identifier of the layout. This value corresponds to the ID of the Layout item. For our case attribute is absent, as it was configured on __Standard Values item.
  • r element — rendering definition. It could have next attributes:
    • uid — a unique identifier. It is a uniquely generated ID, which doesn't correspond to anything in the content tree. UID values are used for the generation of placeholder keys when Dynamic Placeholders are used. When you have an identifier in the placeholder key, you can find in which element this placeholder is present by looking for uid.
    • p:before — attribute that is used for ordering of rendering on page.
    • p:after — similar to p:before. It is used to set order of elements on a page.
    • s:ds — datasource of the element. It corresponds to the ID of the item that was selected as datasource for this rendering
    • s:par — rendering parameters. It is a serialized dictionary of key-value pairs divided by ampersand between pairs and with an equal sign between key and value. It also contains the caching configuration of rendering. (It looks like in old Sitecore versions, caching was serialized by other attributes, like cac, vbd, vbdev, vbl, vbp, vbqs, vbu, ciu. But they are not used anymore)
    • s:ph — placeholder, where rendering should be inserted.
    • s:pt — personalization test ID if it is present of the rendering
  • rls and ruleset — containers for configuration rules on rendering. ruleset has one attribute
    • s:pet — equals true, when you personalize not only data source, but also change rendering. It equals false or absent when the only datasource is changed
  • rule — container for rule definition. It contains:
    • uid — unique identifier of rule
    • s:name — name of rule
  • conditions — is a container for all conditions of a rule
  • or and and elements — are used for building logical conditions when there are many rules. They also have a unique identifier attribute:
    • uid — unique identifier
  • condition — condition serialization. It can have many different values because each condition can have different values. Here are listed only attributes that are common for each condition:
    • uid — unique identifier
    • s:id — ID of the Sitecore item which corresponds to definition of the condition
  • actions — container for all actions that should be done with rendering if rules execution returned true
  • action — definition of an action that should be done on a rendering. It has next attributes:
    • uid — unique identifier
    • s:id — ID of the Sitecore item which corresponds to definition of action. Usually, it is ID of "Hide Rendering", "Set Data Source", "Set Rendering" or "Set Parameters" item
    • s:DataSource — if action is "Set Data Source" then this attribute corresponds to new datasource ID of the Sitecore item
    • s:RenderingItem — if action is "Set Rendering" then this attribute corresponds to new rendering ID of the Sitecore item

Conclusion

It is not a full list. There also are others XML elements and attributes that could be present in your rendering definition. But they are quite rare and there is very small chance that you will face with them. This list give you additional knowledge to perform some tasks quicker. But, please use this knowledge with caution. It is very easy to break something when you edit rendering XML directly and don't use Sitecore interfaces(Content Editor or Experience editor) for it.