Sitecore Symposium 2018 – 3 Opening Keynotes

So you didn’t attend Symposium but wanna find out about the Keynotes?

I tried my hand at sketchnoting for the conference and below I’ve included three notes:  Mark Frost CEO Sitecore, Paige O’Neill, CMO Sitecore, and Deane Barker, author of ‘Web Content Management: Systems, Features, and Best Practices’

Mark Frost CEO Sitecore – Opening Keynote

Main takeaways:

  • “Elevate the Experience”
  • Acquisition of StyleLabs – asset management tools
  • Integration with Salesforce Marketing Cloud
  • Advanced personalization with Cortex
  • Use JSS (Sitecore JavaScript Services) for faster time to market
  • Sitecore to adopt a public charity each year; this year, Canada’s SickKids
  • Women In Technology was a big theme for the year; stay tuned for a separate post on the topic

Mark Frost keynote

 

Paige O’Neill – CMO Sitecore, Opening Keynote

Three case studies with companies optimizing Sitecore:  Volvo (“Cars as a Service”), Cannondale (personalized bike recommendations based on habits), and SickKids (Award for best Acquisition Campaign after making their donation mobile-friendly with unique, personal follow-up).

UntitledPAIGE

Deane Barker @gadgetopia – Technical Keynote

This was primarily a discussion on why software continues to get bigger and is it possible to shrink it?  Of course it is but not without losing functionality and alienating at least some of the audience.  Going headless (separating the presentation from the product), means faster time to market but at what price?  Is this trend going to stick around?

For now, there are two directions software can take:  continue growing the existing tool and add headless capabilities OR begin with headless and gradually add additionally desired features until you have the full tool.

Technical Keynote - Headless CMS

Advertisements

Sitecore – Revisiting token substitutions with Snippets

A previous post, token substitutions in the Rich Text Editor using IFormattable , shows an elegant way to replace tokens with Dictionary entries.

As promised, I’ll show a quick and dirty way to make it easy for the content editor to add these new tokens, via snippets.

Instead of populating the snippets drop down in the rich text editor, you could certainly add your own drop down list within the rich text editor but it has a few more steps.  Here’s a great post from jammykam for that: Adding custom drop down list to rich text editor in Sitecore .

This is a bit simpler and lines up with the previous post.

Roll your own EditorConfiguration

Override the SetupSnippets method in your custom EditorConfiguration class that inherits from Sitecore.Shell.Controls.RichTextEditor.EditorConfiguration.

