Archive

Archive for January, 2009

External Application inside Sitecore Desktop

January 28, 2009 5 comments

Recently I had to integrate Sitecore with an external application written in PHP. The integration included making the PHP application appear inside the Sitecore desktop, so it would appear to the end user that the external application was actually a part of Sitecore. Now, this is actually quite easy to do as we can make any ASPX page an application inside the Sitecore desktop.

An application definition is kind of like a normal navigable item on your website. You have to define the presentation of the application before you can run it.

Applications are defined in the core database, so once inside the desktop, use the database switcher in the bottom right of your screen to swap to the core database.

DBSwitcher

Now we have to create a layout for the application to give us an ASPX to create our presentation. All we’ll do in our ASPX is Response.Redirect to the application we want to integrate. We can redirect comfortably because each window in the desktop is actually run inside it’s own iframe, so the host environment won’t upset the application and vice versa.

Open the developer centre and create a new layout. Next open the content editor and navigate to /sitecore/content/Applications. Now create a new “Application” item and define it’s presentation as just using the layout we created.

exapp layout

Now to add a shortcut for the application into the menu so we don’t have to use the “Run” application to execute it. These are defined at /sitecore/content/Documents and settings/All users/Start menu. I want it to just appear in the “All Applications” section, so I’ll navigate down to the “Programs” item in the content editor and create a new “Application Shortcut” there. We just have to fill in the “Application” field on the shortcut item to point at the application we created above. The application won’t appear immediately in your menu as you have to refresh the client to have the menu recreated. The easiest way to do this is to switch back to the master database using the database switcher.

If you don’t see your shortcut on the menu after you’ve switched back to master, check Chris Wojciech‘s blog post Create application links in the sitecore startmenu (don’t work). For some reason the insert link dialog appends a “.aspx” after your applications path. Just remove the “.aspx” in this field and all should be fine.

Now to have my application redirect. You could either do this in code behind, or even inline in the aspx file. I’ll choose the latter option here for brevity. My layout’s aspx file should look like the following.

<%@ Page language="c#" Codepage="65001" %>
<%@ OutputCache Location="None" VaryByParam="none" %>
<% Response.Redirect("http://www.google.com"); %>

We should probably ensure the page’s URL is configurable incase the URL of our external application changes. This could be placed in a config file, but I prefer the content tree. Seeing as though this config data is to do with a Sitecore client application, I’ll store it in the core database. That’s also where I have to create my template which defines the structure of the config item.

exapp config

To read the config item and redirect to the configured URL, I just change the last line of my aspx file. Note that when inside the desktop the Context.Database is by default the core database.

<% Sitecore.Data.Items.Item config = Sitecore.Context.Database.GetItem(
  "/sitecore/system/Modules/External Application/Config");
Response.Redirect(((Sitecore.Data.Fields.LinkField)config.Fields["url"]).Url); %>

Now I can point the URL field at any external (or internal) URL and have the application appear inside the desktop and appear as if it’s part of Sitecore. That might be fine for basic applications, but what about those that require a login? You don’t want to have your users logging into Sitecore and then having to type login information into another window. Luckily through the magic of HTML we can post a populated form to whatever URL we desire. For this example, I’ll alter the above application to silently log into an application written in PHP, that has a standard login form. (I chose PHP cause PHP reads the raw form data and doesn’t augment it with control classes like .net does. Just makes my example easier.) The username and password for the account being used are stored in the config item along side the URL which we’ll post our form to. This saves the users from having to enter an additional password and allows single sign on. This also only works if the same account is being used by everyone for the external application, unless you changed the configuration item to be per user.

So now my ASPX file looks like the following:

<%@ Page language="c#" Codepage="65001" %>
<%@ OutputCache Location="None" VaryByParam="none" %>
<%@ Import Namespace="Sitecore" %>
<% Sitecore.Data.Items.Item config =
  Sitecore.Context.Database.GetItem(
    "/sitecore/system/Modules/External Application/Config"); %>
<html>
  <body onload="javascript:document.forms[0].submit();">
    <form action="<%= ((Sitecore.Data.Fields.LinkField)
config.Fields["url"]).Url %>"
      method="post">
      <input type="hidden" name="username"
        value="<%= config["username"] %>"/>
      <input type="hidden" name="password"
        value="<%= config["password"] %>"/>
    </form>
  </body>
</html>

What we need to populate into the form will depend on the target application. For example, when posting to a .net application it is not normally adequate to just fill in the form fields and submit the form. The dev normally hooks into the asp:Button’s OnClick event and so you have to simulate the click event firing from the button control.

And another thing, don’t forget the security. As the application definition and menu shortcut are just items, we can apply security to the items so only authorised users can execute the application or see it on their menu.

Categories: Sitecore

PNG curse? Solve it with a link.

January 21, 2009 6 comments

