So amongst other things (like DevDays, computer crashes, crazy clients and the SharePoint and MOSS How Do I videos on Tech Net), I’ve been working on another side project for a couple of months, and I’ve finally launched it. For all those working with, or interested in SharePoint or Microsoft Office SharePoint Server (MOSS), check out my all-new, all-shiny, SharePoint podcast - The MOSS Show.
There’s still a lot of work to be done, both on improving the podcast, the site, the editing, and so forth, but I’m excited to finally have it launched (it’s been incubating for a looong time). If you’ve got any suggestions, please send them in. Also, if you’ve got topic or speaker requests/suggestions or ideas for the show in general, please also let me know.
- H
I've been working with creating a custom list Feature in a MOSS project recently, and today I hit an error when creating a custom edit page for a content type. I'm trying to override the default new, edit and display pages for a specific content type, and all has gone fine till now, but today I noticed that the Attachments button was not working, and that it was giving the following error - "This form was customized not working with attachement." One of the most interesting solutions I found via Google was to this hotfix, but it is unsupported and also seemed to focus more on SharePoint Designer, so I carried on my search...
Eventually, I stumbled upon a blog post where the author had gone searching through the form.js JavaScript file to locate the error, and it helped me resolve the issue finally with just a touch of hacking around in SharePoint :->.
The form.js file contains the following:
function ShowPartAttachment()
{
if (document.getElementById("part1")==null ||
typeof(document.getElementById("part1"))=="undefined")
{
var L_FormMissingPart1_Text= "This form was customized not working with attachement.";
alert(L_FormMissingPart1_Text);
return;
...
so, what the above reveals is that the JavaScript was expecting an element called "part1" somewhere on the page. This lead me to go searching in the DefaultTemplates.ascx file (in C:\Program Files\Common Files\microsoft shared\Web Server Extensions\12\TEMPLATE\CONTROLTEMPLATES), where I found the following:
<SharePoint:RenderingTemplate ID="UserListForm" runat="server">
<Template>
<SPAN id='part1'>
<wssuc:ToolBar CssClass="ms-formtoolbar" id="toolBarTbltop" RightButtonSeparator=" " runat="server">
<Template_RightButtons>
<SharePoint:SaveButton runat="server"/>
<SharePoint:GoBackButton runat="server"/>
</Template_RightButtons>
</wssuc:ToolBar>
<SharePoint:UserInfoListFormToolBar runat="server"/>
<TABLE class="ms-formtable" style="margin-top: 8px;" border=0 cellpadding=0 cellspacing=0 width=100%>
<TR><SharePoint:CompositeField FieldName="Name" ControlMode="Display" runat="server"/></TR>
<SharePoint:ListFieldIterator runat="server"/>
<SharePoint:FormComponent TemplateName="AttachmentRows" runat="server"/>
</TABLE>
<table cellpadding=0 cellspacing=0 width=100%><tr><td class="ms-formline"><IMG SRC="/_layouts/images/blank.gif" width=1 height=1 alt=""></td></tr></table>
<TABLE cellpadding=0 cellspacing=0 width=100% style="padding-top: 7px"><tr><td width=100%>
<SharePoint:ItemHiddenVersion runat="server"/>
<wssuc:ToolBar CssClass="ms-formtoolbar" id="toolBarTbl" RightButtonSeparator=" " runat="server">
<Template_Buttons>
<SharePoint:CreatedModifiedInfo runat="server"/>
</Template_Buttons>
<Template_RightButtons>
<SharePoint:SaveButton runat="server"/>
<SharePoint:GoBackButton runat="server"/>
</Template_RightButtons>
</wssuc:ToolBar>
</td></tr></TABLE>
</SPAN>
<SharePoint:AttachmentUpload runat="server"/>
</Template>
</SharePoint:RenderingTemplate>
You can see right at the top of the template, the main controls for the page are contained within a Span tag with an id of ... 'part1'. Well, here's what my template looked like to begin with:
<SharePoint:RenderingTemplate ID="MyCustomTemplate" runat="server">
<Template>
<table cellpadding="0" cellspacing="0" id="onetIDListForm">
<tr>
<td>
<table border="0" width="100%">
<tr>
<td class="ms-toolbar" nowrap="">
<SharePoint:FormToolBar ID="FormToolBar2" runat="server" />
</td>
</tr>
<tr>
<td>
<table class="ms-formtable" border="0" cellpadding="2">
<SharePoint:ListFieldIterator ID="ListFieldIterator1" runat="server" />
</table>
<SharePoint:RequiredFieldMessage ID="RequiredFieldMessage1" runat="server" />
</td>
<tr>
<td class="ms-toolbar" nowrap="">
<table>
<tr>
<td class="ms-descriptiontext" nowrap="">
<SharePoint:CreatedModifiedInfo ID="CreatedModifiedInfo1" runat="server" />
</td>
<td width="99%" class="ms-toolbar" nowrap="">
<img src="/_layouts/images/blank.gif" width="1" height="18" />
</td>
<td class="ms-toolbar" nowrap="">
<SharePoint:SaveButton runat="server" ID="savebutton2" Text="Manage Tasks" />
</td>
<td class="ms-separator">
</td>
<td class="ms-toolbar" nowrap="" align="right">
<SharePoint:GoBackButton runat="server" ID="gobackbutton2" />
</td>
</tr>
</table>
</td>
</tr>
</tr>
</table>
<img src="/_layouts/images/blank.gif" width="590" height="1" alt="">
</td>
</tr>
</table>
</Template>
</SharePoint:RenderingTemplate>
So to start off, I wrapped my template in the same Span, to see what would happen:
<SharePoint:RenderingTemplate ID="MyCustomTemplate" runat="server">
<Template>
<SPAN id='SPAN1'>
<table cellpadding="0" cellspacing="0" id="Table1">
...
</table>
</SPAN>
</Template>
</SharePoint:RenderingTemplate>
But, when I did this, I still hit an error, just further down in the same piece of JS:
function ShowPartAttachment()
{
if (document.getElementById("part1")==null ||
typeof(document.getElementById("part1"))=="undefined")
{
var L_FormMissingPart1_Text= "This form was customized not working with attachement.";
alert(L_FormMissingPart1_Text);
return;
}
document.getElementById("part1").style.display="none";
document.getElementById("partAttachment").style.display="block";
GetAttachElement(FileuploadString+FileUploadIndex).focus();
}
this time on the line in the code above referring to "partAttachment". Off we go again into DefaultTemplates.ascx, where we find:
<SharePoint:RenderingTemplate ID="AttachmentUpload" runat="server">
<Template>
<INPUT type=hidden name='attachmentsToBeRemovedFromServer'>
<INPUT type=hidden name='RectGifUrl' value="/_layouts/images/rect.gif">
<SPAN id='partAttachment' style='display:none'>
<TABLE cellSpacing=0 cellPadding=0 border=0 width="100%">
<TBODY>
<TR>
<TD class=ms-descriptiontext style="padding-bottom: 8px;" colSpan=4 vAlign=top>
<SharePoint:EncodedLiteral runat="server" text="<%$Resources:wss,form_attachments_description%>" EncodeMethod='HtmlEncode'/>
</TD>
</TR>
<TR>
<TD width=190px class=ms-formlabel vAlign=top height=50px><SharePoint:EncodedLiteral runat="server" text="<%$Resources:wss,form_attachments_name%>" EncodeMethod='HtmlEncode'/></TD>
<TD width=400px class=ms-formbody vAlign=bottom height=15 id=attachmentsOnClient>
<SPAN dir="ltr">
<INPUT type=file name=fileupload0 id=onetidIOFile class="ms-fileinput" size=56 title="Name"></INPUT>
</SPAN>
</TD>
</TR>
<TR>
<TD class=ms-formline colSpan=4 height=1><IMG SRC="/_layouts/images/blank.gif" width=1 height=1 alt=""></TD>
</TR>
<TR>
<TD colSpan=4 height=10><IMG SRC="/_layouts/images/blank.gif" width=1 height=1 alt=""></TD>
</TR>
<TR>
<TD class="ms-attachUploadButtons" colSpan=4>
<INPUT class="ms-ButtonHeightWidth" id=attachOKbutton type=BUTTON onclick='OkAttach()' value="OK">
<span ID="idSpace" class=ms-SpaceBetButtons></span>
<INPUT t class="ms-ButtonHeightWidth" id=attachCancelButton type=BUTTON onclick='CancelAttach()' value="Cancel">
</TD>
</TR>
<TR>
<TD colSpan=4 height=60>
</TD>
<TD>
</TD>
</TR>
</TBODY>
</TABLE>
<script>
if (document.getElementById("onetidIOFile") != null)
document.getElementById("onetidIOFile").title = "<SharePoint:EncodedLiteral runat='server' text='<%$Resources:wss,form_attachments_name%>' EncodeMethod='EcmaScriptStringLiteralEncode'/>";
if (document.getElementById("attachOKbutton") != null)
document.getElementById("attachOKbutton").value = "<SharePoint:EncodedLiteral runat='server' text='<%$Resources:wss,form_ok%>' EncodeMethod='EcmaScriptStringLiteralEncode'/>";
if (document.getElementById("attachCancelButton") != null)
document.getElementById("attachCancelButton").value = "<SharePoint:EncodedLiteral runat='server' text='<%$Resources:wss,form_cancel%>' EncodeMethod='EcmaScriptStringLiteralEncode'/>";
</script>
</SPAN>
</Template>
</SharePoint:RenderingTemplate>
So it looks like we need to make use of the AttachmentUpload control in our template. Also, if we look at what the JavaScript is doing, it's hiding the one element and showing the other, so clearly the AttachmentUpload control needs to be outside our Span. With this in mind, I tried the following:
<sharepoint:renderingtemplate id="MyCustomTemplate" runat="server">
<Template>
<SPAN id='SPAN1'>
<table cellpadding="0" cellspacing="0" id="Table1">
...
</table>
</SPAN>
<SharePoint:AttachmentUpload ID="AttachmentUpload1" runat="server" />
</Template>
</sharepoint:renderingtemplate>
If you've followed all this way, don't worry, we're getting close...
With all of the above in place, I was now getting an error on the line below referring to idAttachmentsTable:
function OkAttach()
{
fileID=FileuploadString+FileUploadIndex;
fileInput=GetAttachElement(fileID);
filename=TrimWhiteSpaces(fileInput.value);
if (!filename)
{
var L_FileNameRequired_TXT="You must specify a non-blank value for File Name.";
alert(L_FileNameRequired_TXT);
fileInput.focus();
}
else
{
var L_FileUploadToolTip_text="Name";
oRow=document.getElementById("idAttachmentsTable").insertRow(-1);
RowID='attachRow'+FileUploadIndex;
oRow.id=RowID;
back for the last time in DefaultTemplates.ascx, I found:
<SharePoint:RenderingTemplate ID="AttachmentsField" runat="server">
<Template>
<TABLE border=0 cellpadding=0 cellspacing=0 id=idAttachmentsTable>
<asp:Literal ID="AttachmentsList" runat="server"/>
</TABLE>
</Template>
</SharePoint:RenderingTemplate>
but I also spotted:
<SharePoint:RenderingTemplate ID="AttachmentRows" runat="server">
<Template>
<TR id=idAttachmentsRow>
<TD nowrap="true" valign="top" class="ms-formlabel" width="20%">
<SharePoint:FieldLabel FieldName="Attachments" runat="server"/>
</TD>
<TD valign="top" class="ms-formbody" width="80%">
<SharePoint:AttachmentsField FieldName="Attachments" runat="server"/>
<SCRIPT>
var elm = document.getElementById("idAttachmentsTable");
if (elm == null || elm.rows.length == 0)
document.getElementById("idAttachmentsRow").style.display='none';
</SCRIPT>
</TD></TR>
</Template>
</SharePoint:RenderingTemplate>
so I did another search on "AttachmentRows" and came right back to the UserListForm we had near the beginning (and which also has the AttachmentUpload control at the bottom, and outside of the Span, but which I've not shown below for brevity -see earlier for the full template):
<SharePoint:RenderingTemplate ID="UserListForm" runat="server">
<Template>
<SPAN id='SPAN2'>
...
<SharePoint:ListFieldIterator runat="server"/>
<SharePoint:FormComponent TemplateName="AttachmentRows" runat="server"/>
so I added the FormComponent into my control template, and uploads are now working fine. The full template now looks like this:
<%@ Control Language="C#" %>
<%@ Assembly Name="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register TagPrefix="SharePoint" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
Namespace="Microsoft.SharePoint.WebControls" %>
<SharePoint:RenderingTemplate ID="MyCustomTemplate" runat="server">
<Template>
<span id='Span3'>
<table cellpadding="0" cellspacing="0" id="Table2">
<tr>
<td>
<table border="0" width="100%">
<tr>
<td class="ms-toolbar" nowrap="">
<SharePoint:FormToolBar ID="FormToolBar2" runat="server" />
</td>
</tr>
<tr>
<td>
<table class="ms-formtable" border="0" cellpadding="2">
<SharePoint:ListFieldIterator ID="ListFieldIterator1" runat="server" />
<SharePoint:FormComponent ID="FormComponent1" TemplateName="AttachmentRows" runat="server" />
</table>
<SharePoint:RequiredFieldMessage ID="RequiredFieldMessage1" runat="server" />
</td>
<tr>
<td class="ms-toolbar" nowrap="">
<table>
<tr>
<td class="ms-descriptiontext" nowrap="">
<SharePoint:CreatedModifiedInfo ID="CreatedModifiedInfo1" runat="server" />
</td>
<td width="99%" class="ms-toolbar" nowrap="">
<img src="/_layouts/images/blank.gif" width="1" height="18" />
</td>
<td class="ms-toolbar" nowrap="">
<SharePoint:SaveButton runat="server" ID="savebutton2" Text="Manage Tasks" />
</td>
<td class="ms-separator">
</td>
<td class="ms-toolbar" nowrap="" align="right">
<SharePoint:GoBackButton runat="server" ID="gobackbutton2" />
</td>
</tr>
</table>
</td>
</tr>
</tr>
</table>
<img src="/_layouts/images/blank.gif" width="590" height="1" alt="">
</td>
</tr>
</table>
</span>
<SharePoint:AttachmentUpload ID="AttachmentUpload1" runat="server" />
</Template>
</SharePoint:RenderingTemplate>
and the attachment fields works perfectly. Hopefully this will help someone with the same problem.
- H
I was doing a bunch of work with querystring variables a little while ago, so I spun up a small little Windows app that breaks a url into the address and querystrings, and then breaks these querystrings down variable by variable as well. You can pick it up here at CodePlex.
Here is a screenshot:

You can also add it into Visual Studio as an external tool (which you can do with pretty much any .exe, in fact).
I also command-line enabled it and set it Visual Studio to pass the selected text to the app so that you can select any text in the IDE, then launch it off the Tools menu (or set a keyboard shortcut) and have it pre-loaded with the passed in query string. To do this, go to External Tools:

Then add your .exe with a title (including shortcut key if you want) and set Arguments using the arrow alongside to some of the available variables (in this case $CurText, which is Current Text).

For those who missed all our advertising (including my post about the event), yesterday we had the one and only Scott Hanselman come and talk to us. Scott gave an absolutely awesome (and very candid) presentation about ASP.Net MVC and some of its related aspects. The talk was very well received, so Scott a big thanks again for taking time out of your holiday vacation to come visit us.
Incidentally, I tried to record the event with my little webcam. The video came out ok, but the sound is pretty unusable. Anyone very good at sound editing want to give it a go?
By the way, a huge thank you to BSG for so kindly sponsoring Scott's flights and some of the other expenses.
P.S. If you have no idea who Scott Hanselman is, you're probably not too involved in day to day software development. He's got excellent blog posts on just about everything.