Showing posts with label List Input Forms. Show all posts
Showing posts with label List Input Forms. Show all posts

Friday, February 13, 2009

Correction - Customized Input Forms: The JavaScript Approach

Previously I posted about my method for customizing SharePoint list forms (NewForm.aspx, DispForm.aspx, EditForm.aspx). Recently, an issue has come up with respect to this code and saving list items.

If a user is modifying a list item, and they have disabled controls on the page (grayed out), then when they save the item, those disabled fields will revert to the default value specified for each of those columns. The correct values populate on the page, but apparently the values stored within disabled controls do not get returned to the server upon the form being submitted.

I have come up with a quick solution (not sure if it is the best at this moment), but it allows for the values to be returned to the server by re-enabling all of the disabled fields just before the form is submitted. Only problem so far is that if the page takes a while to submit, the fields appear to be editable to the user, even though changes made won't actually be sent to the server.

Please see this link for a reference to the function PreSaveAction(): Add JavaScript Date Validation into List Item forms

Updated code (new code added is highlighted in yellow): CustomizedInputForms_JavaScriptFile

Enjoy!
--andrew

Tuesday, December 9, 2008

MOSS Cascading DropDownLists - The Sexy Approach - 1 of 2

Disclaimer: I recommend reviewing any/all code (including content that is linked to) and fully understanding what it does before deploying anything to your staging, test, and/or production environments. There is nothing worse than deploying code/solutions which cause critical errors that you don't know how to fix.

Link to next post: MOSS Cascading DropDownLists - The Sexy Approach - 2 of 2

I am really excited about this post for a number of reasons:
  • AJAX makes things look really slick
  • Cascading DropDownLists in SharePoint are a pain
  • I've finally finished this project!
The task at hand was to implement Cascading DropDownLists within a standard SharePoint list (no InfoPath). Some resources that were immensely helpful while constructing this are as follows.
Regarding Custom Field Controls and Cascading DropDownLists:
Regarding AJAX integration with SharePoint:
The project consists of two solutions, one which configures the web.config with modifications to allow ASP.NET AJAX, and one which deploys an AJAX Cascading DropDownList custom field control. This post will cover the integration with AJAX. My next post will cover the custom field control.

Prerequisites:
These are links to the four files (Feature.xml, manifest.xml, wsp_structure.ddf, AjaxControlToolkitSupportInstaller.cs) which are necessary for the solution which enables AJAX on your web application. I have decided to host the files within my GoogleDocs account because I like color coded files, there is too much code to paste it into this blog window, and I don't feel like creating a CodePlex project to upload, maintain, track bugs, and such.

If you are familiar at all with SharePoint Solution deployment, you will be right at home with manifest.xml and wsp_structure.ddf. I did not use any 3rd party tools for solution deployment. I used a simple C# class library with some custom batch files for processing the wsp_structure.ddf.

Please note:  You *must* create two GUIDS: one for the solution, and one for the feature. Also, be sure to create your assembly with a strong name, and insert your PublicKeyToken into the Feature.xml file.

I took bits and pieces from both Rich Finn's CodePlex project and Mike Ammerlaan's blog to create this feature. However, I really liked the way that Ted Pattison implemented a SPWebConfigModification feature in his post Using a Web Application Feature to Modify web.config, so instead of Ajaxify MOSS's command line approach, this is entirely browser based, and activated/deactivated within Central Administration on the "Web Application Features" page. Speaking from experience, be sure you have the correct web application selected before you activate the feature.

Part 2 to be coming shortly.

Enjoy!
--andrew

Monday, July 28, 2008

Customized Input Forms: The JavaScript Approach

Correction [02.13.2009]: I have recently been informed of an issue with field values not saving when they are disabled for the user. Please see my post here for updated code.

If any of you have had to customize the input forms for lists or libraries in SharePoint, you'll know that it is not easy. You can customize the form with a custom list form web part, but you better not delete the original list form web part, otherwise your list gets hosed. You can have both on the page (with the original web part hidden), but then you lose attachment support with JavaScript errors. If you create a new page without the original list form web part, then you get a nice JavaScript alert saying that the form has been customized to not allow attachments. If you used the custom list form web part, then you might find it cumbersome and tedious to make changes when new columns are added. I have tried countless recommendations for modifying the list input form, and none of them really seemed to work cleanly and effectively. Until I stumbled across the forum discussion located here on MSDN Forums.

