dinsdag 6 maart 2012

Copy, Duplicate or Pre-populate Sharepoint Fields


New demand arrived to duplicate items in a custom list. Out of the box, Sharepoint hasn't provided
that functionality.
So, lets develop it our selfs...

Wat you need to do is to provide a custom link on you existing item that provides a parameter,
for example DID (Duplicate ID). Create a custom newform.aspx where you add a new datawebpart that
takes your parameter as filter. Add the fields that you want to duplicate to your page en put them 
in a table that you set hidden.
Once done, you add a content webpart Editor to your page and some next script to your page : 

_spBodyOnLoadFunctionNames.push('SPCC_Dup_fill_AllDefaultValues()');
ExecuteOrDelayUntilScriptLoaded(SPCC_Dup_fill_TaxonomyValues, "scriptforwebtaggingui.js");

// only change this function...
function SPCC_Dup_fill_AllDefaultValues(){          
 // Pre-populate peoplepicker field
 // fillPeoplePicker (ID of Sharepoint Form PeoplePicker_userfield, Name of Sharepoint Form PeoplePicker_userfield, ID of DIV readonly field)
 SPCC_Dup_fill_PeoplePicker("FIELDID_UserField","FIELDNAME$UserField", "REFERENCE_DIV_ID");
 // Pre-populate text Field
 // fillInputField( ID of Sharepoint Form Text Field, ID of DIV readonly field)
 SPCC_Dup_fill_InputField("FIELDID_TextField","REFERENCE_DIV_ID");
 // Pre-populate Lookup/Select Field
 // Fill_Lookup_Select_FromFieldName(Form FieldName, ID of DIV readonly field, type [Lookup, DropDownChoice ] )
 SPCC_Dup_fill_Lookup_Select_FromFieldName("FIELDID_DropDownChoice","REFERENCE_DIV_ID", 'DropDownChoice');
 // Pre-Populate Hyperlink
 // FillHyperlinkField(Form FieldName Url, Form FieldName Description, ID of DIV readonly field);
    SPCC_Dup_fill_HyperlinkField("FIELDID_UrlFieldUrl","FIELDID_UrlFieldDescription", "REFERENCE_DIV_ID");
 // Pre-Populate CheckBox Yes No
 SPCC_Dup_fill_CheckboxFieldYN("FIELDID_BooleanField", "REFERENCE_DIV_ID");
 // Pre-Populate Date Time field.  
 SPCC_Dup_fill_DateTimeField("FIELDID_DateTimeField_DateTimeFieldDate", "REFERENCE_DIV_ID", "mm/dd/yyyy");
 // Pre-Populate RadioButtonlist
 SPCC_Dup_fill_RadioButtonList("FIELDNAME$RadioButtons", "REFERENCE_DIV_ID");
 // Pre-Populate CheckButtonList
 SPCC_Dup_fill_CheckButtonList("FIELDID","REFERENCE_DIV_ID");
 // Pre-Populate Dropdown
 SPCC_Dup_fill_CustomSelect("FIELDID_DropDownChoice","REFERENCE_DIV_ID");
    // Pre-Populate MultiSelect
 SPCC_Dup_fill_MultiSelect("FIELDID_SelectCandidate", "REFERENCE_DIV_ID");        
    // Pre-Populate MetaDataField
 // see SPCC_Dup_fill_TaxonomyValues function
 // Pre-populate Mutli line text Field
 SPCC_Dup_fill_MultiTextField("FIELDID_TextField_inplacerte","REFERENCE_DIV_ID");
}

function SPCC_Dup_fill_TaxonomyValues(){
 // Pre-Populate MetaDataField
 SPCC_Dup_fill_MetaDataFied("FIELDID_ctl02", "REFERENCE_DIV_ID");
}
 

I have written an function for the different types of Sharepoint Fields... if I have missed one, 
don't hesitate to let me know...

Now the important block, the different functions...

two function I use often to get the Field Objects for manipulating the values or innerHTML methods:
// Direct call for getting the element Object by the ID-property of the element
function SPCC_Dup_getTagFromIdentifier(identifier) {  return document.getElementById(identifier); }
// Call for getting the element Object by the TagName and filter them by identifier or the ID-property
function SPCC_Dup_getTagFromIdentifierAndTitle(tagName, identifier, myid){
 var len = identifier.length;
 var tags = document.getElementsByTagName(tagName);
 for (var i=0; i< tags.length; i++)
 {
  var tempString = tags[i].id;
  if( (tags[i].id == myid) && ( (identifier == '') || (tempString.indexOf(identifier) == tempString.length - len)) )
  { return tags[i]; }
 }
 return null;
} 
 