This is another post which starts out with me reading another post :) . I was reading the Molten Core blog which is written by Thomas Eldblom (fellow MVP) and Jens Mikkelsen. Thomas published a post in which he points out the large number of PNG icon files included in Sitecore. Sitecore contains an extensive icon library consisting of 1,000′s of files. And each of these files is provided in several resolutions to improve the quality on screen. Have you ever tried zipping up an existing Sitecore solution? It takes a while. Not because it’s huge, but because there are so many little files. Same with applying attributes (security) to a folder containing a Sitecore solution.

I have often thought about somehow removing the icons folders and sharing them amongst several Sitecore instances. Like most Sitecore devs, I have several Sitecore instances on each of my machines. If you had to move even just one of these sites from box to box, such as taking a site home on the laptop to work on, it’s a pain.

One thing about reading blogs through RSS; You miss out on the comments. Thomas points out the issue in his post, but there are some great solutions to the problem, and discussion, in the comments.

Alexey Rusakov from Sitecore tells us about the different ways in which Sitecore have tried to minimise the impact of the situation. Mark Cassidy (fellow MVP) from CorePoint told us that Per Bering from Codehouse compiled all the images into a resource DLL and used a custom HTTP Module to serve from the resource DLL. Jakob Christensen from Sitecore pointed out you could use virtual directories in IIS to remap the icon folders to a single shared location. He even posted a NAnt build file which would do this for you. While reading all this I thought “If only we had symbolic links in Windows like we have in *nix. That would solve the problem.”

Seeing all these great comments got me motivated to rewrite Jakob’s build script in MSBuild. I used to be a huge fan of Ant and NAnt, but since Microsoft bought out MSBuild with .net 2.0 I have usually found everything I need in that. Every Visual Studio project file is actually an MSBuild project file. So I got to work on making this happen.