(You can choose to show or not show the snippets for the rich text editor created in the core database: /sitecore/system/Settings/Html Editor Profiles/[your RTE]/Snippets

namespace Sample821.Shell.Controls.RichTextEditor
{
 public class EditorConfiguration 
           : Sitecore.Shell.Controls.RichTextEditor.EditorConfiguration
 {
 public EditorConfiguration(Sitecore.Data.Items.Item profile) : base(profile){}

  protected override void SetupSnippets()
 {
 // allow the base to set up the snippet structure
 base.SetupSnippets();

 // If you don't want to use any of the snippets that are in the core db ...
 Editor.Snippets.Clear();

 // add your snippets based on your custom template
 Sitecore.Data.Database master = Sitecore.Configuration.Factory.GetDatabase("master");

 Sitecore.Data.Items.Item parentDictionary =
 master.GetItem("/sitecore/content/SampleSite/Token Dictionary");

 if ((parentDictionary == null) || (!parentDictionary.HasChildren)) return;

 foreach (Sitecore.Data.Items.Item folder in
 parentDictionary.Children)
 {
 //if u have a folder structure ... less expensive than .GetDescendants()
 if (!folder.HasChildren) continue;

 foreach (Sitecore.Data.Items.Item entry in folder.Children)
   {
      if (string.IsNullOrEmpty(entry["Key"])) continue;

      var snippet = string.Format("{{0:{0}}}",entry["Key"]);

      Editor.Snippets.Add(entry["Key"], snippet);
    }
 }
 }
 }
}

Add a new patch configuration file and change the EditorConfiguration in Sitecore.config to your new one

<setting name="HtmlEditor.DefaultConfigurationType">
  <patch:attribute name="value">
      Sample821.Shell.Controls.RichTextEditor.EditorConfiguration,Sample821
  </patch:attribute>
 </setting>

Test the result

The content editor experience is now much easier to handle the tokens.

snippet2

You may ask .. why not use the snippet to replace the text with the value in the dictionary entry?  That is certainly possible but defeats the purpose of having the dictionary entry in one place … to easily change it … in. one. place.

Combine this with the previous post and you’ll add reusable functionality for your content editing staff.

 

Export Sitecore SQL Server DB to Azure DB … illustrated

So you wanna export your Sitecore databases (7.5 – 8.x) from SQL to Azure …

Sitecore introduced Microsoft Azure support in version 6.3 which meant flexible and unlimited scalability, content delivery across the world, real-time support features like failover and backup among other pluses.

In my last post, I illustrated how to bring a data-tier application .BACPAC file into SQL Server.  While working on the same project, I also needed to restore (well, import) the newly-upgraded Sitecore 8.1 databases in to the Azure cloud.

Below I have documented my steps and cleared the obstacle that you may run into.

Export

Instead of creating a back-up of the SQL database, you export each database as a data-tier application.  (See my last post for the definition).

export 1

In the wizard, you are given the choice to export it to a local file on your drive or save it directly in Microsoft Azure. (It uses a temporary file stored locally to accomplish this).

I am saving to the local disk, FTP’ing the files, and then importing the data-tier applications into Azure.

export 2
Save to the local disk and manually move the file to the cloud
export 3
… or … connect to Microsoft Azure directly.

All was going smoothly … exporting master, web, core, analytics … until, the sessions database. The Sessions database was introduced as part of Sitecore’s database architecture in version 7.5.  As the name implies, it provides storage for the application’s session state.

When exporting the Sessions database, the operation failed.  Diving in to the details of the error revealed that it was failing within a Sessions database stored procedure, CreateTables, that made use of the common pattern, tempdb creation.

export 5

export 5b

The Azure virtual machine I was working with was not set up with a tempdb as yet.

After researching what could be done, I found out that this stored procedure is not needed and the common approach, recommended by Oleg Burov of Sitecore, is to drop it.  Run this simple script before exporting:

USE [Sitecore_session]
GO
/****** Object: StoredProcedure [dbo].[CreateTables] ******/
IF OBJECT_ID(‘CreateTables’) IS NOT NULL
DROP PROCEDURE [dbo].[CreateTables]
GO

(Reference: http://bit.ly/1RAcvwu)

export 6

The export for the Sessions database was smooth sailing after dropping that stored proc.

If you missed my post about importing from Azure to SQL, please take a look.

Happy Sitecore-ing!

To learn more about implementing Sitecore Azure, please see the Sitecore community support page.

Import Sitecore Azure DB to SQL Server DB … illustrated

So you wanna import the Sitecore Azure DBs into SQL Server …

Sitecore introduced Microsoft Azure support in version 6.3 which meant flexible and unlimited scalability, content delivery across the world, real-time support of features like failover and backup among other pluses.

Recently, I had the pleasure of working with such a system for a Sitecore upgrade and have documented my questions/hurdles that I overcame when recreating the Sitecore web application on my local machine.

Below is the journey that I took to restoring the Sitecore production databases down to my local database server.  Although there are Azure database interfaces available, I choose to use good-ole SQL Server Management Studio to back up.  Then, to import the db into SQL Server, I attempted to use the studio for SQL Server 2012, version 11.0.3156.0.  HINT:  If you don’t want to repeat my mistake, read this entire post before following along.  🙂

1. Back up

The first step is to take a back-up of the Azure database.  There’s nothing too special about how to perform this step in SQL Server Management Studio.  Run the back-up task for each database.  However, the output file has a different format;   instead of a .bak file, a .BACPAC file is created.  This is a logical backup Azure file of the schema definition and the database’s table data.

2. Time to restore (well, import)

On the Databases node of your server, choose Import Data-tier Application … and follow the wizard (seems simple enough).

What is a data-tier application or DAC?
It’s “.. an entity that contains all of the database and instance objects used by an application. A DAC provides a single unit for authoring, deploying, and managing the data-tier objects instead of having to manage them separately. A DAC allows tighter integration of data-tier development with the development of the associated application code. It also gives administrators an application level view of resource usage in their systems.”- https://technet.microsoft.com/en-us/library/ee240739(v=sql.105).aspx

Pic 1

Pic 2
This is me breezing thru the steps …

I was so confident when going through the wizard, only to have it bite me at the end.  It would have been nice to be warned on that Intro wizard screen, eh? Something like, “Note: The Database Schema Provider only supports file versions up to x.x.” Yeah, you’re right; I wouldn’t have necessarily looked up what schema file version of Azure I was importing but nonetheless …

Pic 4Let’s add an additional step and re-do the last step, shall we?

2. Install a new version of SQL Server Management Studio.

I chose SQL Server Management Studio December 2015 preview as it was the latest version available when I was working on this:

https://msdn.microsoft.com/en-us/library/mt238290.aspx

Pic 5
You’ll need a later version of SQL Server Management Studio

3. Time to import (again)

This time… it worked!  Rinse and repeat for each database.

Pic 7
Success

Here’s hoping that this makes your import much smoother than mine.  In my next post, I’ll be putting the upgraded, SQL Server databases out into Azure (yep, another hurdle).  Stay tuned!

Reference:  http://stackoverflow.com/questions/28566610/unable-to-import-sql-azure-v12-bacpac-type-microsoft-data-tools-schema-sql-sql

 

Sitecore – A case for intercepting the InsertRenderings pipeline

So you wanna modularize and re-use Sitecore items even more than how you were originally Sitecore-trained?

Here’s a perfect opportunity.

How about using the InsertRenderings pipeline? This pipeline determines the presentation components (e.g. sublayouts, renderings, views, etc) that will render on a page during a given request.

Why would you want to intercept the InsertRenderings pipeline?

A case may be that you want to use more than one component together over and over but do not want to assign them to each template or even the base template.
Imagine the 1990’s concept of HTML framesets. Each frame could be populated independently and the content within could be saved and used on any page.  Although this example is clunky and out-dated, it may help visualize the re-use that this post is addressing.

A case for headers and footers

Many sites have headers that aren’t a single component.  In fact, they may be made of components like a logo spot, a primary navigation, secondary navigation, search bar, login area, and perhaps a login status.  If each of these are considered a component (for modularity), it means that each of these, in the correct order must be placed on each page that has a header.
Yes, you could create base template variations of each component combination, true. You’re relying on no content author messing with the standard values of the presentation layer which could potentially remove components.
An alternative to this could be to create one or more items whose presentation layers reflect all the variations of headers and use logic to pick which one is used.

The logic would be contained an extension of the InsertRenderingsProcessor.

public override void Process(InsertRenderingsArgs args){ }

Within the argument of type InsertRenderingsArgs passed into the Process method is Renderings property which is a list of RenderingReference.  The objective of extending the InsertRenderings is to append additional components within this list.

List<RenderingsReference> renderingReferenceList = args.Renderings;

Where do we store a reference to the header?

One idea would be to add an additional property to the <site> node within the web.config and store the site’s header guid there.

You would retrieve the renderings from the guid’s item (guidItem) with:

RenderingReference[] headerRenderings = guidItem.Visualization.GetRenderings(device, false);

Add (AddRange) these renderings to the renderingReferenceList.

args.Renderings.AddRange(headerRenderings.ToList());

TIP: Consider using this for re-usable complex sidebar widgets as well.

Sitecore – Add true cascading (CSS) and increase reusability with Rendering Parameters

If you are just starting out as a developer in Sitecore, you may be swimming in all the architectural directions that Sitecore can go.

One approach would be to consider setting up custom rendering parameters to extend the use of your renderings and sublayouts by providing an HTML wrapper around them.

If your project contains several modules that use the same templates but require different sublayouts, this may be the answer.  This approach relies on the CSS to do the heavy lifting, creating a single sublayout but wrapping it with a flexibly-classed div that allows the CSS to manipulate the layout to fit each unique design.

This approach can save lots of time if you do have several modules using the same template.

NOTE:  In the example below, we are using MVC and also the ORM, GlassMapper. This approach can be accomplished in web forms without an ORM as well.

Let’s start with an illustration of the end result sublayout first.  It’s always nice to see where we are going before we get there.

finished mvc markup

Above you see the sublayout (it can be any markup) wrapped in a div with a variable class.  We will add code to populate “specificClass” in a future step.

When this sublayout is consumed, the content editor will pick from a pre-defined list of descriptive classes within the Presentation->Details of the item.

Rendering Parameters dropdown

Here we see a general sublayout (called “Call To Action Base” with a rendering parameter dropdown list called Specific Module.  This list is made up of terms that would be familiar to the content editor to customize this module with the correct CSS.

The dropdown list is simply a name/value collection of the terms matched to the developer’s CSS class.

setting up rendering parameters dropdown

To create this “Specific Module” field and make it available to the module, we need to create a new Rendering Parameters template by inheriting a new template from the Standard Rendering Parameters base template.

rendering parameters template

This new template is now assignable to the sublayout module under the Layouts section of the tree. Pick the new template under the Parameters Template field.

Make sure to select ‘Yes’ on the Open Properties after Add field to ensure the content editor sees the field each time they place this sublayout on the page.

sublayout setup

Now we are ready to code within the specific sublayout and grab this new “specificClass” value:


string specificClass= string.Empty;
var renderingContext = Sitecore.Mvc.Presentation.RenderingContext.CurrentOrNull;
if (renderingContext != null) {
var parms = renderingContext.Rendering.Parameters;
specificClass = parms["Specific Module"];
}

Within MVC, we have access to the Sitecore.Mvc.Presentation.RenderingContext’s collection of parameters.


string specificClass= string.Empty;
var attributes = WebUtil.ParseUrlParameters(Attributes["sc_parameters"]);
if (attributes != null) {
specificClass = Sitecore.Context.Database.GetItem(new ID(attributes["Specific Module"]))
}

In web forms, we have access to a name/value collection of the parameters, called sc_parameters.

Once this value is assigned to the div tag wrapper, we have extended the life of a module to limitless styling configurations!

finished mvc markup

Multi-lingual form validation using MVC DataAnnotation attributes and Sitecore – Part 3 (Custom validation, soup to nuts)

So you wanna provide a custom validation data annotation that would function in both the server and client validation arena?

international

Below we have a scenario where there are two form fields highlighted, Password and VerifyPassword. Imagine that we would like to make the VerifyPassword property required IF the Password field was filled out. [This could exist, for example, on a ‘change password’ form.]

The DataAnnotation provided within ASP.NET MVC don’t have a built-in attribute to support this so we will build one based on the Required attribute.

1) Create a custom attribute class that inherits from RequiredAttribute and IClientValidatable. If either of these concepts are new, please see Part 1 and Part 2 of the series.

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
    public class RequiredIfPopulated : RequiredAttribute, IClientValidatable
    {
        public  string PropertyName { get; set; }

        public RequiredIfPopulated(string propertyName)
        {
            PropertyName = propertyName;
        }

        protected override ValidationResult IsValid(object value, ValidationContext context)
        {
            Object instance = context.ObjectInstance;
            Type type = instance.GetType();
            Object propertyvalue = type.GetProperty(PropertyName).GetValue(instance, null);
            if (propertyvalue != null)
            {
                ValidationResult result = base.IsValid(value, context);
                return result;
            }
            return ValidationResult.Success;
        }

        public override string FormatErrorMessage(string name)
        {
            return Translate.Text(base.FormatErrorMessage(name));
        }

        public IEnumerable GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
            var clientValidationRule = new ModelClientValidationRule()
            {
                ErrorMessage = FormatErrorMessage(ErrorMessage),
                ValidationType = "requiredif"
            };

            clientValidationRule.ValidationParameters.Add("other", OtherProperty));

            return new[] { clientValidationRule };
        }
    }

