Sitecore Xperiences - The things I've seen as a Sitecore Developer

Prevent orphan components in Experience Editor when deleting composed components

Prevent-a-component-to-be-deleted-in-Experience-Editor-when-it-has-inner-components-banner

If an editor deletes a component in Experience Editor and that component has inner components, only the outer component will be actually deleted from the page rendering. Inner components will not show anymore (and often Content Editors will be happy), so why would that be a problem?

The problem

Take for instance this set of renderings of a certain page:

Orphan Renderings-before

If in Experience Editor someone tries to delete it:

Remove Components

The result will be a “ghost” orphan item, which will not render when the page loads because its placeholder does not exist anymore.

Orphan Renderings-after

That rendering will continue at the page rendering, being either forgotten forever or re-appearing when someone tries to add a new Carousel to the same Full-Width Container placeholder.

Not just me

I’ve seen more people concerned with that – such as the author of this question at Sitecore Exchange – and some other people suggested possible solutions.

One approach I tried was to follow this blog post by Mike Tschida and implement a pipeline processor to clean-up the Page Rendering, removing orphan components when the page is saved. That would be perfect, however during development or debugging, it is very common to temporarily move a component to an unexistent placeholder to make it vanish from the page. In cases like that, the component would be deleted, which is not desired.

Acting at the other end

So what if, instead of acting later at the server, we stop content editors at the root of the problem: the Experience Editor? At that case, when an editor attempts to delete a component which has inner components, a prompt like this would be displayed. In order to delete the outer component, the editor now needs to delete the inner components first.

Orphan Renderings-prompt

Inspired by this simple Stack Overflow post,  in which the author was looking for a way to simply ask for a confirmation before deleting a component, I ended up with the solution below.

Nip the evil in the bud

The deletion of a component is handled by javascript code located at \sitecore\shell\Applications\Page Modes\ChromeTypes\RenderingChromeType.js – more specifically at the deleteControl method. Our fix will comment out the original code (I will leave there for future reference in case of an upgrade) and add the following code:

    deleteControl: function () {
        var canDelete = this.canDeleteControl(this.chrome);
        if (canDelete === false) {
            alert("Please delete inner controls before deleting this.");
            return false;
        }
        var placeholder = this.getPlaceholder();
        if (placeholder) {
            placeholder.type.deleteControl(this.chrome);
            return true;
        }
        return false;
    },

Along of course with the new canDeleteControl method, which does the trick of verifying if the component attempted to be deleted has inner components.

    canDeleteControl: function (chrome) {
        var canDelete = true;
        var childChromes = chrome.getChildChromes();
        for (var i = 0; i < childChromes.length; i++) {
            if (childChromes[i].type.key() == "placeholder") {
                canDelete = this.canDeleteControl(childChromes[i]);
                if (canDelete === false)
                    break;
            }
            else if (childChromes[i].type.key() == "rendering") {
                canDelete = false;
                break;
            }
        }
        return canDelete;
    },

And that’s all we need! Replace this file in all instances, clean up your browser caches and don’t worry about orphan components anymore!

Posted in Content Edition Experience, Development, Experience Editor, Uncategorized

How we won the Hackathon

How we won

dxaHackthon-AwardThe famous Sitecore Hackathon is an annual programming contest, where teams should put together, within 24 hours, a piece of software for Sitecore in one of the available categories. At this fourth edition in 2017, there were four categories: Azure PaaS Module, Sitecore Experience Accelerator, Habitat Module and Data Exchange Framework – this last the one the Go Horse Team picked.

92 teams participated with members from 26 different countries!

This was our first participation, so we were not sure what to expect. Like any other professional development we execute, we made sure to read all requirements and rules and to strictly obey to everything: deadlines, supporting documentations, Youtube video, the software itself, etc. And yes, that was important, as we’ve seen so many teams being declassified by not following just a small portion of these.

The Go Horse Team

Our team was composed by Anderson Fortaleza, João Amancio Neto and Rodrigo Peplau (myself). We joined together because at that time we were colleagues at Nonlinear Creations in Florianópolis, Brazil. Nowadays João is at Virtual Affairs in Netherlands, and I am working at Nish Tech Inc.

Go Horse WhiteBut what exactly is “Go Horse”?

The Go Horse Process is a joke in the form of a software development anti-pattern. Despite everybody knows its practices are not to be followed, the funny part is that at some point in our career, everyone had to “follow” it to deliver in time. Some of its axioms are:

  1. I think therefore it’s not Go Horse - In Go Horse you don’t think, you do the first thing that comes to your mind. There’s not a second option as the first one is faster;
  2. There are 3 ways of solving a problem: the right way, the wrong way and the Go Horse way which is exactly like the first one but faster. Go Horse is faster than any development process you know

These funny principles are pretty self-explanatory and also gives a sense of our mindset for this 24 hours contest: we got no serious ambitions, we were there just for fun, we had to be “pragmatic” but deliver something functional within the deadline.

Choosing the subject

Maybe one of the most important steps, if not the most important, was to select the subject. This step will define if you’ll have a good chance to win or if you’ll fail miserably. The first thing we made was to take our team out of the computer and go to a place where we could brainstorm.

Next, we got to measure our team and the individual skills of each member. Maybe a member of your team masters Azure technologies while other is good at doing SPEAK interfaces. During brainstorming, we wrote down any ideas (bad, good, mild) that came to our mind based on every subject of the contest. We opted as individuals to not be afraid of suggesting dummy ideas, which encouraged the whole team to not be afraid as well. Remember: from stupid ideas, good ones can come forth.

We ended up with the idea of using the Wikipedia API to generate content inside Sitecore, using Data Exchange Framework. Of course, other suggestions came before, but we decided this would be a simple one and easy to execute. One of the team members had already done some integration with Wikipedia’s API before so he was familiar with that. Other member had already implemented some SPEAK interfaces before. What remained? Learning Data Exchange Framework.

Some important things at this stage:

  • Avoid spending more than 1 hour to choose your subject. It is a 24 hours competition, and the software itself is only part of the delivery. Every minute is of much importance;
  • Don’t be too ambitious. Again, the short time to implement won’t let you much time to refinements. It’s better to deliver something simple but that works, then having to deliver something unfinished or buggy.

Execution: Work distribution and Teamwork

