Updating #Sitecore 9 Update 1 to Update 2

I am always a little hesitant going from one Sitecore version to another, but with Sitecore 9 I believe it is a straightforward process. Using the Sitecore package updater simplified things. Steps and pictures below.

In the control panel choose Install an Update.

Choose your package and upload.

Click the Analyze the Package brings up this screen:

I then clicked the Analyze green arrow. The following screen appears.

Some of the conflicts were spaces or things that I could skip. If it was a configuration change I still needed a patch was created. Really nothing too major.

That was it for me. There are some minor things that might need to be adjusted. Keep in mind that you will need to update NuGet packages in your Sitecore Visual Studio solution. Also, any config and pipeline changes that might be different.

Upgrading to #Sitecore 9 Data Exchange Framework Module 2.0.1. What to Expect. #DEF

In last year’s blogs I did a several part series on the Data Exchange Framework or DEF. You can find that here. I decided to upgrade my DEF Reddit feed module to the latest versions of Sitecore and DEF to get more familiar with the changes.

The first thing I did to the solution after installing the new latest version of DEF into Sitecore 9 Update 1 was to replace the following files.

Code Changes:

Starting with BaseReadDataStepProcessor I noticed a slight change.

The following code:

protected override void ReadData(Endpoint endpoint, Sitecore.DataExchange.Models.PipelineStep pipelineStep, PipelineContext pipelineContext) {  

Should now be:

protected override void ReadData(Endpoint endpoint, PipelineStep pipelineStep, PipelineContext pipelineContext, ILogger logger) {  

As you can see from the above code PipelineContext was replaced with ILogger.

The function that adds the plugin has changed.

From this:

pipelineContext.Plugins.Add(dataSettings);  

To this:

pipelineContext.AddPlugin(dataSettings);  

In the RedditFeedValueReader the following method has changed since CanReadResult object is now ReadResult,

Original:

  1. public CanReadResult CanRead(object source, DataAccessContext context) {  
  2.     bool flag = source != null && source is RedditSharp.Things.Post;  
  3.     return new CanReadResult() {  
  4.         CanReadValue = flag  
  5.     };  
  6. }  

    Change:

  7. public ReadResult CanRead(object source, DataAccessContext context) {  
  8.     bool flag = source != null && source is RedditSharp.Things.Post;  
  9.     return new ReadResult(DateTime.Now) {  
  10.         ReadValue = source, WasValueRead = flag,  
  11.     };  
  12. }

    In the RedditFeedFieldValueAccessorConverter class I noticed the following issue after upgrading.


    After using DotPeek to take a look I found this method no longer exists. It looks like it has been renamed/replaced by ConvertResult. Also it has been set to protected vs public.

  13. protected override ConvertResult < IValueAccessor > ConvertSupportedItem(ItemModel source) {  
  14.     return this.PositiveResult((IValueAccessor) new ValueAccessor() {  
  15.         ValueReader = this.GetValueReader(source), ValueWriter = this.GetValueWriter(source)  
  16.     });  
  17. }  

    So, I converted by existing method and it works again.

  18. protected override ConvertResult < IValueAccessor > ConvertSupportedItem(ItemModel source) {  
  19.     var accessor = base.Convert(source);  
  20.     if (accessor == null) {  
  21.         return null;  
  22.     }  
  23.     var fieldName = base.GetStringValue(source, RedditFeedFieldValueValueAccessorItemModel.RedditFeedFieldName);  
  24.     if (string.IsNullOrEmpty(fieldName)) {  
  25.         return null;  
  26.     }  
  27.     if (string.IsNullOrEmpty(fieldName)) {  
  28.         return null;  
  29.     }  
  30.     ValueWriter = this.GetValueWriter(source);  
  31.     ValueReader = this.GetValueReader(source) ? ? new RedditFeedValueReader(fieldName);  
  32.     if (ValueWriter == null) {  
  33.         ValueWriter = new PropertyValueWriter(fieldName);  
  34.     }  
  35.     return this.PositiveResult((IValueAccessor) new ValueAccessor());  
  36. }  

    The way ids were set before is now a bit different.

    Before I would set id’s using the following:

  37. private static readonly Guid TemplateId = Guid.Parse(“{CE67E73A-40DF-4AB7-A7D3-2FD65E166E2E}”);  
  38. public RedditEndpointConverter(IItemModelRepository repository): base(repository) {  
  39.     this.SupportedTemplateIds.Add(TemplateId);  
  40. }  

    I changed that and now just do this:

  41. [SupportedIds(“{68BD9AAD-635F-40F3-9ACD-711662C59EEC}”)]  

    Sitecore Changes:

    The value mapping has changed to a Treelist instead of a droplist.

    The window that gave you updates while the batch process ran has changed. Now it links to a log file.


    Those are the changes I made for now. Unfortunately, I am not able to run the process as I did before. It seems something else has changed. I am currently digging into that and have gone to Sitecore support for help. I will document the change in my next blog and also update the code repository so you can see the new changes.

Named a #SitecoreMVP 2018 MVP Technology

As I am writing this I am still in shock. Several years ago I was just learning and developing with Sitecore at night while working on other projects during the day. Little did I know where it would lead me. I have run several marathons and I can tell you this journey was a marathon. There were some good and rough times along my way, but I kept moving forward.

My drive to become an MVP started a year ago when I got a job with Paragon and they gave me the confidence to believe I can achieve MVP status. I talked to many MVPs this past year and they gave me a lot of helpful advice. I have met so many people in the Sitecore community this past year as well and I am excited about being more involved in the community as an MVP and making new connections. The highlights of this year have been the meetups, Slack conversations and most of all getting to attend the Sitecore Symposium in Las Vegas. So, I just want to say thank you to Paragon, all the MVPs who gave me advice, aspiring MVPs that were on the ride with me, Sitecore of course and the Sitecore community.

So now what? Well no matter what if I didn’t get awarded MVP I was going to continue doing what I am doing. I learned a lot about myself this past year. I love blogging, I really love Sitecore more than I realized before and most of all I love sharing my knowledge. If I was going to add something new I think it would be coming up with a YouTube video this year. I just need to find the right blog topic.

To see all the winners check it out here. Also congratulations to my co-worker Scott Gillis who has gotten Sitecore MVP for the second year in year in a row. You can check his blog at thecodeattic.

 

First Ever Milwaukee Meets Chicago #Sitecore Meetup Recap

So not sure exactly who’s idea it was, but it turned out to be a great one to combine the Milwaukee Sitecore Meetup with the Chicago Sitecore meetup. No Bears vs Packers football was discussed, just Sitecore. There must have been over 30 attendees. Coveo did the first presentation and I was honored to be part of the second presentation of Sitecore Symposium attendees. I can’t wait for the next one. So here is the recap in pictures.

I live in the far west suburbs of Chicago so driving to Kenosha, Wisconsin was about the same time as it took me to get to the Chicago meetups. I enjoyed my view of the country roads and put on some classic hard rock music.

I arrived at the venue. I have never been to the Brat Stop, but I may have to come back for a visit.


Upon arrival it was great to start meeting others in person and seeing people again that I have met at the Symposium, the Chicago meetups and even one of my co-workers in person.

Renee from American Eagle and Isabel from Coveo.

My co-worker Chad from Paragon and Joe from GeekHive.

Like I said a sizable number of attendees.

Look at that food. Not bad for not being Chicago pizza.

I always love Sitecore freebies. I do love Sitecore like the pin says.

So let’s get down to the meetup itself. It first started with all the sponsors telling a little about the company they work for. I talked about Paragon and what we did. Hopefully what I was saying made sense. I am a developer, but I always try and do my best to sell.

Coveo then did a presentation. Coveo has been a huge part of Sitecore searching/marketing and they always impress me. I can’t wait to do another project with their tools.

 

 

 

 

 

 

 

 

 

Now it was time for the Sitecore Symposium panel.

110317_1718_FirstEverMi11.jpg

We all gave our best insights and reported things we learned and brought up a lot of subjects. Mark and the audience had some great questions. This was so much fun.

So when is the next one? Not sure, but this one was so much fun. Maybe we can discuss Bears vs Packers at the next one?

So That is What the Custom Data Property is for. #Sitecore #Hedgehog #TDS and Glass Mapper Model Generation.

Glass and TDS makes our development life easier so we should try and use every feature we can. Right? One of my favorite features is generating a list of Glass models instead of GUIDS. A lot of you probably know this, but there is always times where someone does not. I wanted to document this so this can help others.

Select the field that is a Treelist or whatever multi list field.

In the properties under Code Generation you need to specify the type of list you want returned with model.

In my case:

type=IEnumerable<Feature.SectionLink.Models.sitecore.templates.Feature.SectionLink.SectionLink>.

Now Glass will auto generate and you will get the actual Glass Item Model instead a list of GUIDS.

[SitecoreType(TemplateId=ISectionLinksConstants.TemplateIdString)]

public partial class
SectionLinks  : GlassBase, ISectionLinks

       {

///
<summary>

 /// The Section List field.

            ///
<para></para>

                           ///
<para>Field Type: Treelist</para>          

                           ///
<para>Field ID: e09af999-37fa-42a3-98b7-1ffb802413c2</para>

                           ///
<para>Custom Data: type=IEnumerable<Feature.SectionLink.Models.sitecore.templates.Feature.SectionLink.SectionLink></para>

                           ///
</summary>

                           [global::System.CodeDom.Compiler.GeneratedCodeAttribute(“Team Development for Sitecore – GlassItem.tt”, “1.0”)]

                           [SitecoreField(ISectionLinksConstants.Section_ListsFieldName)]

                           public
virtual
IEnumerable<Feature.SectionLink.Models.sitecore.templates.Feature.SectionLink.SectionLink> Section_Lists  {get; set;}

       }

In my controller I just call it this way and I have my list to run through the ForEach:

viewModel = sitecoreservice.Cast<ISectionLinks>(Sitecore.Context.Item,inferType:true).Section_Lists.ToList();

So that is it. Let me know if there is something else we can do with this property. I would like to know.

#Sitecore Data Exchange Framework Scheduling Tasks Options

One of the little known things that comes with the DEF is new commands that can be used to schedule DEF pipeline batches.

The new command options under System/Tasks/Commands/Data Exchange are the following:

  • Run All Pipeline Batches Command (Used to run multiple batches.)
  • Run Selected Pipeline Batches Command (Used to run one batch process.)

Run All Pipeline Batches Command

This command is used for running multiple batch processes. You will notice in the Pipeline Batches Root I just selected the Pipeline Batches parent folder. This should run all the batch processes underneath it.

Run Selected Pipeline Batches Command

With this command you can select one Batch Process to run.

Scheduling

Once you have your commands setup scheduling is easy. Just create a scheduling task and select the command and fill in the required fields like you would do for other commands.

That’s it. Easy to schedule.

#Sitecore ‘s SaveUI Pipeline One Way it Can Save You from the Cache

Recently I had a bug fix that required fixing a custom tab page in the content editor. The custom page had a save button, but users were also clicking on the content editor save button. Well unfortunately that undid the previous change. The reason why was even though the changes were saved to the database the old values were still being cached.

So after doing some research on maybe refreshing, clearing cache etc… the best way to fix this issue turned out to be was adding a custom pipeline step on the SaveUI pipeline. It was a little a trial and error at first, but I was able to accomplish what I needed to.

First thing is you want to do is come up with a name for your new class and create a config file that will hook into the SaveUI pipeline. Now you have some options here. If you look below the SaveUI has a series of steps that happen in order for an item to save. You just need to find the one you want your step to go before or after.

<saveUI>
<processor mode=”on” type=”Sitecore.Pipelines.Save.BeforeSaveEvent, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.ParseXml, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.CheckItemLock, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.CheckRevision, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.Validators, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.ValidateFields, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.HasWritePermission, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.NewVersion, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.TightenRelativeImageLinks, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.ConvertToXHtml, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.CheckLock, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.Lock, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.CheckBaseTemplateFieldChange, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.CheckTemplateFieldChange, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.ConvertLayoutField, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.CheckLinks, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.Save, Sitecore.Kernel” />
<processor mode=”off” type=”Sitecore.Pipelines.Save.RenderingHack, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.Unlock, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.WorkflowSaveCommand, Sitecore.Kernel” />
<processor mode=”on” type=”Sitecore.Pipelines.Save.PostAction, Sitecore.Kernel” />
</saveUI>

I created a config file and placed it in the app_config\include folder of the website.

<configuration xmlns:patch=”http://www.sitecore.net/xmlconfig/”&gt;
<sitecore>
<processors>
<saveUI>
<processor patch:before=”processor[@type=’Sitecore.Pipelines.Save.CheckRevision, Sitecore.Kernel’]” type=”MySite.Feature.Website.Pipelines.ConfirmChanges,MySite.Feature.Website” />
</saveUI>
</processors>
</sitecore>
</configuration>

The code for the pipeline was simple. Basically I would check for what fields changed and made sure what was saved in the database fields would update the fields in the cache. That way when the save happens the SaveUI pipeline will process the changes and not overwrite anything.

public class ConfirmChanges
{
public void Process(SC.Pipelines.Save.SaveArgs args)
{
Assert.ArgumentNotNull(args, “args”);

if (!args.HasSheerUI)
{
return;
}

Assert.IsNotNull(SC.Context.ContentDatabase, “Sitecore.Context.ContentDatbabase”);
string message = string.Empty;

// for each of the items the user attempts to save
foreach (SC.Pipelines.Save.SaveArgs.SaveItem uiItem in args.Items)
{
// retrieve from the database the same item containing the old values
SC.Data.Items.Item dbItem = SC.Context.ContentDatabase.GetItem(uiItem.ID, uiItem.Language, uiItem.Version);
Assert.IsNotNull(dbItem, “dbItem”);

// for each of the fields in that item (not just updated fields)
if (UsesCustomTab(dbItem))
{
foreach (SC.Pipelines.Save.SaveArgs.SaveField uiField in uiItem.Fields)
{
if (uiField.Value == dbItem[uiField.ID])
{
continue;
}
string title = string.IsNullOrEmpty(dbItem.Fields[uiField.ID].Title)
? dbItem.Fields[uiField.ID].DisplayName
: dbItem.Fields[uiField.ID].Title;
if (title == “FieldName1” || title == “FieldName2”)
{
//if values are the same don’t do anything exit pipeline.
if (!string.IsNullOrEmpty(dbItem[uiField.ID]) && dbItem[uiField.ID] == uiField.Value)
{
continue;
}
else
{
if (title == “FieldName1”)
{
uiField.Value = dbItem.Fields[“FieldName1”].Value;
}
if (title == “FieldName2”)
{
uiField.Value = dbItem.Fields[“FieldName2”].Value;
}
}

}
}
}
}

}
}

That’s it. Let me know if you have any questions.

cache

#Sitecore Meetup Chicago Impressions

I finally attended my first Sitecore Chicago Meetup. This is my high level overview in a few words and pictures.

I got dressed for the occasion with my freshly made Sitecore Runner t-shirt. I took the train to Chicago. It is only 1.5 hour commute for me. 🙂 I am the first/last train stop to/from Chicago.

Shirt

I had some time to kill when I got to Chicago and Rightpoint invited me to check out their offices and have a beer. I got to tour their offices and talked a little about my Sitecore experiences and my current company I am at Paragon.

We got to the meeting a few minutes late. After banging on the door since it was locked we were let in. 🙂 The first thing I saw was the food. They served Lou Malnati’s pizza my favorite, along with refreshments and dessert. Regrettably I forgot to take a picture of the food.

The first presentation was “Experience Marketing Presentation: High Level Review of the latest from Sitecore. Lead by Tim Ahlenius (Sitecore Digital Strategist MVP from Americaneagle.com)”. It was great and very informative of a feature that I can see being very useful.

The second presentation was “Sitecore Publishing Service. Lead by Jim Noellsch (Senior Technical Architect from Rightpoint)”. So good to hear about how much the publishing has improved with Sitecore. I know this change will make Sitecore clients happy.

The meetup ended after the second presentation. I can’t wait for the next one. I hope I can eventually do a presentation at one. If you are interested in attending a future one check out the meetup website here. Listen to Yoda.

Yoda (2)

Unicorn vs Hedgehog TDS for Sitecore. Hmm what to choose.

So let me just start out by doing #Sitecore updates with packages only can lead to a lot of issues. My first site using Sitecore I had to do that. It was with a company that was new to Sitecore and I had to learn it at night with a certified Sitecore developer they contracted to help. Some very long nights, but many years later it was all worth it. While creating packages and installing them it always seemed something was missing especially the bigger changes. It was also not always easy to reverse the changes if something went wrong. (I know now there are ways to do that.)

My next company I was introduced to Hedgehog’s TDS product and never looked back. Sure at first there are some learning curves. There were sync issues when working with other developers especially when working on the same things. It seems those issues have been worked out and I don’t see many of the issues I had when I first used it. Most Sitecore developers are familiar with TDS. In case you are not it allows you to sync Sitecore items (templates, content etc…), integrates well with Visual Studio, Glass Mapper and also has settings that make it easy to deploy your site. It does many other things.You can check out their site here.

Recently I was introduced to Unicorn. It is an open source utility used to sync Sitecore changes. Quoted from GitHub: “Unicorn is a utility for Sitecore that solves the issue of moving templates, renderings, and other database items between Sitecore instances.”  You can find Unicorn on GitHub here. I have wrote some blog posts on my experience with it. I think it a great product and it keeps getting better. It syncs Sitecore changes and helps integrating changes between team members much easier and safer than packages. I like the control panel page that is part of Unicorn. It explains step by step what you need to do to sync changes. It does not do everything TDS does, but it is still evolving and may do some additional things TDS does.

So which one do you choose? Either option in my opinion will be better than using just packages for everything. TDS is supported by a major software company and does a lot of things that make it convenient setting up a Sitecore environment. Unicorn is great if you want something simple and clean to do your Sitecore updates with other developers. Unicorn sums up the main differences below from their GitHub site:

Unicorn solves some of the same issues as Hedgehog’s TDS. The major difference in approach is that because Unicorn forces all of the merging to be done on the disk, you never have to manually select what to update when you’re running a sync operation or remember to write changed items to disk. Unless you have actual collisions, this saves a lot of time because you can take advantage of Git, SVN, TFS, etc to do automerges for you. That said, TDS and Unicorn have different feature sets and goals. TDS is a monolithic product with commercial support and marketing that does a lot more than just serialization. Unicorn is relatively simple, free and open source, and does one thing well. Use whatever makes you happy 🙂

So what do you prefer and why?

 

Unicorn for Sitecore Setting up Separate Core/Master Database Serialization

After getting the Unicorn sync to work I wanted to have separate folders for the Core and Master database items. It will keep things organized much better that way IMHO. After researching an asking around in Slack I came up with the following solution.

First  based on the Unicorn.Configs.Default.example I created two new configs. Unicorn.Configs.Core.config and Unicorn.Configs.Master.config.

coremasterconfig

In each file I changed the configuration name and description. Also it is very important that you add an entry for the physicalRootPath. This will override the default path.

This is what I did for the Core database. You will notice that the path ends in \Core.

 <configuration name="SitecoreTestSiteCore" description="This is my Sitecore Test Site. I am testing Unicorn and syncing CORE only.">
 <targetDataStore physicalRootPath="C:\Projects\SitecoreTestSite\src\WebApplication1\WebApplication1\App_Data\Unicorn\Serilization\Core" type="Rainbow.Storage.SerializationFileSystemDataStore, Rainbow" useDataCache="false" singleInstance="true" />

This is what I did for the Master database. You will notice that the path ends in \Master.

 <configuration name="SitecoreTestSiteMaster" description="This is my Sitecore Test Site. I am testing Unicorn and syncing Master only.">
 <targetDataStore physicalRootPath="C:\Projects\SitecoreTestSite\src\WebApplication1\WebApplication1\App_Data\Unicorn\Serilization\Master" type="Rainbow.Storage.SerializationFileSystemDataStore, Rainbow" useDataCache="false" singleInstance="true" />

After Initialization I did a sync. Look it is a cool looking unicorn.

unicorn

Now I have two separate directories for Core and Master files.

coremasterdirectory

Let me know if you have any questions.