Something new added to the class is the IsValid override. This gets the value of the parameter, PropertyName (in this case, Password), and validates that, if it is populated, that the source property (i.e. VerifyPassword) is also populated. If it is not, the validation fails on the server side.

The GetClientValidationRules method is similar to Part 2‘s CompareTranslated custom attribute. The primary difference is that the “ValidationType” is defined as ‘requiredif’ — a validation rule not available in jQuery.validate.js. You can name this new ValidationType anything javascript-acceptable (i.e. no script keywords), but it must be 100% lowercase.

2) Decorate the source property (VerifyPassword) with the new custom attribute.

[MinLengthTranslated(6, ErrorMessage = "PasswordMinLength")]
public string Password {get; set;}

[RequiredIfPopulated("Password", ErrorMessage = "VerifyPasswordRequired")]
[CompareTranslated("Password", ErrorMessage = "VerifyPasswordInvalid")]
public string VerifyPassword {get; set;}

3) Now, for the javascript.

$.validator.addMethod(
    "requiredif",
    function (val, element, other) {
        var target = $(other);
        return target.val().length == 0 || $.trim(val).length > 0 || (target.val().length > 0 && $.trim(val).length > 0);
    });

$.validator.unobtrusive.adapters.add('requiredif', ['other'],
    function (options) {
        options.rules["requiredif"] = options.params;
        options.messages["requiredif"] = options.message;
    });