Of all of the ideas proposed in this forum topic, I found tscheifler's usage of JavaScript quite intriguing. There were some limitations that I found when implementing their exact solution (most notably around disabling columns of type: Lookup, Date and Time, and Multiple lines of Text). Extending this, and incorporating various other bits of information lying about the web, I ended up with a solution I am quite happy with. This assumes you have an existing list with several fields you would like to hide/disable for certain user groups. We will begin by modifying the "NewForm.aspx" page, but these steps will work for any of the input forms:
  1. Open the "New Form" for the list and remove all query string variables (should end up with http://server/site/listname/NewForm.aspx)
  2. Append "?ToolPaneView=2" onto the end of the url (http://server/site/listname/NewForm.aspx?ToolPaneView=2)

    Note: this places the page into "Add a Web Part" view
  3. Add a new "Content Editor Web Part"(CEWP) to the page after the existing web part
  4. Modify the source view of the CEWP to include this code:
    <SCRIPT LANGUAGE="JavaScript">
    <!--
    function disableChildren(currentElement)
    {
    if (currentElement)
    {
    if(currentElement.tagName == "IFRAME")
    {
    frm = window.frames[currentElement.id].document;
    disableChildren(frm.getElementsByTagName("html")[0]);
    }
    var i=0;
    var currentElementChild=currentElement.childNodes[i];
    while (currentElementChild)
    {
    disableChildren(currentElementChild);
    i++;
    currentElementChild=currentElement.childNodes[i];
    }
    if (currentElement.tagName)
    {
    currentElement.setAttribute("disabled", "true");
    currentElement.setAttribute("contentEditable",
    "false");
    currentElement.setAttribute("onclick", "");
    currentElement.removeAttribute("href");
    }
    }
    }
    
    function hideRowsAfter(currentRow)
    {
    row = currentRow.nextSibling
    while (row)
    {
    row.style.display = "none";
    row = row.nextSibling;
    }
    }
    
    function findControl(FieldName, opp)
    {
    FieldName = "FieldName=\"" + FieldName + "\"";
    //get all comments
    var arr = document.getElementsByTagName("!");
    for (var i=0;i < arr.length; i++ ) 
    {
    // now match the field name
    if (arr[i].innerHTML.indexOf(FieldName) >= 0)
    {
    switch(opp) 
    {
    case 0:  //disable all children
    disableChildren(arr[i].parentNode.parentNode);
    break;
    case 1:  //hide row
    arr[i].parentNode.parentNode.style.display="none";
    break;
    case 2:  //hide all rows after current
    hideRowsAfter(arr[i].parentNode.parentNode);
    break;
    default:
    break;
    }
    }
    }
    }
    
    function disableControls(inputArray)
    {
    for (var i=0; i < inputArray.length; i++)
    {
    findControl(inputArray[i], 0);
    }
    }
    
    function hideControls(inputArray)
    {
    for (var i=0; i < inputArray.length; i++)
    {
    findControl(inputArray[i], 1);
    }
    }
    
    function hideControlsAfter(input)
    {
    findControl(input, 2);
    }
    
    //Usage:
    // disableControls(["Field Name 1", "Field Name 2"..]);
    // hideControls(["Field Name 1", "Field Name 2"..]);
    // hideControlsAfter("Field Name");
    
    //-->
    </SCRIPT>
  5. Follow the "Usage notes" to add calls to the disableControls, hideControls, or hideControlsAfter functions which will modify the fields displayed on the page

    Note: you can find the values to put in place of "Field Name 1" and "Field Name 2" by viewing the source view for the page and searching for: FieldName="
    The value within the double quotation marks will be what you enter inside the arrays in the hide/disable function calls.
  6. In the CEWP's tool pane, add security groups or audiences which should have the fields hidden/disabled to the "Target Audience" setting
  7. Save the web part properties, and click the "Exit Edit Mode" link in the upper right below "Site Actions".
Now you should see all of the fields hidden or disabled based on your security settings specified within the CEWP.

To extend this into a more reusable solution, I created a custom CEWP with this JavaScript code, exported it to a .dwp file, and then uploaded it to the Web Part gallery. Then, the only modifications needed are to add this web part to the page (under ToolPaneView=2 view), add the "usage" functions, and add the security groups to the targeted audiences.

This solution now hides/disables fields specified within the original list input form, which retains the attachment functionality, while being able to easily make modifications when new fields are added/modified based on column ordering.

However, there are some limitations to this method. None of which were show stoppers for me, since this method is very easy to undo (simply remove the CEWP from the forms). Especially in the interim until Microsoft is able to fix this little problem and offer a clean, efficient, and effective method of customizing input forms. These limitations are:
  • Security groups or audiences need to be created for the group of users which should have limited access to list fields, which excludes the users with access to the restricted fields.
  • You cannot make any of the hidden/disabled fields required unless you specify default values, as the fields will still remain on the page and submit data, they are just hidden to the user
  • If the user employs firebug or other plugins which can alter JavaScript, they could unhide/undisable the fields you removed and then manipulate the data
  • Information in these fields is still visible to users if the columns are displayed within list views, as well as Source View of the input form

Enjoy!
--andrew