Hi all,
I'm working on a small project and I had the need of having the MediaPicker show the image chosen in a preview. I solved this in a rather peculiar way and I'd like to know if there's a better solution to this. If not I've presented a solution here so others may benefit.
How I solved it:
1. Creating a new Data Type based off the MediaPicker using a usercontrol and UserControlWrapperCode:
public partial class ExtendedMediaPicker : System.Web.UI.UserControl, umbraco.editorControls.userControlGrapper.IUsercontrolDataEditor
{
int _id;
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
base.Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "ajax", "<script type=\"text/javascript\" src=\"" + GlobalSettings.Path + "/webservices/ajax.js\"></script>");
base.Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "ajax1", "<script type=\"text/javascript\" src=\"" + GlobalSettings.Path + "/js/xmlextras.js\"></script><script type=\"text/javascript\" src=\"" + GlobalSettings.Path + "/js/xmlRequest.js\"></script>");
base.Page.ClientScript.RegisterClientScriptBlock(GetType(), "subModal", "<script type=\"text/javascript\" src=\"" + GlobalSettings.Path + "/js/submodal/common.js\"></script><script type=\"text/javascript\" src=\"" + GlobalSettings.Path + "/js/submodal/subModal.js\"></script><link href=\"" + GlobalSettings.Path + "/js/submodal/subModal.css\" type=\"text/css\" rel=\"stylesheet\"></link>");
base.Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "webserviceExtendedCMSNode", "<script type=\"text/javascript\" src=\"" + GlobalSettings.Path + "/webservices/GetJavaScriptProxy.aspx?service=ExtendedCMSNode.asmx\"></script>");
}
protected override void Render(HtmlTextWriter writer)
{
if (_id > 0)
{
PreviewImage.Visible = true;
PreviewImage.ImageUrl = "/umbraco/ImageGen.aspx?Image=" + umbraco.library.GetMedia(_id, false).Current.SelectSingleNode("//data[@alias='umbracoFile']").Value + "&Width=100&Height=100&Constrain=true";
}
else
{
PreviewImage.Visible = false;
}
string tempTitle = "";
string deleteLink = " <a href=\"javascript:" + this.ClientID + "_clear();\" style=\"color: red\">" + ui.Text("delete") + "</a> ";
try
{
if (_id > 0)
{
tempTitle = new umbraco.cms.businesslogic.CMSNode(_id).Text;
}
}
catch { }
StringBuilder sb = new StringBuilder();
sb.Append("<script language=\"javascript\">\nfunction ");
sb.Append(this.ClientID);
sb.Append("_chooseId() {");
sb.Append("\nshowPopWin('");
sb.Append(GlobalSettings.Path);
sb.Append("/dialogs/treePicker.aspx?useSubModal=true&appAlias=media', 350, 300, ");
sb.Append(this.ClientID);
sb.Append("_saveId)");
sb.Append("\n}");
sb.Append("\nfunction ");
sb.Append(this.ClientID);
sb.Append("_saveId(treePicker) {");
sb.Append("\nsetTimeout('");
sb.Append(this.ClientID);
sb.Append("_saveIdDo(' + treePicker + ')', 200);");
sb.Append("\n}");
sb.Append("\nfunction ");
sb.Append(this.ClientID);
sb.Append("_saveIdDo(treePicker) {");
sb.Append("\nif (treePicker != undefined) {");
sb.Append("\ndocument.getElementById(\"");
sb.Append(ValueHiddenField.ClientID);
sb.Append("\").value = treePicker;");
sb.Append("\nif (treePicker > 0) {");
sb.Append("\nproxies.ExtendedCMSNode.GetNodeName.func = ");
sb.Append(this.ClientID);
sb.Append("_updateContentTitle;");
sb.Append("\nproxies.ExtendedCMSNode.GetNodeName('");
sb.Append(umbraco.BasePages.BasePage.umbracoUserContextID);
sb.Append("', treePicker);");
sb.Append("\n}");
sb.Append("\n}");
sb.Append("\n}");
sb.Append("\nfunction ");
sb.Append(this.ClientID);
sb.Append("_updateContentTitle(retVal) {");
sb.Append("\ndocument.getElementById(\"");
sb.Append(this.ClientID);
sb.Append("_title\").innerHTML = \"<strong>\" + retVal + \"</strong>");
sb.Append(deleteLink.Replace("\"", "\\\""));
sb.Append("\";");
sb.Append("\nproxies.ExtendedCMSNode.GetNodeUmbracoFile.func = ");
sb.Append(PreviewImage.ClientID);
sb.Append("_updateContentPreview;");
sb.Append("\nproxies.ExtendedCMSNode.GetNodeUmbracoFile('");
sb.Append(umbraco.BasePages.BasePage.umbracoUserContextID);
sb.Append("', document.getElementById(\"");
sb.Append(ValueHiddenField.ClientID);
sb.Append("\").value);");
sb.Append("\n}");
sb.Append("\nfunction ");
sb.Append(PreviewImage.ClientID);
sb.Append("_updateContentPreview(retVal) {");
sb.Append("\ndocument.getElementById(\"");
sb.Append(PreviewImage.ClientID);
sb.Append("\").src = \"ImageGen.aspx?Image=\" + retVal + \"&Width=100&Height=100&Constrain=true\";");
sb.Append("\ndocument.getElementById(\"");
sb.Append(PreviewImage.ClientID);
sb.Append("\").style.display = \"block\";");
sb.Append("\n}");
sb.Append("\nfunction ");
sb.Append(this.ClientID);
sb.Append("_clear() {");
sb.Append("\ndocument.getElementById(\"");
sb.Append(this.ClientID);
sb.Append("_title\").innerHTML = \"\";");
sb.Append("\ndocument.getElementById(\"");
sb.Append(ValueHiddenField.ClientID);
sb.Append("\").value = \"\";");
sb.Append("\ndocument.getElementById(\"");
sb.Append(PreviewImage.ClientID);
sb.Append("\").style.display = \"none\";");
sb.Append("\n}");
sb.Append("\n</script>");
writer.WriteLine(sb.ToString());
if (_id <= 0)
deleteLink = "";
writer.WriteLine("<span id=\"" + this.ClientID + "_title\"><b>" + tempTitle + "</b>" + deleteLink + "</span><a href=\"javascript:" + this.ClientID + "_chooseId()\">" + ui.Text("choose") + "...</a> <br />");
base.Render(writer);
}
protected void Page_Load(object sender, EventArgs e)
{
}
#region IUsercontrolDataEditor Members
public object value
{
get
{
int parsed;
if (int.TryParse(ValueHiddenField.Value, out parsed))
{
_id = parsed;
return parsed;
}
return -1;
}
set
{
int parsed;
if (int.TryParse(value.ToString(), out parsed))
{
_id = parsed;
}
else
{
_id = -1;
}
}
}
#endregion
}
As you can see it's mainly a copy of the original MediaPicker in regards to how it's build with exceptions of the stringbuilder (Should increase performance somewhat since using "+" to put strings together isn't best practice) and the fact that the hiddenfield for the value now isn't created inside the code anymore but is an ASP.NET hiddenfield on the UserControl itself to make it more easily adressable.
Code:
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="ExtendedMediaPicker.ascx.cs" Inherits="Umbraco_Usercontrols.ExtendedMediaPicker" %>
<%@ Register Assembly="System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
Namespace="System.Web.UI" TagPrefix="asp" %>
<br /><asp:Image ID="PreviewImage" runat="server" />
<asp:HiddenField ID="ValueHiddenField" runat="server" />
The usercontrol itself is very simple as most of the important stuff is done in the code behind. I placed it here to make things a bit more clear and to those that may wonder how it looks.
With part 1 only you already have sort of a preview but the image only updates when saving. Taking into account the name changes and the image doesn't may sometimes be confusing and also most would like to see both change to reflect what you're doing. That's where part 2 comes in.
2. Extending the Webservices to get what I needAnd this is mainly where I was wondering: Isn't there a better way to get this done? It seemed way too farfetched to have to go as far as to extend the webservices of Umbraco to get the data I needed. First off I tried to make a more generic method to get the XML of the node by using the ToXml() on the CMSNode which would mean I could further parse it to get what I needed but to my surprise that only gave me the <node> and not it's <data> childs. Having no better idea's as my knowledge of Umbraco isn't as extensive yet I created a specific method to fetch the "umbracoFile" which you can see below:
Code:
/// <summary>
/// An extension on the original CMSNode webservice
/// </summary>
[WebService(Namespace = "http://umbraco.org/webservices/")]
[ScriptService]
public class ExtendedCMSNode : System.Web.Services.WebService
{
[WebMethod]
public string GetNodeName(string ContextID, int NodeId)
{
if (umbraco.BasePages.BasePage.ValidateUserContextID(ContextID))
return getNodeName(NodeId);
return "";
}
private string getNodeName(int NodeId)
{
umbraco.cms.businesslogic.CMSNode n = new umbraco.cms.businesslogic.CMSNode(NodeId);
return n.Text;
}
[WebMethod]
public string GetNodeUmbracoFile(string ContextID, int NodeId)
{
if (umbraco.BasePages.BasePage.ValidateUserContextID(ContextID))
return getNodeUmbracoFile(NodeId);
return "";
}
private string getNodeUmbracoFile(int NodeId)
{
umbraco.cms.businesslogic.CMSNode n = new umbraco.cms.businesslogic.CMSNode(NodeId);
return umbraco.library.GetMedia(n.Id,false).Current.SelectSingleNode("//data[@alias = 'umbracoFile']").Value;
}
}
And those 2 together form my ExtendedMediaPicker which does what I want it to do: Display the chosen image. The question is: Anyone knows a way to make this work in a more easy way without having to extend the webservices? Other comments are ofcourse welcome as well.
Thanks,
Ivan