Slideshows and Page Editor

I want my content editors to be able to do anything in the Page Editor that they can do in the Content Editor, giving them the choice to use what they prefer. However, when the editor wants to change a slideshow on a page they must do this through the Content Editor, because when they try to make a change in the Page Editor they get the following error message (or a message similar to it):

Uncaught DOM element is expected to have a non-empty chromes collection.
    Sitecore.PageModes.Chrome.Base.extend._clickHandler
    c
    d.event.handle
    k.handle.m

I was talking about this with another Sitecore developer during a Sitecore Usergroup, and what he suggested was instead of showing it as a slideshow in Page Editor mode, have the images and text be underneath each other. While that’s certainly a valid option, I don’t really like it because it takes away one of the strengths of the Page Editor: See what the result will be while you are changing it.

What I decided to do was create an additional button in the Page Editor and create my own class that inherits from Sitecore’s FieldEditorCommand class.

Create the command

I’ve created a class which inherits the Sitecore.Shell.Applications.WebEdit.Commands.FieldEditorCommand.

For display purposes I’ve overridden the CommandState as well, to disable the button if the page doesn’t have a slideshow. I simply check this by getting the value of a field called Source:

public override CommandState QueryState(CommandContext context)
{
  if (string.IsNullOrEmpty(Sitecore.Context.Item["Source"]))
  {
    return CommandState.Disabled;
  }

  return base.QueryState(context);
}

The Execute method will call the field editor, so we’ll need to implement that:

public override void Execute(CommandContext context)
{
  Assert.ArgumentNotNull(context, "context");
  if (context.Items.Length >= 1)
  {
    var args = new ClientPipelineArgs(context.Parameters);
    args.Parameters.Add("uri", context.Items[0].Uri.ToString());
    Sitecore.Context.ClientPage.Start(this, "StartFieldEditor", args);
  }
}

Now all that’s left is overriding the GetOptions method. The Slideshow field is a dropdown pointing to an item with a multilistfield called Images. We’ll want to edit the Title, Keywords and Description fields of the respective images:

protected override PageEditFieldEditorOptions GetOptions(ClientPipelineArgs args, NameValueCollection form)
{
  Assert.IsNotNull(args, "args");
  Assert.IsNotNull(form, "form");
  Assert.IsNotNullOrEmpty(args.Parameters["uri"], "uri");

  Sitecore.Data.ItemUri uri = Sitecore.Data.ItemUri.Parse(args.Parameters["uri"]);
  Assert.IsNotNull(uri, "uri");

  var item = Sitecore.Data.Database.GetItem(uri);
  Assert.IsNotNull(item, "item");

  var slideShowItem = Sitecore.Context.ContentDatabase.GetItem(item["Source"]);
  Assert.IsNotNull(slideShowItem, "slideShowItem");

  var fields = new List<Sitecore.Data.FieldDescriptor>();
  fields.Add(new Sitecore.Data.FieldDescriptor(slideShowItem, "Images"));
  var images = (MultilistField)slideShowItem.Fields["Images"];
  foreach (var image in images.GetItems())
  {
    foreach (string fieldName in GetFieldNames())
    {
      fields.Add(new Sitecore.Data.FieldDescriptor(image, image.Fields[fieldName].Name));
    }
  }

  var options = new PageEditFieldEditorOptions(form, fields);
  options.PreserveSections = false;
  options.DialogTitle = "Edit the slideshow");
  options.Icon = item.Appearance.Icon;
  return options;
}

private IEnumberable<string> GetFieldNames()
{
  yield return "Title";
  yield return "Keywords";
  yield return "Description";
}

Note that I’m not only adding the image fields (Title, keywords and description), but also the multilistfield in which the images are selected (as highlighted in line 17).

The one drawback of course is that we need to save the changes we make before the page reflects them, and if we change the Images multilistfield we’ll need to re-open the editor to see that reflected on our popup.

Include the command:

I’ll use an include file to patch my config.

  <?xml version="1.0"?>
  <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
    <sitecore>
      <commands>
        <command name="pageeditor:slideshow" type="TestApplication.PageEditorSlideshows.PageEditorSlideshows, TestApplication" />
      </commands>
    </sitecore>
  </configuration>

Add the button:

To add a button to the Page Editor we’ll need to open the Core database and navigate to /sitecore/content/Applications/WebEdit/Ribbons/WebEdit. I’ll create my button under the existing ‘Page Editor/Edit’ node:

tree

I’ve added the following field values:

Header:   Edit slideshow
Icon:         Applications/32×32/photo_scenery.png
Click:         pageeditor:slideshow
Tooltip:    Edit the slideshow

Now it’s time to test it!

This is how it looks on a page without a slideshow attached:

ribbon_disabled

