Tuesday, 16 August 2011

Persisting the Page Title across Update Panel Post backs

There’s a fun bug with AsyncPostbacks blanking out the Page Title (http://stackoverflow.com/questions/627378/page-losing-title-after-updatepanel-asyncpostback), the recommended fix is to rebind the page title on each Async Postback or to use the declarative page title attribute in the aspx.  My fix is to fix a JS bug in JS:

   1: $(function(){
   2:     var prm = Sys.WebForms.PageRequestManager.getInstance();
   3:     if (!(prm)) return;
   4:     document.orginalTitle=document.title;
   5:     instance.add_endRequest(function(s, e){
   6:         if (document.title.replace(/\s/g,"").length==0)
   7:             document.title=document.orginalTitle;
   8:     });
   9: });

This JS code will ensure that the page title is persisted across ajax requests, with no changes to the server side code.   If you don’t have JQuery available (for shame!) then just move the function body into a code block which will execute on the GET request/first view of the page.

Thursday, 4 August 2011

Ektron: Treating Office Files as Normal DMS Assets

One of the features of Ektron is tight office integration allowing the editing of office files directly from the workarea.  However, in some circumstances you may want to treat these as normal DMS assets (like PDF files) as their the end results of an offline publishing process.

The Ektron support fix for this is to modify the workarea files (detailed below), which would then need to be re-applyed with every upgrade.  This is easy enough to do (if you remember to do it with each upgrade), but I would prefer a more unobtrusive fix which is applied via configuration.

The Manual Fix (Ektron Supported)

Edit /workarea/edit.aspx and comment out the following lines (~line 2110):

   1: var isOffice = document.getElementById("isOfficeDoc");
   2:  
   3: if ((isOffice != null) && (isOffice.value == "true") && (ShowMultipleUpload() || !IsBrowserIE()))
   4: {
   5:     g_initialPaneToShow = 'dvSummary';
   6:     var contentTabHeader = document.getElementById("dvContent");
   7:     var contentTabContent = document.getElementById("_dvContent");
   8:     if (contentTabHeader != null)
   9:        contentTabHeader.style.display="none";
  10:     if (contentTabContent != null)
  11:        contentTabContent.style.display="none";
  12: }

Also, comment out the following lines of /workarea/edit.aspx.vb  (~line 1945)

   1: If (isOfficeDoc.Value = "true") Then
   2:     phContent.Visible = False
   3:     phEditContent.Visible = False
   4: End If

The workarea will now treat office files in the same manner as PDF DMS Assets.

The Unobtrusive Fix (Unsupported)

Looking at what the official fix achieves does, it all hangs on the value of the “isOffice” hidden field being ‘true’.  Realising this it’s an (almost) trivial task to create an HttpModule to manipulate the control tree of the edit page to get the same effect.  The major advantage of this is that it won’t require modification of Ektron files and can be disabled by simply unregistering the module within the web.config.

   1: using System;
   2: using System.Reflection;
   3: using System.Web;
   4: using System.Web.UI;
   5: using System.Web.UI.HtmlControls;
   6:  
   7: namespace MartinOnDotNet.Support
   8: {
   9:     /// <summary>
  10:     /// Force OfficeDocuments to be treated as normal DMS Assets
  11:     /// </summary>
  12:     public class OfficeDocumentFixModule : IHttpModule
  13:     {
  14:  
  15:         /// <summary>
  16:         /// Initializes a module and prepares it to handle requests.
  17:         /// </summary>
  18:         /// <param name="context">An <see cref="T:System.Web.HttpApplication"/> that provides access to the methods, properties, and events common to all application objects within an ASP.NET application</param>
  19:         public void Init(HttpApplication context)
  20:         {
  21:             if (context == null) throw new ArgumentNullException("context");
  22:             context.PreRequestHandlerExecute += new EventHandler(OnPreRequestHandlerExecute);
  23:         }
  24:  
  25:         private void OnPreRequestHandlerExecute(object sender, EventArgs e)
  26:         {
  27:             HttpContext current = HttpContext.Current;
  28:             if (current == null
  29:                 || current.Handler == null
  30:                 || current.Request == null
  31:                 || !current.Request.Url.AbsolutePath.EndsWith("/workarea/edit.aspx", StringComparison.OrdinalIgnoreCase)) return;
  32:  
  33:             Page page = current.Handler as Page;
  34:             page.PreInit += new EventHandler(OnPreInit);
  35:  
  36:         }
  37:  
  38:         /// <summary>
  39:         /// Always return false regardless of actual value
  40:         /// </summary>
  41:         public class AlwaysFalseHiddenField : System.Web.UI.HtmlControls.HtmlInputHidden
  42:         {
  43:             /// <summary>
  44:             /// Gets or sets the value associated with the <see cref="T:System.Web.UI.HtmlControls.HtmlInputControl"/> control.
  45:             /// </summary>
  46:             /// <value></value>
  47:             /// <returns>
  48:             /// The value associated with the <see cref="T:System.Web.UI.HtmlControls.HtmlInputControl"/>.
  49:             /// </returns>
  50:             public override string Value
  51:             {
  52:                 get
  53:                 {
  54:                     return "false";
  55:                 }
  56:                 set
  57:                 {
  58:                     base.Value = value;
  59:                 }
  60:             }
  61:         }
  62:  
  63:  
  64:  
  65:         /// <summary>
  66:         /// Called when the Page load event fires
  67:         /// </summary>
  68:         /// <param name="sender">The sender.</param>
  69:         /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
  70:         private void OnPreInit(object sender, EventArgs e)
  71:         {
  72:             Page page = sender as Page;
  73:             if (page == null) return;
  74:             PropertyInfo officeDocProperty = page.GetType().GetProperty("isOfficeDoc", BindingFlags.Instance | BindingFlags.NonPublic);
  75:             if (officeDocProperty == null) return;
  76:  
  77:             HtmlInputHidden hil = officeDocProperty.GetValue(page, null) as HtmlInputHidden;
  78:             AlwaysFalseHiddenField fhil = new AlwaysFalseHiddenField();
  79:             fhil.ID = hil.ID;
  80:             fhil.Name = hil.Name;
  81:             fhil.Value="false";
  82:             page.PreRenderComplete += (s, ea) =>
  83:                 {
  84:                     hil.Value = "false";
  85:                 };
  86:             officeDocProperty.SetValue(page, fhil, null);
  87:  
  88:         }
  89:  
  90:  
  91:         /// <summary>
  92:         /// Disposes of the resources (other than memory) used by the module that implements <see cref="T:System.Web.IHttpModule"/>.
  93:         /// </summary>
  94:         public void Dispose()
  95:         {
  96:             //throw new NotImplementedException();
  97:         }
  98:  
  99:  
 100:     }
 101: }

Simply wire up the module in web.config and job’s a good ‘un.