One of the difficult fields to fill in by script was the PeoplePicker. 
This because when you take al look at the source of the generated Sharepoint HTML,
you see that the field exists of different span, div and input fields. 
Also, in the view state of your element you get HTML tags with a link and 
as display the name of the user that you have to put in the PeoplePicker field. 
So first step is to get only the username. 
Second step is to retrieve the element object of the peoplepicker field 
that ends with _upLevelDiv. 
Third step is to put your username in that DIV and at last but not least 
to validate the user. 

function SPCC_Dup_fill_PeoplePicker(myPeoplePicker_ID, myPeoplePicker_Name, newPP_ID){
// STEP 1: Retreive only username of the view PeoplePicker field
 var ppf = document.getElementById(newPP_ID);
 var myppfValue = ppf.innerHTML;
 var ibegin = myppfValue.indexOf('">') + 2;
 var myppfValueUpperCase = myppfValue.toUpperCase();
 var myPPValue = myppfValue.substr(ibegin, myppfValueUpperCase.indexOf('>/A<', ibegin ) - ibegin);
// STEP 2 : get the PeoplePicker field Object to fill in.
 var myPeoplePicker_UpperLevel_ID = myPeoplePicker_ID + '_upLevelDiv';
 var assingedToInput = SPCC_Dup_getTagFromIdentifier(myPeoplePicker_UpperLevel_ID);
// STEP 3 : put the username in the PeoplePicker Field object 
 assingedToInput.innerHTML = myPPValue ;
// LAST STEP : refresh and validate picker control
 if(!ValidatePickerControl(myPeoplePicker_ID))
 {
    ShowValidationError();
 }
 var arg = getUplevel(myPeoplePicker_ID);
 var ctx = myPeoplePicker_ID;
 EntityEditorSetWaitCursor(ctx);
 WebForm_DoCallback(myPeoplePicker_Name,arg,EntityEditorHandleCheckNameResult,ctx,EntityEditorHandleCheckNameError,true);
}
 

One of the easy ones is the normal InputFields. With this field you only have to read and set the values...
function SPCC_Dup_fill_InputField(SPFF_ID, newTF_ID)
{
 var sptf = document.getElementById(newTF_ID);
 var spff = document.getElementById(SPFF_ID);
 spff.value = sptf.innerHTML;
}
 

An other hard one is the Multiline textfield. I have passed half a day for understanding how 
this field is being generated by Sharepoint !!! Not easy to understand, because you only see a
hidden input field and some DIV's that are empty. 

First step I have done is to remove from the view field the surrounded DIV-tag. 
Afterwards convert it as full HTML format, and at least put it in the sharepoint Element and
call a sharepoint function to validate it. This is needed because otherwise your text is removed again.

function SPCC_Dup_fill_MultiTextField(SPFF_ID, newTF_ID){
// STEP 1 : get the content to fill in and remove the parent DIV-tag
            var sptf = document.getElementById(newTF_ID);
            var convertRTE = sptf.innerHTML;
            var ibegin = convertRTE.indexOf('">') + 5;
            convertRTE= convertRTE.substr(ibegin , convertRTE.indexOf('</div>',ibegin)-ibegin);
// STEP 2 : convert the content to full qualified HTML body format
            convertRTE= convertRTE.replace(/</g, "<");
            convertRTE= convertRTE.replace(/>/g, ">");
// STEP 3 : fill in the you multiline textfield with your bodycontent and validate it.
            var spff = document.getElementById(SPFF_ID);
            spff.UseInlineStyle = 'True';
            spff.innerHTML = convertRTE;
            RTE.RichTextEditor.transferContentsToInputField(SPFF_ID);
}
 

An other funny one is the Lookup or Select Fields. 
This because Sharepoint can generate two different type of fields. Or it is an select/option list
or just even an input tag. This depending if there are less or more than 20 elements in the list.
So you need to check the type of field Sharepoint has created, and depending on it populate the
select-tag or input-tag.

function SPCC_Dup_fill_Lookup_Select_FromFieldName(fieldName, newTF_ID, type){
 var value = document.getElementById(newTF_ID).innerHTML;
 if (value == undefined) return;
 var theSelect = SPCC_Dup_getTagFromIdentifierAndTitle("select", type,fieldName);
// if theSelect is null, it means that the target list has more than
 // 20 items, and the Lookup is being rendered with an input element
 if (theSelect == null) {
  var theInput = SPCC_Dup_getTagFromIdentifierAndTitle("input","",fieldName);
  ShowDropdown(theInput.id); //this function is provided by SharePoint
  var opt=document.getElementById(theInput.opt);
  SPCC_Dup_setSelectedOption(opt, value);
  OptLoseFocus(opt); //this function is provided by SharePoint
 } else {
  SPCC_Dup_setSelectedOption(theSelect, value);
 }
}
 