We could’ve hidden the button instead (by returning CommandState.Hidden instead of CommandState.Disabled), but I tend to just disable them to keep the ribbon consistent across the board.

This is how it looks on a page which does have a slideshow:

ribbon_enabled

If we then click our new button:

popup

Perfect.

Page Editor JavaScript difficulties

When I was implementing the WeBlog module on one of my projects, I ran into a couple of strange JavaScript errors when using the Page Editor to insert components. I noticed two things about the issues:

  1. They only showed up on WeBlog pages
  2. They only showed up when I tried adding a component to any place except the top – if an existing component was there and I’d add it below it would work.

The errors:

When I clicked Component – Add to here in the Page Editor, I got one of the two following errors when I looked at them in Chrome:
Could not find the rendering in the HTML loaded from server
    Sitecore.PageModes.ChromeTypes.Placeholder.Sitecore.PageModes                                                                  .ChromeTypes.ChromeType.extend._frameLoaded
    c 
    d.event.handle 
    k.handle.m

OR

insertRendering
    Cannot find rendering chrome with unique id: 17A541CA4BC34DCBB60F783861BF3191
    Sitecore.PageModes.ChromeTypes.Placeholder.Sitecore.
PageModes.ChromeTypes.ChromeType.extend.insertRendering
    options.success
    f.resolveWith
    v

In addition to that, whenever I clicked anywhere on the page to change one of the renderings/ a piece of text, I also get:

Cannot parse command parameters

Whichever error I got, I would also get a JavaScript popping up saying:

An error ocurred.

I could however still change things around successfully, whether that’s adding or changing text, image, link or even changing a Datasource using ‘Edit the component properties’.

The solutions:

There were actually two issues I was dealing with here, not one:

  1. The first issue was known issue: The Page Editor did not save renderings inserted to a placeholder if the placeholder contained renderings that specified the placeholder key using mixed case. (367952)
    This issue was actually fixed in SItecore 6.6 Update 4 (as specified on SDN)
    After I changed my placeholder keys from camelcase to lowercase, the popup JavaScript error went away, as did the ‘Could not find the rendering in the HTML loaded from server’ error. The second issue remained, however.
  2. The second error is actually a bug (which has been registered with Sitecore Support).

So the first solution was reasonably simple, the second one a bit confusing. Here’s what was going on:

Widgets in WeBlog are based on the /sitecore/templates/Modules/WeBlog/Blog Sublayout template, rather than the default /sitecore/templates/System/Layout/Renderings/Sublayout template. This was so the output of the widgets can be cached per blog.

The Page Editor checks whether the template of the inserted control is the /sitecore/templates/System/Layout/Renderings/Sublayout template. If the control is not based on that template  the control can not be identified as a Sublayout.

Initially I changed the template of the widgets to the default, after that Sitecore Support did come back to me with a possible workaround: a changed version of the /sitecore/shell/Applications/Page Modes/Utility.js file. I have not implemented this (as I didn’t need the caching per blog), so that hasn’t been tested.

Big kudos to Sitecore Support for helping me figure out the cause for these problems.

SonarQube and TeamCity

Recently I attended a Sitecore Usergroup in London, with a talk about Continuous Integration by Andrew Thompson. Among other things, he was talking about SonarQube and how he implemented that in combination with TeamCity. I decided to try my hand on implementing it as well, but found the documentation (or findability of the documentation, more accurately) lacking.

Here are my finds:
I started with installing a local copy of it, with the integrated database. To do this I could simply follow the steps documented in the ‘Get Started in Two Minutes‘ guide.

As that was so easy I figured I’d implement this on our TeamCity server right away, but that was when things got a little more unclear. I tried following the installation guide as much as I could, but initially without the first step – Installing the Database, as I wanted to start off simple. Initially I’d install it with the embedded database (which would of course be a big no-no going forward, so it would have to change later on). Now, I hadn’t had any experience with anything like this before, so I wasn’t sure about the POM file or the properties file, and what I could do with them. A little reading up here told me it was a Project Object Model file, which as the documentation on POMs says: ‘It is a one-stop-shop for all things concerning the project’. Sample pom files for various languages can be found here. I used the sample C# file. A sample sonar.properties file can be found in the same project.