$(".myform").data('validator', null);
$.validator.unobtrusive.parse($(".myform"));

First, we have to add a new function to jQuery.validator called ‘requiredif’ (exactly the same name as the ValidationType declared in GetClientValidationRules()). This function is added to the validation list with $.validator.addMethod and contains validation logic in javascript. The first param of the function is the value of the field being evaluated (VerifyPassword). The second is the VerifyPassword element, and the third is the ‘other’ parameter, in this case, Password.

Second, we have to add an adapter to $.validator.unobstrusive.adapters, using the ‘add; function, passing in: a) the name of the validator; b) is the ‘other’ parameter, in this case, Password. … remember the param “other” we declared in GetClientValidationRules()? Here’s one place we are using it. c) Finally, the method ties the rule and message for the attribute into the unobtrusive validator.

And last but not least, we have two very important lines of code. Your page and custom client validation may run just fine without it. It all depends on how you load the previous two snippets of javascript. Here’s the trick … they need to be loaded after the 3 jquery includes (defined in Part 2) but prior to DOM ready and certainly NOT in (document).ready.

These two lines of code first, remove the previous set of validators, and then reload them. In effect, they have a chance to grab all the ones that were missed the first time around.

Stay tuned for future posts on fluent validation and perhaps some validator troubleshooting.