function SPCC_Dup_setSelectedOption(select, value)
{
 var opts = select.options;
 var l = opts.length;
 if (select == null) return;
 for (var i=0; i < l; i++) {
  if (opts[i].value == value) {
   select.selectedIndex = i;
   return true;
  }
 }          
 return false;
}
 

A Hyperlink field gives you in view an array with the link and the description of the link.
so to fill it in your newform you need to fill in two input fields. 
function SPCC_Dup_fill_HyperlinkField(SPFFUrl_ID, SPFFDesc_ID, newTF_ID){
 var sptf = document.getElementById(newTF_ID);
 var spffurl = document.getElementById(SPFFUrl_ID);
 var spffdesc = document.getElementById(SPFFDesc_ID);
 var mySplitResult = sptf.innerHTML.split(", ");
 if(mySplitResult.length > 1)
 {
  spffurl.value = mySplitResult[0];
  spffdesc.value = mySplitResult[1];
 }
 else
 {          
  spffurl.value = mySplitResult[0];
 }
}           
 

Sharepoint provided also a YES_NO field, behind the screen it is just a checkbox. 
function SPCC_Dup_fill_CheckboxFieldYN(SPFF_ID, newTF_ID){
 var sptf = document.getElementById(newTF_ID);
 var spff = document.getElementById(SPFF_ID);
 if (sptf.innerHTML.toUpperCase()=="TRUE"||sptf.innerHTML=="1"){
  spff.checked=true;
    }
    if (sptf.innerHTML.toUpperCase()=="FALSE"||sptf.innerHTML=="0"){
        spff.checked=false;
    }
}
 

Two elements that acts amost the same are the radiobutton list and the checkbutton list. 
Here you need to loop the array of items in your fields and check them if needed.

function SPCC_Dup_fill_RadioButtonList(SPFF_Name, newTF_ID){
 var sptf = document.getElementById(newTF_ID);
 var spff = document.getElementsByName(SPFF_Name);
 if (spff != null)
 {
  for (i=0; i < spff.length;i++)
  {
   if (spff[i].parentNode.innerHTML.indexOf(sptf.innerHTML) >= 0)
    {spff[i].checked = true;}
   else
    {spff[i].checked = false;}
  }                      
 }
}
                       
function SPCC_Dup_fill_CheckButtonList(SPFF_ID, newTF_ID){
 var sptf = document.getElementById(newTF_ID);
 var split_sptf = sptf.innerHTML.split(", ");
 var tags = document.getElementsByTagName('input');
 var identifier =  SPFF_ID.substr(0,SPFF_ID.length -5);
 var found = 0;
 for (var i=0; i< tags.length; i++)
 {   found = 0 ;
  var tempString = tags[i].id;
  if(tempString.indexOf(identifier) >= 0)
  {
   var spff= tags[i];
   for (j=0; j < split_sptf.length; j++)
   {
    if (spff.parentNode.innerHTML.indexOf(split_sptf[j]) >= 0)
    { spff.checked = true; found++;}
    else
       {   if (found == 0){spff.checked = false;}}
   }
  }                      
 }
}
 

The Date Time field of sharepoint is not so easy to fill in. 
This because you get from your field a totaly other format as what you need to fill in sharepoint.
The new fields exists of three blocks of fields. The date-field, the Hour-field and the minutes-field.
So you need to convert your datetime result into three different blocks. 
Also it is possible to fill in the hours as format HH: or HH AM/PM. 
We have put our general site settings for time to 24h option.
So here an example function, but depending your site settings 
it may have to be changed or be completed !
function SPCC_Dup_fill_DateTimeField(setDateFieldId,theDate, sDateFormat){
 // expecting format yyyy-MM-ddTHH:mm:ssZ
    var odate = SPCC_Dup_getTagFromIdentifier(theDate);
    var setDate = SPCC_Dup_getTagFromIdentifier(setDateFieldId);
    var setHours = SPCC_Dup_getTagFromIdentifier(setDateFieldId + "Hours");
    var setMinutes = SPCC_Dup_getTagFromIdentifier(setDateFieldId + "Minutes");
    if (odate != null)
    {
   var ddate = odate.innerHTML;
   if (ddate.length > 0)
   {
  var dDay = ddate.substring(8,10);
  var dMonth = ddate.substring(5,7);
  var dYear= ddate.substring(0,4);
  var dHours= ddate.substring(11,13);
  var dMinutes= ddate.substring(14,16);

  if (setDate != null)
  {
    if (sDateFormat.toLowerCase() == 'dd/mm/yyyy')
    {           setDate.value = dDay + '/' + dMonth + '/' + dYear;}
    if (sDateFormat.toLowerCase() == 'mm/dd/yyyy')
    {           setDate.value = dMonth + '/' + dDay + '/' + dYear;}
    if (sDateFormat.toLowerCase() == 'yyyy/mm/dd')
    {           setDate.value = dYear+ '/' + dMonth + '/' + dDay;}
  }
  if (setHours != null)
  {
    var iHours = parseInt(dHours) + 1;
    if ( iHours <10) {dHours = '0' + iHours;} else {dHours = iHours;}
    setHours.value = dHours +':';
  }
  if (setMinutes != null)
  {
    setMinutes.value = dMinutes;
  }
   }
 }
}