We choose our theme, made sure every team member was comfortable with that. What now? Let’s start to plan the execution. Who is going to do what? You don’t want to have in the middle of the Hackathon somebody in your team uncomfortable with the plan and willing to change everything. It’s the receipt to fail miserably.

In our case, I was already experienced with SPEAK, so I got responsible for the interfaces, which took most of my time. Anderson and João took the responsibility to learn Data Exchange Framework, understand how it works and come up with technical solutions that I would be binding together with the interface. From time to time, we stopped our work for a quick desk check, in a way that the whole team is aligned: I got to understand how the framework works, they got find solutions to our software needs such as how to programmatically trigger data importation.

Take a rest! It’s important

It was also very important to plan and optimize our team member’s presence. 24 hours is a little time to develop a software, but is time enough for people to get tired, and we don’t want everybody sleeping at the same time. So what we did was to plan our rests in such a way that there’s always someone producing. Honestly SPEAKing I tried to sleep but I couldn’t, my mind couldn’t stop scanning all gaps and possible flaws, so I took the time to lay down, rest my body and keep on planning and projecting.

Supporting material

As previously described at this post, the software itself was only part of the whole deliverable. Along with that, we had to prepare 1) Installation instructions (1-2 pages); 2) Module documentation (2-5 pages); 3) A video explaining the module (2-10 minutes). These tasks seems trivial, but they also demand strict attention as they count as much as the software itself. We split these tasks into our team members, each of us doing one of these.

Final touches

When everything was pretty done we still had about 1 hour to deliver, so we concentrate our efforts to revise everything. Members started to check each other’s work: does the documentation makes sense? Is there any typo? Is that covering everything? Is the module installing and working in a vanilla solution? Everything should be well aligned and ready for deployment.

Respect the deadlines!

We ended up uploading our work 20 minutes before the deadline. That is also something extremely important to respect, as the judges are not tolerant to delays. Our friends from the “Works on My Machine” team took more than it should to deliver and had their Dropbox access cut in the middle of the upload, which is frustrating after so many hours of hard work.

Judges Feedback

Since that was our first participation, we were not expecting to win. Surprise, we have been announced the winners, with very flattering feedbacks from the jury.

WikiDX - Judges feedback

Main lessons extracted from their feedbacks:

  • Choose something with a practical use. It is ok to implement an abstract case study, but if your module have a real life use, it may help judges to understand it, which can potentially call their attention to your team;
  • Be respectful to the category you chose. No matter what category you picked, someone have implemented the base software you will work to extend. Respect its principles and best practices, implementing something that creators will be glad to see.

Our module – WikiDX

Resulting module is called WikiDX and is available to download at the Marketplace – check it out!

Next Year?

Now that members of the Go Horse team are not working at the same company, will they be at the next contest? Of course we will! We need to defend our belt and have fun together again! While João is in Netherlands, and thus will have to join us remotely, myself and Anderson are still living at the same city, so we plan to get together for the next effort and repeat the good work.

What would we make different?

We were newbies and didn’t know what to expect. Our preparation was close to zero, we just got our own professional experiences and a lot of curiosity. For the next year we plan to have a better pre-Hackathon preparation: getting familiar to existent categories, getting to know for instance Sitecore on Azure, SXA in deep, etc. But it is also important to keep an eye at the new modules Sitecore launches, as new categories can arise. Data Exchange Framework for instance, was a new category this year, and has been released just a few months before the Hackathon. As a result of this preparation we may end up with a collection of knowledges and code snippets, which could be very helpful to a smoother execution.

Posted in Uncategorized Tagged with:

WFFM - Sitecore Context Item NULL inside a Save Action

WFFM

Business requirements

There is a use case in one of our clients at Nish Tech, where a WFFM form, with a Submit E-mail Save Action, is supposed to be reused at several different parts of the website. It should have the same fields and look & feel at every place we want to reuse it, the only difference would be that the e-mail would be submitted to different addresses depending on what page the form is located.

The pages where this form will be allowed are what we call “Communities”. Each of them has a couple of fields with e-mails (Eg: Sales E-mail, Human Resources E-mail) from which we wanted to have WFFM looking for, instead of using the one hardcoded at the WFFM itself.

One may ask: why not duplicate the WFFM form and hardcode a different e-mail for each page we need? The answer is plenty clear: we will have more than 80 communities, and growing. That would mean more than 80 duplications of the WFFM form, which sound quite abusive. If we happen to need some kind of maintenance at these forms, this would be a nightmare.

The resulting implementation will be subject of a future blog post, this one in particular will focus in a specific challenge: how to access the Sitecore.Context.Item inside a WFFM Save Action.

Technical challenge

So the idea is that WFFM should look into a field at the item where it is located, grabbing the destiny e-mail from there instead of the hardcoded e-mail at the WFFM Form itself. To achieve this we will need to access the current context item (Sitecore.Context.Item), which surprisingly is not available inside a Save Action, as confirmed by Sitecore in a Helpdesk ticket we created.

To be fair, Sitecore.Context.Item is available in Preview Mode because Sitecore explicitly sets the current item ID at the querystring. However, in publish mode there are no such parameter at the querystring, which happens to let Sitecore.Context.Item with value NULL when you are inside a Save Action. This happens because the Item Resolver only runs AFTER the Save Action, thus Context.Item will only be filled after the Save Action is executed.

Implementing a Solution

So here is the solution we ended up arranging, with best possible reusing of same WFFM and not jeopardizing WFFM’s or Sitecore’s normal pipelines and flows.

Step 1 – Create a new WFFM field for Sitecore.Context.Item

This would be a hidden field at the form, responsible for carry on the Sitecore.Context.Item.ID, available at the time when the form is rendering, to the Save Action where that is not available.

  1. At /sitecore/system/Modules/Web Forms for Marketers/Settings/Field Types/Simple Types start by duplicating the “Single-Line Text” field to serve as a basis for our new field;
  2. Rename it to “Sitecore Context Item ID”;
  3. Fill the field “MVC Type” with the assembly we are going to create to our new field:
    ClientName.Website.Core.WFFM.Fields.SitecoreContextItemId, ClientName.Website.Core
  4. Create a view called “SitecoreContextItemId.cshtml” for this field under /Views/Form/EditorTemplates and add the following content. Notice the <span style=”display:none;”> surrounding the field, which will make it hidden when the form renders
