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:
- Open the "New Form" for the list and remove all query string variables (should end up with http://server/site/listname/NewForm.aspx)
- 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 - Add a new "Content Editor Web Part"(CEWP) to the page after the existing web part
- 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>
- 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. - In the CEWP's tool pane, add security groups or audiences which should have the fields hidden/disabled to the "Target Audience" setting
- Save the web part properties, and click the "Exit Edit Mode" link in the upper right below "Site Actions".
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