After I created my POM file and properties file I installed the SonarQube server on my TeamCity server. I installed this as a service using the “InstallNTService.bat” file and started the service by using the “StartNTService.bat” file. At the time, I wasn’t an administrator on the server, so I couldn’t initially, but after I had my access rights to the server changed I was able to execute both bat files. My first issue showed up here: I received an error about not being able to start the server because of the language (C#) plugins missing. This is also something the documentation addresses. After installing the C# plugins (which consists of downloading them and copying them into the <sonarfolder>\extensions\plugins folder) I tried running it again, but still no dice, this time I had an error about not being able to locate Java (although I can’t quite remember what the error was). The solution to this was fairly simple, add Java to the PATH environment variable.
This time I got the Sonar server started through the service.

Next step: I had to make sure that my code analysis would be executed from TeamCity, which as specified in the documentation consists of 4 steps:

  1. Make sure the ‘fail build if: at least one test failed’ is unchecked (this can be found in the Build Failure Conditions step in TeamCity).
  2. In the configuration for the Build Step select the runner type Maven with the goals ‘clean install sonar:sonar’.
    As additional Maven command line parameters set ‘-Dmaven.test.failure.ignore=true’
  3. Create a build trigger (I’ve set mine to a daily build)
  4. In the Build Parameters, in the Environment Variables, add a variable called MAVEN_OPTS and set the value to ‘-Xmx512m’.

From TeamCity I now started the project – and it failed. I changed the goal in TeamCity from: Clean install sonar:sonar To: Clean install sonar:sonar –X Which adds extended logging. The issue I was having was that it was using the wrong language (Java) even though I specified sonar.language=cs in my properties file. Adding <sonar.language>cs</sonar.language> to the POM file resolved this. For some reason, it looks like my environment is completely skipping the properties file, which I am currently looking into (although I don’t have too much time to spend on this as it’s already working through the POM file).

Now to remove the database and use a SQL database instead. I found this extremely helpful article to set up Sonar which told me I had to create a new database (I called it simply “sonarqube”) with the collate option SQL_Latin1_General_CP1_CS_AS. The article also gives a seperate link to settings you need to change to enable using a SQL database with SonarQube rather than the default embedded database.

In short, it consists of the following changes:

  • Create a new database (with collate option SQL_Latin1_General_CP1_CS_AS)
  • Set sonar.jdbc.username to the user you want to use
  • Set sonar.jdbc.password to the password of the selected user
  • Change the sonar.jdbc.url to Jdbc:jtds:sqlserver://<database server>;databaseName=database;selectMethod=cursor;
  • Change the sonar.jdbc.driverClassName to com.microsoft.sqlserver.jdbc.SQLServerDriver (this did not work for me – we’ll get to that in a second)
  • Set sonar.jdbc.validationQuery: select 1 (sonar.jdbc.validationQuery wasn’t in my properties file at all, so I had to add this. As I understand it its an optional parameter)
  • Set sonar.jdbc.dialect=mssql (which also wasn’t in my properties file, so added that too)

More information of course is available in the article itself.

After changing the properties to what’s specified in the previous link I was getting errors.
Initially because I didn’t have the correct database access rights for my sonar user, but even after I gave the user dbo access I was getting nowhere starting http://localhost:9000/ – something that was working before I changed databases. My logfiles (located in <sonarfolder>\logs) gave me an error with a huge stack trace, starting with:
Cannot connect to database. Please check connectivity and settings (see the properties prefixed by ‘sonar.jdbc.’). org.apache.commons.dbcp.SQLNestedException: Cannot load JDBC driver class ‘com.microsoft.sqlserver.jdbc.SQLServerDriver’
That did give me a hint of course, and I changed the sonar.jdbc.driverClassName setting to net.sourceforge.jtds.jdbc.Driver (which was the default under SQL Server to begin with). If there’s no driver in <sonarfolder>\extensions\jdbc-driver\mssql it can be downloaded here. Be aware that there can be only one JAR file in that folder.
However, after that it still gave me an error:
INFO | jvm 1 | 2013/08/12 12:58:24 | java.sql.SQLException: No suitable driver
I wasn’t sure how to fix that, so I contacted Andrew to see if he could help out. He kindly agreed to help with my issues: I had my sonar.jdbc.url set to
jdbc:sqlserver://servername;databaseName=sonarqube;selectMethod=cursor;
but it needs to be
jdbc:jtds:sqlserver://servername;databaseName=sonarqube;selectMethod=cursor;
Next thing I encountered was the error
Fail to connect to database: Cannot create PoolableConnectionFactory (Connection is broken: “java.net.ConnectException: Connection refused: connect: localhost
I wasn’t using anything even remotely resembling localhost, so that confused me. Andrew to the rescue again, he was kind enough to send me over one of his POM files.
The difference: In the <properties> node he had the following:
<sonar.jdbc.url>jdbc:jtds:sqlserver://servername;databaseName=sonarqube;SelectMethod=Cursor;</sonar.jdbc.url>
<sonar.jdbc.driver>net.sourceforge.jtds.jdbc.Driver</sonar.jdbc.driver>
<sonar.jdbc.username>username</sonar.jdbc.username>
<sonar.jdbc.password>password</sonar.jdbc.password>
I initially had this in the .properties file, but SonarQube seems to be ignoring that file completely for some reason.

After I added this to my POM file it worked successfully and I could start improving the code.