@using Sitecore.Forms.Mvc.Html
@model Sitecore.Forms.Mvc.ViewModels.FieldViewModel
<span style="display:none;">
@using (Html.BeginField())
{      
    @Html.BootstrapEditor("Value")    
}
</span>

 

Step 2 – Make that field comes auto-populated with Context.Item

This class, which is tied to the field at the bullet 3 of Step 1, implements the logic that makes the field renders with Sitecore.Context.Item.ID auto-populated. Notice that this is simply inheriting from SingleLineTextField, adding the ID to the field Value, and nothing else.

using Sitecore.Forms.Mvc.ViewModels.Fields;
namespace ClientName.Website.Core.WFFM.Fields
{
    public class SitecoreContextItemId : SingleLineTextField
    {
        public override void Initialize()
        {
            if (this.MaxLength != 0)
                return;
            this.MaxLength = 256;
            this.Value = Sitecore.Context.Item.ID.ToString();
        }
    }
}

Step 3 – Make Context.Item be automatically filled from that field

We could very well now just go ahead and read from that field, couldn’t we? But to make things pretty, instead of that we are going to implement a pipeline processor to populate Sitecore.Context.Item.

  • Create an include file “Sitecore.WFFM.SetContextItem.config” with following content:
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <wffm.submit>
        <processor type="ClientName.Website.Core.WFFM.Pipelines.SetContextItem,ClientName.Website.Core" patch:after="processor[@type='Sitecore.Forms.Mvc.Pipelines.Submit.RegisterFormSubmitEvent,Sitecore.Forms.Mvc']"/>
      </wffm.submit>
    </pipelines>
  </sitecore>
</configuration>
  • Then let’s implement this class to make Sitecore.Context.Item be populated.
using System.Linq;
using Sitecore.Data;
using Sitecore.Diagnostics;
using Sitecore.Forms.Mvc.Interfaces;
using Sitecore.Forms.Mvc.Models;
using Sitecore.Forms.Mvc.Pipelines;
namespace ClientName.Website.Core.WFFM.Pipelines
{
    public class SetContextItem : FormProcessorBase<IFormModel>
    {
        public override void Process(FormProcessorArgs<IFormModel> args)
        {
            Assert.ArgumentNotNull((object)args, "args");
            var model = args.Model as FormModel;
            if (model == null || Sitecore.Context.Item!=null)
                return;
            var contextField = model.Item.Fields.FirstOrDefault(p => p.MVCClass.Contains(typeof(Fields.SitecoreContextItemId).FullName));
            if (contextField == null)
                return;
            var contextFieldResult = model.Results.FirstOrDefault(p => p.FieldName == contextField.Name);
            if (contextFieldResult == null)
                return;
            var contextItem = Sitecore.Context.Database.GetItem(new ID(contextFieldResult.Value.ToString()));
            Sitecore.Context.Item = contextItem;
        }
    }
}

Pay special attention to the following lines:

var contextField = model.Item.Fields.FirstOrDefault(p => p.MVCClass.Contains(typeof(Fields.SitecoreContextItemId).FullName));

Where the assembly name we created at Step 2 is used to find our field and next:

var contextFieldResult = model.Results.FirstOrDefault(p => p.FieldName == contextField.Name);

 Where the ID value is taken to later populate Sitecore.Context.Item:

var contextItem = Sitecore.Context.Database.GetItem(new ID(contextFieldResult.Value.ToString()));
Sitecore.Context.Item = contextItem;

 

Step 4 – Add the field to your WFFM Form

Easy part, but if forgotten nothing will work. Make sure your new field is present at your WFFM Form:

WFFM Field Sitecore Context Item

Posted in WFFM

Corey Peplau wrote this - WFFM conflict with Unity DI and a lesson on how Sitecore community is so amazing

This blog was since the beginning, a space to share my own experiences with Sitecore development, always trying to bring that something new, not easily seen, or showing my interpretation of common problems. But this time I need to break my own rule and bring a story starred by another person. I’m talking about my good friend Corey Smith, the most willful person I ever met.

First a small parable

An engineer was hired to fix a complex machine, which he did in less than an hour by simply twisting a single screw.

  • How much it cost? Asks the client.
  • 10 thousand dollars, sir…
  • But that is a lot of money just to twist a screw! I need you to do an invoice with details of your job to justify this price.

And the invoice listed:

  • Twist a screw: $ 5.00
  • Know what screw to twist: $ 9,995.00

Corey is a step ahead of this story and managed to not only twist the right screw, but made that remotely, and for free.

The WFFM issue

Everything started when, at my first project as new member of Nish Tech Inc, I had to install WFFM 8.2 rev. 170413 (Update-3) up above an existent solution. Starting from there, any MVC WFFM form were dying with the following error:

Error Rendering Controller: Sitecore.Forms.Mvc.Controllers.FormController, Sitecore.Forms.Mvc. Action: Index: Could not create controller: ‘Sitecore.Forms.Mvc.Controllers.FormController, Sitecore.Forms.Mvc’. The item being rendered is: ‘/sitecore/content/Website/Home/Community/Cincinnati’. The context item is: ‘/sitecore/content/Website/Home/Community/Cincinnati’. The current route url is: ‘{*pathInfo}’. This is the default Sitecore route which is set up in the ‘InitializeRoutes’ processor of the ‘initialize’ pipeline.

 at Sitecore.Mvc.Controllers.SitecoreControllerFactory.CreateController(RequestContext requestContext, String controllerName)
  at Sitecore.Mvc.Controllers.ControllerRunner.GetController()
  at Sitecore.Mvc.Controllers.ControllerRunner.Execute()
  at Sitecore.Mvc.Presentation.ControllerRenderer.Render(TextWriter writer)
  at Sitecore.Mvc.Pipelines.Response.RenderRendering.ExecuteRenderer.Render(Renderer renderer, TextWriter writer, RenderRenderingArgs args)

