3. Deleting Attachments
To delete attachments, we will write a
client-side function that clears the contents of the textboxes, table
cells, and hidden form field for the selected attachment. Add the RenderDeleteAttachmentJavascript() helper function to the code.
private void RenderDeleteAttachmentJavascript()
{
StringBuilder script = new StringBuilder();
// Get the name of the placeholder
string sPhName = this.BoundPlaceholder.Name;
script.Append("<SCRIPT>");
script.Append("function DeleteAttachment(i)");
script.Append("{" + crlf);
script.Append("eval('document.all.MAPH_AttIcon_"+sPhName+"_'
+ i).innerHTML='';");
script.Append("eval('document.all.MAPH_AttIconUrl_" + sPhName+"_'
+ i).value='';");
script.Append("eval('document.all.MAPH_AttName_" + sPhName + "_'
+ i).value='';");
script.Append("eval('document.all.MAPH_AttLink_" + sPhName + "_'
+ i).value='';");
script.Append("}" + crlf);
script.Append("</SCRIPT>");
Page.RegisterClientScriptBlock("MAPH_DeleteAttachment", script.ToString());
}
At the end of the LoadPlaceholderContentForAuthoring() method, call the RenderDeleteAttachmentJavascript() method.
protected override void
LoadPlaceholderContentForAuthoring(PlaceholderControlEventArgs e)
{
. . . code continues . . .
RenderDeleteAttachmentJavascript();
}
4. Reusing the Insert Attachment Dialog
We need a dialog for authors to specify the file
to attach and its display text. While it is possible to write one from
scratch, a better idea would be to reuse the Insert Attachment dialog
that ships with Web Author.
The Insert Attachment dialog provides the author
with the option of either attaching a shared resource or a local
attachment. Depending on the whether the developer has allowed local
attachments, the author may be restricted to only selecting attachments
from resource galleries. In such cases, the Insert Local Attachment
link will not be available. After the author has made the selection, he
or she is provided with a field for entering the display text of the
attachment.
Calling the Dialog
To call the Insert Attachment dialog, we borrow the WBC_launchAttachmentGallery() method found in the AuthFormClientIE.js used by Web Author. The method accepts six arguments that affect the dialog’s behavior.
Argument | What it does |
---|
strPostQueryString | A
querystring containing information about the posting in unpublished
mode such as its GUID or, if it’s a new posting, the channel’s GUID. |
strPhName | The name of the placeholder. |
strPhType | The
value here determines how the Insert Attachment dialog passes
information about the attachment back to the calling control. When
dealing with attachments, Web Author recognizes the following values:
MultiPurpose. Applies to controls that accept HTML with full formatting. SingleAttachment. Used by the SingleAttachmentPlaceholderControl for single file uploads. ThinEditIE. Similar to MultiPurpose.
The difference is that insertions to the placeholder use the
placeholder document object model (DOM) instead. We will use this option
in this example.
|
bAllowUpload | A Boolean to indicate if local attachments are allowed. A value of false means that only resource gallery items can be attached. |
bAttachIcon | A Boolean flag to indicate if the attachment should be rendered as an icon. When it is set to true, the attachment is added to the content as an icon. Otherwise, it will be displayed as a text link. |
bAllowVideo | The
last Boolean indicates if video attachments are supported. This is a
throwback to the previous version of MCMS where videos were supported.
In MCMS 2002, this value should always be set to false. |
Putting it all together, here’s the script that
launches the Insert Attachment dialog. Of course, the GUIDs and the
placeholder name are variables that will have to be dynamically
generated. The following code snippet would allow local attachments and
use icons to represent attachments:
WBC_launchAttachmentGallery('wbc_purpose=Basic&NRMODE=Unpublished&WBCMODE=Pres
entationUnpublished&FRAMELESS=true&NRCHANNELGUID={C40686DA-83CE-452F-AF24-
3D990FA2BAC1}&NRNODEGUID={7AF93078-5FF8-4963-A9F9-B1A0977258A7}',
'NewHtmlPlaceholderDefinition1', 'ThinEditIE', true, true, false);
The GetAttachmentDialog() method generates the required client-side script. The script gathers information about:
The channel’s and posting’s GUIDs.
The name of the placeholder the control is bound to.
Whether or not local attachments are allowed.
Whether the attachments should be displayed as icons or text. For this control, we will always set this parameter to true as we want icons to be displayed in the authoring table.
Once it has collected the necessary data, it puts them together as arguments of the WBC_launchAttachmentGallery() function. Add the GetAttachmentDialog() method now:
private string GetAttachmentDialog(int i)
{
// Check to see if the NRCHANNELGUID querystring contains a value
// If it does, then it's most likely a new posting
string channelGUID = Page.Request.QueryString["NRCHANNELGUID"];
if (channelGUID != "")
{
channelGUID = "&NRCHANNELGUID="+channelGUID;
}
// Get the GUID from the NRNODEGUID querystring parameter
string postingGUID = Page.Request.QueryString["NRNODEGUID"];
// Get the name of the placeholder to bind
string placeholderName = this.PlaceholderToBind;
// Are local attachments allowed?
string allowLocalAttachments = (!m_resourceGalleryOnly).ToString();
StringBuilder script = new StringBuilder();
// Specify the name of the placeholder that we are adding the attachment to
script.Append("MAPH_DestPH ='" + placeholderName + "_" + i.ToString()
+ "';");
// Launch the Insert Attachment Dialog
script.Append("WBC_launchAttachmentGallery('"
+ "wbc_purpose=Basic&"
+ "NRMODE=Unpublished&"
+ "WBCMODE=PresentationUnpublished&"
+ "FRAMELESS=true" + channelGUID + "&"
+ "NRNODEGUID=" + postingGUID + "',"
+ "'" + placeholderName + "',"
+ "'ThinEditIE',"
+ allowLocalAttachments.ToLower() + ","
+ "true,"
+ "false);");
script.Append("return false");
return script.ToString();
}
Returning Values from the Dialog
The Insert Attachment dialog allows the author
to specify the attachment and its display text. It’s not a modal dialog.
Therefore, in order for content to be written back to the placeholder
control, the dialog accesses client-side functions within the calling
window.
Depending on the chosen placeholder type, Web
Author prepares different client-side routines. The “ThinEditIE”
placeholder type used in this example calls the WBC_setThinEditIEAttachment() method found within the AuthFormClientIE.js script (located in the <install directory>\ Microsoft Content Management Server\Server\IIS_CMS\WebAuthor\Client\ PlaceholderControlSupport\ directory).
We need to modify the WBC_setThinEditIEAttachment()
routine to update the authoring table with the attachment’s icon,
display text, and URL. The tricky part is doing so without modifying the
original code (or we will cross Microsoft support boundaries, plus not
modifying the original file safeguards the code from being overwritten
when future service packs are applied). As a workaround, we will create a
modified version of the method and add it to the page. When the browser
sees two JavaScript methods with the same name, one defined in within
the page and a second in a separate *.js file, the browser will choose to run the one embedded within the page.
To begin, let’s take a look at the original WBC_setThinEditIEAttachment() routine shown below. It accepts three input parameters:
The name of the placeholder that’s accepting the attachment
The URL of the uploaded attachment
The display name of the attachment
It uses the URL and display name of the attached
file to construct an HTML anchor tag. The anchor tag is added to the
placeholder by calling the insertHtml() method of the
placeholder’s DOM. Don’t worry about typing in the code for now. We will
be writing a server-side function to generate this script on the fly
later.
function WBC_setThinEditIEAttachment(strPhName, strURL, strDispText)
{
var strMyDispText = strDispText;
if (strMyDispText == "")
{
strMyDispText = strURL;
}
document.all["NCPHRICH_" + strPhName].insertHtml("<a href=\"" + strURL
+ "\">" + strMyDispText + "</a>");
}
While the WBC_setThinEditIEAttachment() method works perfectly for regular HtmlPlaceholderControls, in order for it to function correctly within the MutipleAttachmentPlaceholderControl, we need to make several modifications:
The method calls an object with an ID of NCPHRICH_(PlaceholderName): our custom control has a different naming convention and executing this line of code will result in an error.
Instead of inserting an entire anchor tag in an HtmlPlaceholderControl,
we need to pass information about the attachment back to the
appropriate hidden fields and cells within the table in the multiple
attachment placeholder control.
Client-Side Script for All Types of Placeholder Controls
Let’s tackle the first problem. By default, the WBC_setThinEditIEAttachment()
method works only with the out-of-the-box placeholder controls.
Placeholder controls that ship with MCMS have IDs prepended with the
text NCPHRICH to differentiate them from custom controls that we build. Our control does not have the ID of NCPHRICH_(PlaceholderName). As a result, the following line of code will lead to ’document.all[...]’ is null or not an object errors when the dialog is used with custom controls.
document.all["NCPHRICH_" + strPhName].insertHtml("<a href=\"" + strURL
+ "\">" + strMyDispText + "</a>");
On the other hand, we can’t remove the line of
code entirely as there may be some out-of-thebox placeholder controls on
the same page that require it. To get around this, we will wrap the
line in a check to see if the object is undefined (which will be the
case for the multiple attachment placeholder
control). Again, don’t worry about adding the code to the class file for
now, we will show you how the JavaScript gets injected later.
function WBC_setThinEditIEAttachment(strPhName, strURL, strDispText)
{
var strMyDispText = strDispText;
if (strMyDispText == "")
{
strMyDispText = strURL;
}
if(typeof document.all["NCPHRICH_" + strPhName] != 'undefined')
{
document.all["NCPHRICH_" + strPhName].insertHtml("<a href=\"" + strURL
+ "\">" + strMyDispText + "</a>");
}
}
Passing Attachment Information to the Placeholder Control
Now, on to the second problem of passing
information about the attachment back to the multiple attachment
placeholder control. We will populate the hidden fields and textboxes
with information about the attachment’s icon, display text and URL.
To get this information, let’s look at two input parameters passed into the WBC_setThinEditIEAttachment() method:
Input Parameter | What it means |
---|
StrUrl | As the name suggests, this parameter contains the URL of the attachment. |
strMyDispText | Stores the icon. The returned HTML is an image tag, which looks like this:
<img alt="Plants are Green!"
src="/NR/rdonlyres/670B7551-5A53-4B0A-
99782B7AD3EE7BC8/0/MyAttachment.doc?
thumbnail=true&label=Plants%20are%20Green%21"
border="0">
|
So strUrl contains the URL of the attachment and strMyDispText holds the icon as shown in the script below:
function WBC_setThinEditIEAttachment(strPhName, strURL, strDispText)
{
var strMyDispText = strDispText;
if (strMyDispText == "")
{
strMyDispText = strURL;
}
if(typeof document.all["NCPHRICH_" + strPhName] != 'undefined')
{
document.all["NCPHRICH_" + strPhName].insertHtml("<a href=\"" + strURL
+ "\">" + strMyDispText + "</a>");
}
else
{
// Return the image of the icon
if (typeof document.all["MAPH_AttIcon_" + MAPH_DestPH] !='undefined')
{
document.all[\"MAPH_AttIcon_\" + MAPH_DestPH].innerHTML = strMyDispText;
}
// Return the attachment's URL
if (typeof document.all[\"MAPH_AttLink_\" + MAPH_DestPH] != 'undefined')
{
document.all[\"MAPH_AttLink_\" + MAPH_DestPH].value = strURL;
}
// Return the icon
if (typeof document.all[\"MAPH_AttIconUrl_\" + MAPH_DestPH]
!= 'undefined')
{
document.all[\"MAPH_AttIconUrl_\" + MAPH_DestPH].value = strMyDispText;
}
}
}
What about the display text? We need to extract the display text from the alt attribute of the icon’s image tag. To do so, we will split strMyDispText using double-quotes (”) as the separator. Let’s say the original HTML is:
<img alt="DisplayText" src="AttachmentUrl" border="0">
The array formed after splitting is shown below
where the second element of the resulting array contains the display
text from the alt attribute:
Array Item 0: <img alt=
Array Item 1: DisplayText
Array Item 2: src=
Array Item 3: AttachmentUrl
Array Item 4: border=
Array Item 5: 0
Array Item 6: >
The extracted values are deposited into the hidden form fields and textboxes generated earlier.
function WBC_setThinEditIEAttachment(strPhName, strURL, strDispText)
{
. . . code continues . . .
if(typeof document.all["NCPHRICH_" + strPhName] != 'undefined')
{
. . . code continues . . .
}
else
{
. . . code continues . . .
// Split strMyDispText using the double-quote character as the separator
elements = strMyDispText.split('\"');
// Return the Display Text
if (typeof document.all[\"MAPH_AttName_\" + MAPH_DestPH] != 'undefined')
{
// The display name is the second element of the array
document.all[\"MAPH_AttName_\" + MAPH_DestPH].value = elements[1];
}
}
}
Generating the WBC_setThinEditIEAttachment() Method
As our custom placeholder control is
constructed entirely from server-side code, we need to generate the
client script above on the fly and register it with Page.RegisterStartUpScript(). To do so, we must add the helper function RenderResourceGalleryRelatedJavascript() shown below to the code:
private void RenderResourceGalleryRelatedJavascript()
{
StringBuilder script = new StringBuilder();
// Start the script block
script.Append("<SCRIPT language=\"javascript\">" + crlf);
// Declare the MAPH_DestPH variable
script.Append("var MAPH_DestPH = \"\";" + crlf);
// Define WBC_setThinEditIEImage function to replace one defined by MCMS
script.Append("function WBC_setThinEditIEAttachment(strPhName, strURL,"
+ "strDispText){" + crlf);
script.Append("var strMyDispText = strDispText;" + crlf
+ "if (strMyDispText == \"\") "
+ "{"
+ "strMyDispText = strURL;"
+ "}" + crlf);
script.Append("if(typeof document.all[\"NCPHRICH_\" + strPhName] !=
'undefined')"
+ "{"
+ "document.all[\"NCPHRICH_\" + strPhName].insertHtml"
+ "(\"<a href=\\\" + strURL + \\\">\" + strMyDispText
+ \"</a>\");"
+ "}" + crlf);
script.Append("else"
+ "{"
+ "elements = strMyDispText.split('\"'); " + crlf);
script.Append("if(typeof document.all[\"MAPH_AttIcon_\"
+ MAPH_DestPH]!='undefined')"
+ "{"
+ "document.all[\"MAPH_AttIcon_\"
+ MAPH_DestPH].innerHTML=strMyDispText;"
+ "}" + crlf);
script.Append("if(typeof document.all[\"MAPH_AttLink_\"
+ MAPH_DestPH]!='undefined')"
+ "{"
+ "document.all[\"MAPH_AttLink_\" + MAPH_DestPH].value = strURL; "
+ "}" + crlf);
script.Append("if(typeof "
+ "document.all[\"MAPH_AttIconUrl_\"+MAPH_DestPH]!='undefined')"
+ "{"
+ "document.all[\"MAPH_AttIconUrl_\"
+ MAPH_DestPH].value =strMyDispText;"
+ "}" + crlf);
script.Append("if (typeof "
+ "document.all[\"MAPH_AttName_\"+MAPH_DestPH]!='undefined')"
+ "{"
+ "document.all[\"MAPH_AttName_\"
+ MAPH_DestPH].value = elements[1];"
+ "}" + crlf);
// Close the if-else block
script.Append("}" + crlf);
// Close the function
script.Append("}" + crlf);
// Close the script block
script.Append("</SCRIPT>");
// Register the script
this.Page.RegisterStartupScript("WBC_setThinEditIEAttachment",
script.ToString());
}
Finally, at the end of the LoadPlaceholderContentForAuthoring() method, we add a call to the RenderResourceGalleryRelatedJavascript() method:
protected override void LoadPlaceholderContentForAuthoring(
PlaceholderControlEventArgs e)
{
. . . code continues . . .
RenderResourceGalleryRelatedJavascript();
}