For a Select field, you have the option to choose of an normal selectbox, 
or one where you may fill in a custom text. This means that in case of pre-populate
you need to check if you value exist in the Dropdown field, if not to fill in the
custom inputfield. 

function SPCC_Dup_fill_CustomSelect(fieldID, newTF_ID){
 var value = document.getElementById(newTF_ID).innerHTML;
 if (value == undefined) return;
 var theSelect = document.getElementById(fieldID);
 if (SPCC_Dup_setSelectedOption(theSelect, value) == false)
 {   // value does not exist in the DropDown box. zo fill in the custum field.
  var myNewID = fieldID.replace("DropDownChoice", "ctl01");
  var theInput = document.getElementById(myNewID);
  theInput.value = value;
  var myNewButtonID = fieldID.replace("DropDownChoice", "FillInButton");
  SetChoiceOption(myNewButtonID);
 }
}

A interesting field for prepopulating is also the Multi Select field.
This because you get an array of fields in your result, but in the newform
you receive two multi select boxes. One with the available items and 
one with the selected items.
For making me easy, I created the script to select first the items in the
candidates list and call the sharepoint function to populate them in the selected box.
So I don't have to remove them myself in the candidate list and transfer them manualy in 
the selected items box. 

function SPCC_Dup_fill_MultiSelect(fieldID, newTF_ID){          
 var value = document.getElementById(newTF_ID).innerHTML;
 var fieldbuttonID = eval(fieldID.replace("SelectCandidate", "MultiLookupPicker_m"));
 var split_value = value.split("</a>");
 var theSelect = document.getElementById(fieldID);
 var i = 0;
 for (i=0; i< theSelect.options.length; i++)
 {
  var valuefound = false;
  for(var j=0;j< split_value.length;j++)
  {
   var ibegin = split_value[j].indexOf(">") + 4;
   var myMLPValue = split_value[j].substr(ibegin, split_value[j].length);
   if(theSelect.options[i].text == myMLPValue)
   {
    theSelect.options[i].selected = true;
    valuefound = true;
   }
   else
   {
    if(!valuefound)
    {
     theSelect.options[i].selected = false;
    }
   }
  }
 }
 GipAddSelectedItems(fieldbuttonID);
}

Last function I hope is the MetaData Field (or better known as Termstore or taxonomy-field).
What I thought to be difficult ended very easy to do. 
You just need to fill in a DIV that has your fieldID followed by editableRegion.
put the values in that DIV and call a Sharepoint function 
with as parameter the master Parent of your TermStore field. 
And at least call the validation function to justify your filled in records.
  
function SPCC_Dup_fill_MetaDataFied(fieldID, referenceFieldID){
 var value = document.getElementById(referenceFieldID).innerHTML;
 if (value != '')
 {
  var resetDefault = document.getElementById(fieldID.substr(0,fieldID.length-1) + "1");
  resetDefault.value = "";
  var targetvalue = document.getElementById(fieldID);
  var mydiv = document.getElementById(fieldID);
  var editableRegion = document.getElementById(fieldID + "editableRegion");
  editableRegion.innerHTML = value;  
  var taxonomyParent = editableRegion.parentNode.parentNode.parentNode;  
  var taxonomyObject = new Microsoft.SharePoint.Taxonomy.ControlObject(taxonomyParent);
  taxonomyObject.validateAll();
 }
}

I hope I have handled all different types of fields. 
It has costed me some hours of development, but I hope the functions I have written 
may help you and others. 
I also prefered not to use the JQuery library, this for learning reasons. 
It makes you understand better how Sharepoints generates his fields 
and how there behaviour are.


Spread the words around and forward my blog so many others can save some time on finding solutions.

Geen opmerkingen:

Een reactie posten