Inner Exception: Resolution of the dependency failed, type = “Sitecore.Forms.Mvc.Controllers.FormController”, name = “(none)”. Exception occurred while: while resolving. Exception is: InvalidOperationException – The current type, Sitecore.Forms.Mvc.Interfaces.IRepository`1[Sitecore.Forms.Mvc.Models.FormModel], is an interface and cannot be constructed. Are you missing a type mapping? ———————————————– At the time of the exception, the container was: Resolving Sitecore.Forms.Mvc.Controllers.FormController,(none) Resolving parameter “repository” of constructor Sitecore.Forms.Mvc.Controllers.FormController(Sitecore.Forms.Mvc.Interfaces.IRepository`1[[Sitecore.Forms.Mvc.Models.FormModel, Sitecore.Forms.Mvc, Version=8.2.0.0, Culture=neutral, PublicKeyToken=null]] repository, Sitecore.Forms.Mvc.Interfaces.IAutoMapper`2[[Sitecore.Forms.Mvc.Interfaces.IFormModel, Sitecore.Forms.Mvc, Version=8.2.0.0, Culture=neutral, PublicKeyToken=null],[Sitecore.Forms.Mvc.ViewModels.FormViewModel, Sitecore.Forms.Mvc, Version=8.2.0.0, Culture=neutral, PublicKeyToken=null]] mapper, Sitecore.Forms.Mvc.Interfaces.IFormProcessor`1[[Sitecore.Forms.Mvc.Models.FormModel, Sitecore.Forms.Mvc, Version=8.2.0.0, Culture=neutral, PublicKeyToken=null]] processor, Sitecore.WFFM.Abstractions.Shared.IAnalyticsTracker analyticsTracker) Resolving Sitecore.Forms.Mvc.Interfaces.IRepository`1[Sitecore.Forms.Mvc.Models.FormModel],(none)

 at Microsoft.Practices.Unity.UnityContainer.DoBuildUp(Type t, Object existing, String name, IEnumerable`1 resolverOverrides)
  at Microsoft.Practices.Unity.UnityContainer.Resolve(Type t, String name, ResolverOverride[] resolverOverrides)
  at Sitecore.Mvc.Controllers.SitecoreControllerFactory.CreateControllerInstance(RequestContext requestContext, String controllerName)
  at Sitecore.Mvc.Controllers.SitecoreControllerFactory.CreateController(RequestContext requestContext, String controllerName)

Inner Exception: The current type, Sitecore.Forms.Mvc.Interfaces.IRepository`1[Sitecore.Forms.Mvc.Models.FormModel], is an interface and cannot be constructed. Are you missing a type mapping?

 at Microsoft.Practices.ObjectBuilder2.DynamicMethodConstructorStrategy.ThrowForAttemptingToConstructInterface(IBuilderContext context)
  at lambda_method(Closure , IBuilderContext )
  at Microsoft.Practices.ObjectBuilder2.DynamicBuildPlanGenerationContext.<>c__DisplayClass1.b__0(IBuilderContext context)
  at Microsoft.Practices.ObjectBuilder2.BuildPlanStrategy.PreBuildUp(IBuilderContext context)
  at Microsoft.Practices.ObjectBuilder2.StrategyChain.ExecuteBuildUp(IBuilderContext context)
  at Microsoft.Practices.ObjectBuilder2.BuilderContext.NewBuildUp(NamedTypeBuildKey newBuildKey)
  at lambda_method(Closure , IBuilderContext )
  at Microsoft.Practices.ObjectBuilder2.DynamicBuildPlanGenerationContext.<>c__DisplayClass1.b__0(IBuilderContext context)
  at Microsoft.Practices.ObjectBuilder2.BuildPlanStrategy.PreBuildUp(IBuilderContext context)
  at Microsoft.Practices.ObjectBuilder2.StrategyChain.ExecuteBuildUp(IBuilderContext context)
  at Microsoft.Practices.Unity.UnityContainer.DoBuildUp(Type t, Object existing, String name, IEnumerable`1 resolverOverrides)

The greatness of Sitecore community

After some googling I found this article from Corey Smith, where he describes a similar issue after an upgrade, which he solved by moving his Dependency Injection declaration before the InitializeControllerFactory. I know that recent versions of WFFM are using Dependency Injection, so it sounded like a good clue to start my investigation.

But in my case we were using Unity Dependency Injection, nothing registered at Sitecore Pipeline, so I thought – why not asking Corey himself for an idea? He is a great guy, as per what we’ve seen during his presentation at the Sitecore Usergroup Brazil (video can be seen here).

What followed was a real lesson for me, breaking my paradigms of balance between personal and professional life, and showing to my amazed eyes how strong, powerful and great is the Sitecore community in general, and Corey Smith in particular.

Our investigation

It was 3rd July, I was stuck at the hotel in Cincinnati with my family and this issue to solve. Corey were safe at his home in Atlanta, at the convenient distance of a single click to interrupt the chat. We were texting like crazy thru Twitter private messages. Fortunately, we both had beers at our side, would you do without some?

We started focusing at the Unity Dependency Injection setup. Under my App_Start folder I had a class called UnityMvcActivator with following decorations above the namespace:

[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(UnityWebActivator), “Start”)]

[assembly: WebActivatorEx.ApplicationShutdownMethod(typeof(UnityWebActivator), “Shutdown”)]

namespace MyClient.Website.Core.App_Start

{

   public static class UnityWebActivator

   {

       public static void Start()

