Monday 25 January 2010

Problem: Inserting custom media types in the WYSIWYG of Umbraco 4.0.2.1 shows blank.gif instead of the correct Url



The Scenario
We had a custom media type called Image. We did this to store more information about each image that was uploaded.

The Problem
The CMS user wants to insert the image to the WYSIWYG of one of the content nodes but sees blank.gif inserted into the Url field when they click on the custom media type. This does not occur for standard Umbraco files.

Investigation
I used Charles to see what Umbraco was doing when a user clicks. Umbraco makes a call to ~/umbraco/dialogs/imageViewer.aspx to get the url. Let's take a look at the source code...

protected void Page_Load(object sender, System.EventArgs e)
{
 //Response.Write(umbraco.helper.Request("id"));
 //Response.End();
 // Put user code to initialize the page here
 if (Request.QueryString["id"] != null) 
 {
  if (Request.QueryString["id"] != "")  
  {
   //TODO: fix Nasty FAST'N'CANELINE HACK. ..
   int MediaId = int.Parse(Request.QueryString["id"]);
   
   image.Controls.Clear();
   int fileWidth = 0;
   int fileHeight = 0;
   string fileName = "/blank.gif";
   string altText = "";

   try 
   {
    cms.businesslogic.media.Media m = new cms.businesslogic.media.Media(MediaId);

    // TODO: Remove "Magic strings" from code.
    try 
    {
     fileName = m.getProperty("fileName").Value.ToString();
    } 
    catch 
    {
     try 
     {
      fileName = m.getProperty("umbracoFile").Value.ToString();
     } 
     catch 
     {
      fileName = m.getProperty("file").Value.ToString();
     }
    }

    altText = m.Text;
    try 
     {
     fileWidth = int.Parse(m.getProperty("umbracoWidth").Value.ToString());
     fileHeight = int.Parse(m.getProperty("umbracoHeight").Value.ToString());
     }
    catch {
    
    }
    string fileNameOrg = fileName;
    string ext = fileNameOrg.Substring(fileNameOrg.LastIndexOf(".")+1, fileNameOrg.Length-fileNameOrg.LastIndexOf(".")-1);
    string fileNameThumb = GlobalSettings.Path + "/.." + fileNameOrg.Replace("."+ext, "_thumb.jpg");
    image.Controls.Add(new LiteralControl("<a href=\"" + GlobalSettings.Path + "/.." + fileNameOrg + "\" title=\"Zoom\"><img src=\"" + fileNameThumb + "\" border=\"0\"/></a>"));
   } 
   catch {
   }
   image.Controls.Add(new LiteralControl("<script>\nparent.updateImageSource('" + GlobalSettings.Path + "/.." + fileName.Replace("'", "\\'") + "','"+altText+"','" + fileWidth.ToString() + "','" + fileHeight.ToString() + "')\n</script>"));
  }
 }
}



Let's ignore the shamefullness of the code. We've all had our bad moments, and open source projects are bound to have some things like this. Lets comment on what the code does.

Notice this line:
string fileName = "/blank.gif";
Now notice that the bottom catch is empty. This catch is hit whenever a file does not have one of these attibutes:fileName, umbracoFile, file. So filename is never set to anything.

The solution
The last catch needs to get your custom media type. In our case:
Media m = new Media(MediaId);
fileName = m.getProperty("Image").Value.ToString();

That is the fix for Umbraco. So you have a couple options on how to implement it.

1. You can recompile Umbraco's source then redeploy.

2. If you don't want to recompile Umbraco you can can create a new file called ImageViewer.cs that inherits from umbraco.dialogs.imageViewer. Copy and paste the code, do your fix, then modify the ~/umbraco/dialogs/imageViewer.aspx file to inherit from your new file.

I.e. Change the header:
<%@ Page Language="c#" CodeBehind="imageViewer.aspx.cs" AutoEventWireup="True" Inherits="umbraco.dialogs.imageViewer" %>

To this:
<%@ Page Language="c#" CodeBehind="MyImageViewer.aspx.cs" AutoEventWireup="True" Inherits="My.Project.UmbracoExtensions.MyImageViewer" %>

The downside of both methods is that you will overwrite your changes if you upgrade Umbraco.


The awesome solution
Here is a solution that is awesomely hilarious. Using the already installed Urlrewriting.Net module that comes with Umbraco.

1. Create a new file called ImageViewer.aspx in your project somewhere. Mine was in ~/UmbracoExtensions/

Make the code behind inherit from umbraco.dialogs.imageViewer. Copy and paste the code, do your fix.

2. Open ~/config/UrlRewriting.config and add this entry:

<add name="ImageViewer"
          virtualUrl="~/umbraco/dialogs/imageViewer.aspx(.*)"
          rewriteUrlParameter="IncludeQueryStringForRewrite"
          redirectMode="Permanent"
          destinationUrl="~/UmbracoExtensions/ImageViewer.aspx$1"
          redirect="Application"
          ignoreCase="true" />

This redirects ~/umbraco/dialogs/imageViewer.aspx to your new ~/UmbracoExtensions/ImageViewer.aspx and passes the query string which allows the lookup of the file.

3. Open your ~/web.config and add /UmbracoExtensions/ to your umbracoReservedPaths under appSettings. This allows your newly created file to be redirected to.

That's it!

I find this solution absolutely hilarious! But it means I will not get undone by an upgrade. Hopefully they will eventually fix the imageViewer class to look for other files.

UPDATE
Ok after all that, I found out that you just need to change the alias name in the cmsPropertyType in the database.

First check if your file alias is being used more than once. In our case we had an Image file type, and a field in a document type called Image. So we have to modify the correct one.

Here is how to check your table:
SELECT * FROM [YOUR_DATABASE].[dbo].[cmsPropertyType]
  WHERE Alias LIKE 'Image'

To figure out which was which I did this really lazily. I had a synced db already on dev, so I just changed the alias of one to see if it would change in Umbraco. Note that you must bump the config for the alias to change in Umbraco.

Here is the sql to update your file type alias:
UPDATE [YOUR_DATABASE].[dbo].[cmsPropertyType]
  SET Alias = 'umbracoFile'
  WHERE Alias in ('YOUR_FILE_TYLE_ALIAS') // for multiple file types
  AND id !=  // line is optional


So it turns out there was no coding involved after all. Well at least I learned heaps about how Umbraco was structured :)



No comments:

Post a Comment