Out of the box, MSBuild can’t talk to IIS. But MSBuild is very extensible, and the MSBuild community tasks provide extensions to allow you to communicate with IIS. Unfortunately I couldn’t get this working with Vista :( (perhaps I didn’t try hard enough.) So I looked around for a different way of doing it. MSBuild also let’s you execute any command through the “Exec” task. IIS 7, which is the version which ships with Vista, provides a command line tool called APPCMD (find it at %systemroot%\system32\inetsrv\APPCMD) which allows you to manage IIS from the command line. I found this page very helpful in working out how to create virtual directories.

But seeing as though I was just using Exec, I decided to change my script from MSBuild to a plain old batch file. It was while I was trawling through help files trying to remember how to check if a script argument (%1) was not provided that I stumbled across a new command I hadn’t seen before. MKLINK.

MKLINK allows you to create symbolic links in Vista and Windows 2008…

WHAT??!!?

I can create symbolic links in Vista??!!?

I MISSED THE MEMO!

That’s right, as of Vista (sorry XP guys) we now have symbolic links. I was ecstatic. I have been longing for this minor feature for so long in Windows.

So now it’s as easy as:

  1. Move an existing Theme folder to a location where it can be shared amongst all your Sitecore sites such as c:\inetpub\Themes.
  2. Delete all the Theme folders under each Sitecore website (perhaps you should check first and make sure you’re not losing any files).
  3. Create a symbolic link under the Sitecore site to the shared location.
cd c:\inetpub\mysitecoresite\website\sitecore\shell\
mklink /d Themes c:\inetpub\Themes

Symbolic links operate at the file system level, below IIS. To users and applications it’s as if this is a real file. Now this is going to save me a lot of headaches.

Yay symbolic links!

Categories: Sitecore

No Workflow for Web

January 13, 2009 6 comments

I was reading a post by Alex Shyba recently which showed how you can get workflow history using the Sitecore API. This post reminded me of a post I was going to write a while ago on the subject of the workflow APIs. At the bottom of the above post, Alex points out that you could use the code in a workflow email action to get the history and send an email to the user who submitted the item into the current workflow state.

This code runs fine when it’s being executed as part of a workflow, but it will fail if you try to run it from the website. Why might I want to run this code (or similar) on the website? Allow me to elaborate.

I love the simplicity and power of the Sitecore workflow engine. And I don’t just use it for content approval. Let’s say we have a “contact us” form on the website. Usually the way these forms works is they collect the information from the user then email it to a predefined email address. You might even pull the email address from the content tree to allow admins to update the recipient without having to recompile the app. But email is not a guaranteed delivery mechanism. So in this case, instead of relying on email, I prefer to store the “contact us” data which has been submitted in the content tree. What’s more, after creating the item to store the data, I put the item into workflow (well, it goes into workflow automatically).

There are several benefits to using workflow for this kind of non-content data. Firstly, even if not defined, “contact us” data does follow a workflow. At it’s simplest, it starts off in the “received” state and would transition to the “actioned” state after someone had read it. Sitecore workflow is a great way to track and make sure all “contact us” data has been actioned. You could also extend this simple workflow to include sending the data off to more appropriate parties. The initial reviewer might determine it’s a product related query and execute the “send to marketing” workflow command.

With the data in workflow it’s very easy for reviewers to see all the “contact us” data that requires their attention in their workbox. The marketing people don’t even see the data until the initial reviewer has looked over the item and determined that it is in fact a query that marketing needs to deal with.

It’s also very easy to extend the workflow to include sending emails. You might have an action on the initial workflow state that sends an email to the initial reviewer alerting them that new contact data has been received. You could also incorporate emails which are sent to the submitter (website user) telling them what’s going on with their query.

“Thanks for your email. We’ve got it and we’re looking into it”.

“Thanks for your email. Someone from our marketing team will contact you shortly.”

So back to making the code work on the website. Have you ever attempted to run code that calls the workflow API from the website? It will fail. Why? Because the Workflow provider is null. Why? Well, have a look at your web.config and the database and site definitions. The master database contains nodes to create the workflow provider. The web database does not have the required configuration to run workflow. The “website” site also does not have the enableWorkflow attribute set to true as the “shell” website does. You not only have to enable workflow on the database, you also need it enabled on the current site context.

If you’re going to be creating items from the live website (such as in the example above), make sure you’re creating them in the master database. If you don’t and you create them in the current context database, you’ll end up losing those items on the next publish. This will take care of placing the item into the correct workflow and workflow state upon creation.

And because we’re now operating the in master database, the workflow provider is no longer null and we can make other workflow API calls. Although, if you’re still on the “website” site, many of these calls will fail. You can swap over to the “shell” site by calling Context.SetActiveSite(“shell”). Incidently the code Alex posted will all work without changing to the “shell” website, but my code at the bottom of this post will.

Let’s say the “contact us” item we create as a result of submitting the “contact us” form is more complex than an average form, and there are lots of fields to input on the item. We’ve also added a workflow action to the initial state to send an email to the initial reviewer and we include some item data such as title and submitters email in the email to the reviewer. If this action was placed on the initial workflow state, then when the action reads the item data it will likely be empty. This is because the action is executed as soon as the item lands in that state. So as soon as the item is created, and before you have a chance to enter the data into the item, the action is executed and the email containing empty data will be sent.

To work around this, you just need to introduce a state before the initial state above. When all the data is entered, we then execute the workflow command to move the item along to the next state, where the action will now execute with the item’s field data available.

The following code will create a new item, populate the fields, then find the command with name “submit” on the initial workflow state and execute it to move the item to the next workflow state.

string sitename = Context.Site.Name;
Context.SetActiveSite("shell");
Database db = Configuration.Factory.GetDatabase("master");
Item item = db.GetItem("/sitecore/content/home/contact").Add("my item",
  db.Templates["user defined/contact"]);
using(new EditContext(item))
{
  // Fill in item fields
}
IWorkflow workflow = db.WorkflowProvider.GetWorkflow(item);
WorkflowCommand[] commands = workflow.GetCommands(item);
WorkflowCommand command = null;
for (int i = 0; i < commands.Length; i++)
  if (string.Compare(commands[i].DisplayName, "submit", true) == 0)
    command = commands[i];

if (command != null)
{
  WorkflowResult result = workflow.Execute(commands[0].CommandID, item,
    "Item Created", false);
  if (!result.Succeeded)
  {
    // Command failed
  }
}
SC.Context.SetActiveSite(sitename);

Note I preserve the current site name at the top before I set “shell” as the active site and I revert back to the initial context site when I’m done with the workflow API.

Categories: Sitecore

It’s official, welcome the MVPs!

January 6, 2009 2 comments

Well, it’s official.

Today Sitecore made a press release about this years MVPs. And I am so proud to finally be able to say that I am now a Sitecore MVP. I join 12 others from the wider Sitecore community who have all been named MVPs by Sitecore. Read the press release for more details on what the MVP program is, and how we all ended up there.

So onto the congrats and thanks!

Congratulations to Christopher Wojciech, Julius Ganns, Justin Sjouw, Klaus Petersen, Mark van Aalst, Mark Cassidy, Thomas Eldblom, Eric Briand, Andy Uzick, Glen McInnis, Alexander Pfandt and Ben Golden for also being named MVPs this year. I have read many of your blogs and contributions before, and I now have some more blogs to add to my list.

Next, thank you to Philipp Heltewig and Jimmie Overby for nominating me (and anyone else involved in that). Thanks Phil for pushing me to start a blog and for supporting me in my Sitecore endeavors. Thanks Jimmie for helping me get hooked on shared source.

A huge thanks to my beautiful wife Tina who puts up with me tapping away on the laptop on the couch each evening, whilst I explore and communicate to the Sitecore world.

And finally, thanks to all who voted for me. It is so great to be recognized and this award is a huge motivator to get some more of my projects finished and start some new ones.

Categories: Sitecore
Follow

Get every new post delivered to your Inbox.