       {

Line in bold is specifically setting the start-up of Dependency Injection. That would be automatically invoking (at some point in the Application_Start event) the method Start(). The exact point, however, was a mystery for us, so we managed to increase a little of our control over this. Perhaps changing the order here would make any difference?

So we removed the line in bold, then registered the method Start as an Initialize Pipeline processor. For that, the Start signature had to change into a valid Process method, as seem below:

public void Process(PipelineArgs args)

That way we would be able to control the timing when the DI start-up would occur. Promising? I thought so! However unfortunately, no matter where the registration is made, before or after everything, still the same error was showing.

Root cause

After digging a bit more, Corey came up with the root cause, and a possible solution. Happens that the controller for WFFM forms (FormController), which is the affected class at the error message, has actually two constructors: one without any parameter

public FormController()

And a second one with more parameters

public FormController(IRepository<FormModel> repository, IAutoMapper<IFormModel, FormViewModel> mapper, IFormProcessor<FormModel> processor, IAnalyticsTracker analyticsTracker)

When Unity tries to instantiate this class it is automatically choosing the controller with most parameters. And that is making the whole thing blow up to the sky.

Temporary (quick) solution

The temporary solution we ended up doing was:

  1. Roll back the pipeline modification and allow Unity to registry itself again
  2. Add the following new line to UnityConfig, RegisterTypes method:

public class UnityConfig

{

       public static void RegisterTypes(IUnityContainer container)

       {

           // Workaround to solve WFFM issues

           container.RegisterType<FormController>(new InjectionConstructor());

What the line above does is to tell Unity to register, only at the FormController class, the constructor with no parameters. This, however, is an ad-hoc solution which brings a very important indagation: What else are we breaking in Sitecore? What other controllers and dependencies do we need to register?

“You will probably find out in production, I suppose” says Corey.

Appropriate (hard) solution

Best thing to do is to abandon Unity Dependency Injection at this project and have Sitecore’s built-in Dependency Injection (Microsoft.Extensions.DependencyInjectionto handle all injections at our system. We are not gaining anything using Unity at this project, but actually we are creating problems.

Sitecore’s OOTB Dependency Injection is shipped from factory with all proper Dependency Injection configurations. That means that all Sitecore modules play very nice with it, so the whole issue we are having with Unity, for instance, simply wouldn’t even happen at first place.  

Along with that, in Sitecore 8.2, we can do Dependency Injection into pipeline processors, which has always been a requirement at Nish Tech, and is also totally awesome!

Posted in Bug fixing, Sitecore Community, WFFM

PhantomJS Screenshots not being generated with Windows Authentication turned on

It has been observed with several versions of Sitecore 8 and above (empty versions) – including 8.2 Update 2 – that the screenshot generation is broken when the site has Windows Authentication turned ON.

How Windows Authentication is turned on on IIS

Symptoms

When attempting to start up a test in any level, the Test Start dialog will show screenshots of your website to demonstrate how your tested content will display. This can be experienced, for instance, if you trigger the Page Test command inside Experience Editor:

Create Page Test

When the Page Test dialog appears you will noticed screenshots are all broken:

Page Test - Screenshot errors 1

Page Test - Screenshot errors 2

Sitecore log shows the following messages during the generation of screenshots:

ManagedPoolThread #16 19:09:27 INFO Job started: Generate screenshots
18756 19:09:28 INFO [Content Testing]: PhantomJS console: 
Resource error
id: 0
url: http://sc82rev161221/?sc_mode=edit&sc_itemid={110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9}&sc_disable_edit=yes&sc_lang=en&sc_version=1&sc_vercomp=0
errorCode: 5
errorString: Operation canceled
!--error--!
ManagedPoolThread #16 19:09:28 INFO Job ended: Generate screenshots (units processed: 1)
ManagedPoolThread #5 19:09:30 INFO Job started: Generate screenshots
28116 19:09:30 INFO [Content Testing]: PhantomJS console: 
Resource error
id: 0
url: http://sc82rev161221/?sc_mode=edit&sc_itemid={110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9}&sc_disable_edit=yes&sc_lang=en&sc_version=1&sc_vercomp=0
errorCode: 5
errorString: Operation canceled
!--error--!
ManagedPoolThread #5 19:09:30 INFO Job ended: Generate screenshots (units processed: 1)

Root Cause

In the end the root cause is not exactly Windows Authentication, but the fact that PhantomJs, when confronted with the Windows Authentication prompt, don’t know how to proceed.

WIndows Auth Prompt

Unable to type Login and Password at this prompt, PhantomJS will get back a “403 – Forbidden” error and fail creating screenshots.

Short Story: the root cause is that PhantomJS doesn’t have proper permissions to access the filesystem and write down the screenshot images. This issue thus, may also happen in scenarios where Windows Authentication is not enabled.

How Windows Authentication is being used

We are using Windows Authentication to achieve a specific goal at our environment: During Sitecore development, we have multiple Sitecore instances with different purposes (DEV, QA, UAT), being constantly updated by TeamCity, our Continuous Integration server. One of these instances (generally UAT) is exposed to public so our clients can access from outside. However, we don’t want unrelated people accessing this address, so we lock it down with Windows Authentication and provide our clients one or multiple internal accounts to login and access the website. Unrelated people would be stopped at the Windows Authentication prompt.

Ideal solution would be for us to know exactly what folder(s) to enable Anonymous authentication, so the website itself is protected and still PhantonJS can properly generate screenshots.

Solution

With the help of Sitecore Helpdesk we were able to determine how to tweak our website, enabling PhantomJS to properly generate screenshots of our website, and still sheltering it from being accessed by anyone but the people we want.

The solution consists of A) enabling Anonymous authentication for the site root node and B) disabling Anonymous authentication for every sub-folder and files

Powershell Script

Simple to understand, hard to implement.

Can we have this automatized? Yes of course! With the assistance of my college Eduardo Pereira  (thank you very much, my friend!) I was able to end up with the following PowerShell script:

Import-Module WebAdministration
$Str_IISSiteName = "Your-Website-Name-On-IIS"
$Str_PSPath = "IIS:\Sites\" + $Str_IISSiteName + "\"
$Obj_Site = Get-Item $Str_PSPath
$Obj_Items = Get-ChildItem $Obj_Site.physicalPath
write-host "Changing authentication method on " $Str_IISSiteName
Set-WebConfigurationProperty -Filter "/system.webServer/security/authentication/AnonymousAuthentication" -Name Enabled -Value True -PSPath $Str_PSPath
Set-WebConfigurationProperty -Filter "/system.webServer/security/authentication/WindowsAuthentication" -Name Enabled -Value True -PSPath $Str_PSPath
Foreach ($Obj_Item in $Obj_Items)
{
    write-host "Changing authentication method on" $Str_PSPath$Obj_Item
    Set-WebConfigurationProperty -Filter "/system.webServer/security/authentication/AnonymousAuthentication" -Name Enabled -Value False -PSPath $Str_PSPath -location $Obj_Item
    Set-WebConfigurationProperty -Filter "/system.webServer/security/authentication/WindowsAuthentication" -Name Enabled -Value True -PSPath $Str_PSPath -location $Obj_Item
}

Important: you may need to give proper access to enable PowerShell to properly execute the script above. According to this StackOverflow article, that very well describes the issue and solution, you may have to firstly execute the steps below:

  1. Open IIS Manager
  2. Click the server name in the tree on the left
  3. Right hand pane, Management section, double click Configuration Editor
  4. At the top, choose the section system.webServer/security/authentication/anonymousAuthentication
    Right hand pane, click Unlock Section
  5. At the top, choose the section system.webServer/security/authentication/windowsAuthentication
    Right hand pane, click Unlock Section
Posted in Phantom JS, Support Ticket

Richtext Editor - Table Properties adds cells to the table

When you edit the properties of a table in the content editor of most page types, extra cells are added. Check the following animation to see the issue:

Table Issue

 

This issue has been reproduced in empty (clean) instances of Sitecore 8.1 rev 160302, 160519 and Sitecore 8.2 rev 161221, and has been registered by Sitecore as a bug at their Bug Tracking System with the reference number 101901.

Solution

To resolve the issue, please update the Telerik RTE assemblies to the newer version:

  1. Extract the Telerik.Web.UI.dll and Telerik.Web.UI.Skins.dll assemblies into your bin folder (replacing the existing ones);
  2. Replace the Telerik.Web.UI.XML file in your bin folder with the attached one;
  3. Add the Telerik.Web.UI assembly binding to your web.config:
    <runtime>
     <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
     ...
     <dependentAssembly>
     <assemblyIdentity name="Telerik.Web.UI" publicKeyToken="121fae78165ba3d4" />
     <bindingRedirect oldVersion="2015.1.401.45" newVersion="2015.3.930.45" />
     </dependentAssembly>
     </assemblyBinding>
    </runtime>
Posted in Uncategorized

Language specific MediaProvider breaking icons at Media Library

UPDATE: Kamruz Jaman has contributed with a comment for a simpler, less intrusive and more generalist solution:  Inside the GetMediaUrl method of your custom MediaProvider, check for the “Thumbnail” property of your MediaUrlOptions parameter. If it is true, make your code fall back to the base, such as:

if (mediaOptions.Thumbnail)
return base.GetMediaUrl(mediaItem, mediaOptions);

During an upgrade from Sitecore 6.5 to Sitecore 8.1 one issue were noticed at the Media Library: when a Media Item is uploaded, the icon appears broken.

Broken Icon

A custom MediaProvider is used at this build to modify the way Sitecore generates Media Item urls. All it does is to add the language at beginning of the media URL so we can have language-specific media items. This is important to allow the same Media Item (etc: an image or PDF) to be served in multiple languages.

Root cause

When a media item is uploaded to the Media Library, Sitecore will fill the “Icon” field with the URL of the media item itself. Because of the custom MediaProvider, language is being added to the beginning of the generated URL. Apparently that was not a problem in Sitecore 6.5, but things seems to have changed at Sitecore 8.

Technical explanation by Sitecore

A Support Ticket #476256 were created to allow further investigation from Sitecore on this issue. According to the technical team:

“The behavior you reported is expected as tree node icons are rendered by Sitecore.Shell.Applications.ContentManager.Sidebars.Tree.RenderIcon(Item item) method which does not use language definition, embedded to URL, to resolve item language. So when the Icon field contains URL with language definition embedded, it breaks the method functionality and leads to incorrect resolution of the item’s icon.

When you have versioned media item, the icon is rendered for specific language automatically even when language is not specified deliberately. For instance, if you have media item with two language versions and different images for each language, and use -/media/ItemGUID.ashx?h=16&thn=1&w=16 string in the Icon field, language specific icon will be rendered when you switch the language in the Content Editor.”

This explains the causes but doesn’t fix the issue: how can we skip or bypass the custom MediaProvider, avoiding the language to be added to the URL when the “Icon” field is filled?

Solution

In order to fix that we will need to extend and override the Sitecore.Resources.Media.Media class, responsible for the upload and update of metadata when a Media Item is uploaded to the Media Library, then modify all Media Types to use your class instead of the original.

Step 1 – Extend the Media class

This is going to be responsible for removing the language from the URL when a Media Item is uploaded. In bold you see the code responsible for that, rest is basically native code to ensure compliance with default Sitecore behavior.

using Sitecore;
using Sitecore.Configuration;
using Sitecore.Data.Items;
using Sitecore.Data.Proxies;
using Sitecore.Diagnostics;
using Sitecore.Resources.Media;
using Sitecore.SecurityModel;

namespace CustomMediaProvider
{
  public class Media : Sitecore.Resources.Media.Media
  {
    public override Sitecore.Resources.Media.Media Clone()
    {
      return new Media();
    }

    public override void UpdateMetaData(MediaStream mediaStream)
    {
      Assert.ArgumentNotNull(mediaStream, "mediaStream");
      var innerItem = MediaData.MediaItem.InnerItem;
      if (!innerItem.Paths.IsMediaItem) return;
      using (new EditContext(innerItem, SecurityCheck.Disable))
      {
        innerItem["extension"] = mediaStream.Extension;
        innerItem["mime type"] = mediaStream.MimeType;
        innerItem["size"] = mediaStream.Length.ToString();
        if (Settings.Media.AutoSetAlt)
        {
          innerItem["Alt"] = innerItem.Name;
        }
        var shellOptions = MediaUrlOptions.GetShellOptions();
        shellOptions.Thumbnail = true;
        shellOptions.Height = 0x10;
        shellOptions.Width = 0x10;
        var realItem = ProxyManager.GetRealItem(innerItem, true);
        // CODE ADDED TO REMOVE LANGUAGE FROM URL WHILE UPLOADING
        var iconUrl = MediaManager.GetMediaUrl(realItem, shellOptions);
        var prefix = "/" + Context.Language.Name + "/";
        if (iconUrl.StartsWith(prefix)) iconUrl = iconUrl.Replace(prefix, "");
        innerItem.Appearance.Icon = iconUrl;
      }
    }
  }
}

Step 2 – Register the Media class to all Media Types

This is the painful part: we will need to register our class to every Media Type we want to cover. There is no easy way for that other than adding a node for each Media Type, paying attention to the correct way for patching that specific node (some nodes may have a Media class already defined and thus needs correct patching, others don’t).

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
 <sitecore>
 <mediaLibrary>
 <!-- This is our Custom MediaProvider causing all the trouble -->
 <mediaProvider>
 <patch:attribute name="type">CustomMediaProvider.MediaProvider,CustomMediaProvider</patch:attribute>
 </mediaProvider>
 <!-- And here goes our custom Media class being attached to several Media Types -->
 <mediaTypes>
 <mediaType name="Any" extensions="*">
 <prototypes>
 <media type="CustomMediaProvider.Media, CustomMediaProvider" patch:instead="*[@type='Sitecore.Resources.Media.Media, Sitecore.Kernel']"/>
 </prototypes>
 </mediaType>
 <mediaType name="AVI video" extensions="avi">
 <prototypes>
 <media type="CustomMediaProvider.Media, CustomMediaProvider"/>
 </prototypes>
 </mediaType>
 <mediaType name="Windows Bitmap image" extensions="bmp, dib">
 <prototypes>
 <media type="CustomMediaProvider.Media, CustomMediaProvider" patch:instead="*[@type='Sitecore.Resources.Media.ImageMedia, Sitecore.Kernel']"/>
 </prototypes>
 </mediaType>
 <mediaType name="Flash" extensions="swf">
 <prototypes>
 <media type="CustomMediaProvider.Media, CustomMediaProvider" patch:instead="*[@type='Sitecore.Resources.Media.SwfMedia, Sitecore.Kernel']"/>
 </prototypes>
 </mediaType>
 <mediaType name="Flash video" extensions="flv">
 <prototypes>
 <media type="CustomMediaProvider.Media, CustomMediaProvider"/>
 </prototypes>
 </mediaType>
 <mediaType name="GIF image" extensions="gif">
 <prototypes>
 <media type="CustomMediaProvider.Media, CustomMediaProvider" patch:instead="*[@type='Sitecore.Resources.Media.ImageMedia, Sitecore.Kernel']"/>
 </prototypes>
 </mediaType>
 <mediaType name="HTML" extensions="htm,html,stm">
 <prototypes>
 <media type="CustomMediaProvider.Media, CustomMediaProvider"/>
 </prototypes>
 </mediaType>
 <mediaType name="JPEG image" extensions="jpg, jpeg, jpe, jfif">
 <prototypes>
 <media type="CustomMediaProvider.Media, CustomMediaProvider" patch:instead="*[@type='Sitecore.Resources.Media.ImageThumbnailGenerator, Sitecore.Kernel']"/>
 </prototypes>
 </mediaType>
 <mediaType name="MP3" extensions="mp3">
 <prototypes>
 <media type="CustomMediaProvider.Media, CustomMediaProvider" patch:instead="*[@type='Sitecore.Resources.Media.Mp3Media, Sitecore.Kernel']"/>
 </prototypes>
 </mediaType>
 <mediaType name="MP4 video" extensions="mp4">
 <prototypes>
 <media type="CustomMediaProvider.Media, CustomMediaProvider"/>
 </prototypes>
 </mediaType>
 <mediaType name="PDF file" extensions="pdf">
 <prototypes>
 <media type="CustomMediaProvider.Media, CustomMediaProvider"/>
 </prototypes>
 </mediaType>
 <mediaType name="PNG image" extensions="png">
 <prototypes>
 <media type="CustomMediaProvider.Media, CustomMediaProvider" patch:instead="*[@type='Sitecore.Resources.Media.ImageMedia, Sitecore.Kernel']"/>
 </prototypes>
 </mediaType>
 <mediaType name="SVG image" extensions="svg">
 <prototypes>
 <media type="CustomMediaProvider.Media, CustomMediaProvider" patch:instead="*[@type='Sitecore.Resources.Media.SvgMedia, Sitecore.Kernel']"/>
 </prototypes>
 </mediaType>
 <mediaType name="QuickTime movie" extensions="mov, qt">
 <prototypes>
 <media type="CustomMediaProvider.Media, CustomMediaProvider"/>
 </prototypes>
 </mediaType>
 <mediaType name="TIFF image" extensions="tiff, tif">
 <prototypes>
 <media type="CustomMediaProvider.Media, CustomMediaProvider" patch:instead="*[@type='Sitecore.Resources.Media.ImageMedia, Sitecore.Kernel']"/>
 </prototypes>
 </mediaType>
 <mediaType name="Zip file" extensions="zip">
 <prototypes>
 <media type="CustomMediaProvider.Media, CustomMediaProvider" patch:instead="*[@type='Sitecore.Resources.Media.ZipMedia, Sitecore.Kernel']"/>
 </prototypes>
 </mediaType>
 <mediaType name="Video for Adobe Flash Player" extensions="f4v">
 <prototypes>
 <media type="CustomMediaProvider.Media, CustomMediaProvider"/>
 </prototypes>
 </mediaType>
 <mediaType name="Windows Media video" extensions="WMV">
 <prototypes>
 <media type="CustomMediaProvider.Media, CustomMediaProvider"/>
 </prototypes>
 </mediaType>
 <mediaType name="Word document" extensions="doc">
 <prototypes>
 <media type="CustomMediaProvider.Media, CustomMediaProvider"/>
 </prototypes>
 </mediaType>
 <mediaType name="Word 2007 document" extensions="docx">
 <prototypes>
 <media type="CustomMediaProvider.Media, CustomMediaProvider"/>
 </prototypes>
 </mediaType>
 <mediaType name="PPTX File" extensions="pptx">
 <prototypes>
 <media type="CustomMediaProvider.Media, CustomMediaProvider"/>
 </prototypes>
 </mediaType>
 <mediaType name="PPT File" extensions="ppt">
 <prototypes>
 <media type="CustomMediaProvider.Media, CustomMediaProvider"/>
 </prototypes>
 </mediaType>
 <mediaType name="XLSX File" extensions="xlsx">
 <prototypes>
 <media type="CustomMediaProvider.Media, CustomMediaProvider"/>
 </prototypes>
 </mediaType>
 <mediaType name="XLS File" extensions="xls">
 <prototypes>
 <media type="CustomMediaProvider.Media, CustomMediaProvider"/>
 </prototypes>
 </mediaType>
 </mediaTypes>
 </mediaLibrary>
 </sitecore>
</configuration>
Posted in Bug fixing, Development, Support Ticket, Upgrades

ERROR [Content Testing]: Cannot find PhantomJS executable at ' (...) /data/tools/phantomjs/phantomjs.exe'. Aborting screenshot generation.

In Sitecore 8 and above, while starting a Multivariate Tests, you may see broken thumbnails and popups with warnings such as below, and your Sitecore Log showing the error at this blog title.

Error Running Test - PhantomJS - 1
Error Running Test - PhantomJS - 2
According to what Sitecore Development Team announced at this community post for why Sitecore adopted PhantomJS:

Content Testing in Sitecore uses the Phantom JS tool for generation of the screenshot image files. In case you didn’t know, Sitecore has had screenshot generation features for quite some time. We use it to generate icons for items that you’ll be listing in the UI like renderings. These icon generation features are based on the System.Windows.Forms.WebBrowser control built into .net. So why did we not use the existing screenshot features for Content Testing? In early testing we found discrepancies with the WebBrowser control. WebBrowser uses Internet Explorer installed on the server where Sitecore is running. But the specific version that it uses isn’t always the latest. There are registry updates one can make to force the appropriate IE version, but this seemed like a big ask of users. This was one of the reasons we chose to use Phantom JS instead.

Cause and Solution

The error in question will show the full path to where Sitecore is looking for PhantomJS. This path is defined at Sitecore.ContentTesting.config, at the entry “ContentTesting.PhantomJS.ExecutablePath”. Start your investigation by checking where your setting is currently pointing to.

By default this setting points to “$(dataFolder)/tools/phantomjs/phantomjs.exe”. If that is your case, check if your Data folder has the PhantomJS executable under that path. If this file is lacking, take a copy from a fresh Sitecore installation (make sure to use the very same revision your site is using).

See also: PhantomJS and security hardening

Posted in Uncategorized Tagged with: ,

Workflow not starting (state field is empty) when Versions.AddVersion after Sitecore upgrade from 6.5 to 8.1

UPDATE: A ticket at Sitecore Helpdesk demonstrated the issue happened to be something else. During the upgrade, the site definition we’re using got the attribute “enableWorkflow” removed, which in turn made the default value “false” take over, and that was causing ALL WORKFLOWS to be simply ignored.

If your site does have enableWorlflow=”true” then you don’t need to manually start workflows as shown in this article

After an upgrade, a certain code was simply not working as expected anymore. When you create a new version of an item (item.Versions.AddVersion), whose _Standard Values had a Default Workflow setup, the workflow simply was not starting, and thus the “State” field gets empty. The very same code, at the non-upgraded version, works perfectly.

Looking at the code, it was never touching Workflows, suggesting that Sitecore would be automatically handling the thing. Since it was an upgrade, I lost myself investigating for things I could have left behind: Pipelines, Scheduled Tasks, etc.

And I then discovered it was Sitecore that changed the way how the API acts… a change that made me struggle in for almost a week!  And I just learned that when I decided to create a small script to run the same code on both 6.5 and 8.1 instances.

The code was getting an item by it’s ID, adding a version and printing the number of the newly created version:

Workflow not starting - 1

Sitecore 6.5

When I run that on 6.5, which has 5 versions before:

Workflow not starting - 2

The new version is created:

Workflow not starting - 3

And the State is correctly filled:

Workflow not starting - 4

Sitecore 8.1

However when I run it on 8.1, which previously had 39 versions:

Workflow not starting - 5

The 40th version is created, but the State field is empty!

Workflow not starting - 6

Solution

So what I had to do, to make the same code run correctly on 8.1, was to force the item thru the workflow after a new version is created. That screw hard to find, easy to twist:

Item newVersion = myItem.Versions.AddVersion();

var defaultWorkflow = newVersion.Fields[FieldIDs.DefaultWorkflow].Value;
ID defaultWorkflowId;
if (!ID.TryParse(defaultWorkflow, out defaultWorkflowId))
    return;

var workflow = newVersion.Database.WorkflowProvider.GetWorkflow(defaultWorkflowId.ToString());
if (workflow!=null)
    workflow.Start(newVersion);
Posted in Sitecore API, Upgrades, Workflow

Developers working in parallel with Continuous Integration builds for Dev and QA

When multiple developers are working in parallel (at the same build, but working in different parts) it is very common to use branches at source control, to keep each developer working in isolation and eliminating any chance that one developer’s work interferes with the work of another. Of course one day their work will have to be integrated, and that is the moment when things can go wrong. The following article proposes one of many possible setups to enable that isolation.

Creating a branch from another branch is very simple and easy. When working with a Continuous Integration system, however, your branch is supposed to be monitored, built and deployed to a place people can access and test it.

A branch and its servers with Continuous Integration

The following image illustrates a scenario where multiple developers are working at the same branch, and a second branch is used as a basis to deployments. The blue boxes at the right represents servers with specific purposes. Being now a Sitecore good practice to develop and test in scaled environment, all environments will have two nodes, one for Content Management (CM) and another for Content Delivery (CD).

01 - All in the same branch

The Continuous Integration server monitors the branch “All developers” and deploys changes to DEV (at every change) and QA (manually). When the time for a deployment comes, the work is merged to Deploy Branch. The Continuous Integration server will then make the last integration to the servers “Deploy-CM” and “Deploy-CD”, where things can be tested for the last time before the actual deployment.

The Problem with this approach is that all developers are pushing their modifications to the very same branch, and thus deployments must carry everything at that branch (except of course, if very granular merges are made, but that can also be tricky and error-prone). To enable a better safe separation, the following setup is proposed.

Multiple Branches

The image below now shows a second branch, with its respective DEV and QA servers. Now each developer can make their work at their own branch, keeping a physical separation that guarantees one developer’s work won’t interfere with the other.
02 - Two DEV branches

The image also shows that branch “Dev1” (the older branch) were used to create branch “Dev2”, which guarantees that both branches are identical at that point in time. Obviously, each branch will have their modifications from that time on, and the code integration is now a bit harder to be executed. As time passes, each branch will be more and more different, bringing the need to a more careful exercise during the integration, executed at the “Deploy” branch.

Deployments and Reverse Merges

When all developers were working at the same branch, the act of checking in their changes were the integration itself. Continuous Integration server would push it to the servers, and integrations were continuously made due to the same code base. But now with physically separated branches, developers must communicate and keep their branches as much in sync as possible. The following images shows a sequence of deployments and reverse merges, that would keep both Dev branches in sync while sustaining their separations.

Image 1 – First Deployment (by Dev1) and Reverse Merge (by Dev2)

03 - First Deploy

Image 2 – Second Deployment (by Dev1) and Reverse Merge (by Dev2)

04 - Second Deploy

Image 3 – First Deployment (by Dev2) and Reverse Merge (by Dev1)

05 - Third Deploy

 

Posted in Architecture, Continuous Integration, Dev, Environments, QA