Edit Document Fields 5.1.0
Bookmark :
Updated version of Edit Document Fields is now available.
Added some more *smarts* to the Edit Document Fields 5.0.1. When selecting categories with documents the categories are not included at all. Download Edit Document 5.1.0 or if you would like to step threw the code break down then continue.
Updated version of Edit Document Fields is now available.
Added some more *smarts* to the Edit Document Fields 5.0.1. When selecting categories with documents the categories are not included at all. Download Edit Document 5.1.0 or if you would like to step threw the code break down then continue.
The 5 Simple Steps
- Select the document(s) to update
- Click your toolbar button
- Select the field to update
- Select the data type or action to be performed
- Enter the new information (if prompted)
Limitations
- Only 500 documents can be updated at a time
- Field value is limited to 255 because of the @Prompt limitations
- When updating multiple documents only the documents that are selected and visible (expanded) in the view will be updated. Notes needs to be able to navigate to the document
Code Break Down
Constants
REM {Edit Document Fields 5.1.0 by Chad Schelfhout.};
REM {Visit http://www.chadsmiley.com/EditDocumentFields for the latest updates};
REM {Constants};
EditLastField:="edfLastField";
EditLastFieldDataType:="edfLastFieldDataType";
EditLastSeparator:="%~%";
ProfileName:="ChadSmiley Tools";
PromptTitle:=@DbTitle+" - "+@ViewTitle;
MaxSearchForSelectedDocs:=5520;
MaxUpdatedDocuments:=1000;
ArraySeperator:=";";
NoteEntryLength:=11;
PormptNewLine:=@Char(13)+@Char(13);
CategoryNoteID:="NT00000000";
REM {Visit http://www.chadsmiley.com/EditDocumentFields for the latest updates};
REM {Constants};
EditLastField:="edfLastField";
EditLastFieldDataType:="edfLastFieldDataType";
EditLastSeparator:="%~%";
ProfileName:="ChadSmiley Tools";
PromptTitle:=@DbTitle+" - "+@ViewTitle;
MaxSearchForSelectedDocs:=5520;
MaxUpdatedDocuments:=1000;
ArraySeperator:=";";
NoteEntryLength:=11;
PormptNewLine:=@Char(13)+@Char(13);
CategoryNoteID:="NT00000000";
Data Types and Data Type Code
This defines all the possible datatypes and the formula that will be used to set the field.REM {Data types|@Function execution};
DataTypesCombo:=@Explode(
"Integer|@TextToNumber(RawValue)$"+
"Integer Multi Value|@TextToNumber(@Explode(RawValue;Separator;@True))$"+
"Date|@ToTime(RawValue)$"+
"Date Multi Value|@ToTime(@Explode(RawValue;Separator;@True))$"+
"Text|@Text(RawValue)$"+
"Text Multi Value|@Text(@Explode(RawValue;Separator;@True))$"+
"Name|RawValue$"+
"Name Multi Value|RawValue$"+
"Common Name|@Name([CN];RawValue)$"+
"CommonName Multi Value|@Name([CN];@Explode(RawValue;\":\";@True))$"+
"Upper Case Text|@UpperCase(@Implode(@Text(@GetField(EditField[ef]))))$"+
"Lower Case Text|@LowerCase(@Implode(@Text(@GetField(EditField[ef]))))$"+
"Proper Case Text|@ProperCase(@Implode(@Text(@GetField(EditField[ef]))))$"+
"Upper Case Text Multi Value|@UpperCase(@Explode(@Text(@GetField(EditField[ef]));Separator;@True))$"+
"Lower Case Text Multi Value|@LowerCase(@Explode(@Text(@GetField(EditField[ef]));Separator;@True))$"+
"Proper Case Text Multi Value|@ProperCase(@Explode(@Text(@GetField(EditField[ef]));Separator;@True))$"+
"Replace Substring|@ReplaceSubstring(@GetField(EditField[ef]);FromRawValue;RawValue)$"+
"Replace|@Explode(@Replace(@GetField(EditField[ef]);FromRawValue;RawValue);Separator;@True)$"+
"Implode|@Implode(@Text(@GetField(EditField[ef]));Separator)$"+
"Explode|@Explode(@Text(@GetField(EditField[ef]));Separator;@True)$"+
"Formula|@Eval(RawValue)$"+
"Abbreviate Name|@Name([Abbreviate];RawValue)$"+
"Abbreviate Name Multi Value|@Name([Abbreviate];@Explode(RawValue;Separator;@True))$"+
"Password Set|@Password(RawValue)$"+
"Password Convert|@Password(@GetField(EditField[ef]))$"+
"Remove Field|@DeleteField$"+
"Unique|@Unique(@GetField(EditField[ef]))$"+
"+ Append Values|@If(" +
" @GetField(EditField[ef])=\"\";RawValue;" +
" @Contains(DefaultDataType;\"Date\");" +
" @If(@IsError(@ToTime(RawValue));" +
" \"\";" +
" @SetField(EditField[ef];@GetField(EditField[ef]) : @TextToTime(@Explode(RawValue;Separator))));" +
" @Contains(DefaultDataType;\"Integer\");" +
" @If(@IsError(@TextToNumber(@Explode(RawValue;Separator)));" +
" \"\";" +
" @SetField(EditField[ef];@GetField(EditField[ef]) : @TextToNumber(@Explode(RawValue;Separator))));" +
" @SetField(EditField[ef];@GetField(EditField[ef]) : @Explode(RawValue;Separator)))$"+
"Sort Ascending|@Sort(@GetField(EditField[ef]);[Ascending])$"+
"Sort Descending|@Sort(@GetField(EditField[ef]);[Descending])";"$");
DataTypes:=@Word(DataTypesCombo;"|";1);
DataTypesAction:=@Word(DataTypesCombo;"|";2);
DataTypesCombo:=@Explode(
"Integer|@TextToNumber(RawValue)$"+
"Integer Multi Value|@TextToNumber(@Explode(RawValue;Separator;@True))$"+
"Date|@ToTime(RawValue)$"+
"Date Multi Value|@ToTime(@Explode(RawValue;Separator;@True))$"+
"Text|@Text(RawValue)$"+
"Text Multi Value|@Text(@Explode(RawValue;Separator;@True))$"+
"Name|RawValue$"+
"Name Multi Value|RawValue$"+
"Common Name|@Name([CN];RawValue)$"+
"CommonName Multi Value|@Name([CN];@Explode(RawValue;\":\";@True))$"+
"Upper Case Text|@UpperCase(@Implode(@Text(@GetField(EditField[ef]))))$"+
"Lower Case Text|@LowerCase(@Implode(@Text(@GetField(EditField[ef]))))$"+
"Proper Case Text|@ProperCase(@Implode(@Text(@GetField(EditField[ef]))))$"+
"Upper Case Text Multi Value|@UpperCase(@Explode(@Text(@GetField(EditField[ef]));Separator;@True))$"+
"Lower Case Text Multi Value|@LowerCase(@Explode(@Text(@GetField(EditField[ef]));Separator;@True))$"+
"Proper Case Text Multi Value|@ProperCase(@Explode(@Text(@GetField(EditField[ef]));Separator;@True))$"+
"Replace Substring|@ReplaceSubstring(@GetField(EditField[ef]);FromRawValue;RawValue)$"+
"Replace|@Explode(@Replace(@GetField(EditField[ef]);FromRawValue;RawValue);Separator;@True)$"+
"Implode|@Implode(@Text(@GetField(EditField[ef]));Separator)$"+
"Explode|@Explode(@Text(@GetField(EditField[ef]));Separator;@True)$"+
"Formula|@Eval(RawValue)$"+
"Abbreviate Name|@Name([Abbreviate];RawValue)$"+
"Abbreviate Name Multi Value|@Name([Abbreviate];@Explode(RawValue;Separator;@True))$"+
"Password Set|@Password(RawValue)$"+
"Password Convert|@Password(@GetField(EditField[ef]))$"+
"Remove Field|@DeleteField$"+
"Unique|@Unique(@GetField(EditField[ef]))$"+
"+ Append Values|@If(" +
" @GetField(EditField[ef])=\"\";RawValue;" +
" @Contains(DefaultDataType;\"Date\");" +
" @If(@IsError(@ToTime(RawValue));" +
" \"\";" +
" @SetField(EditField[ef];@GetField(EditField[ef]) : @TextToTime(@Explode(RawValue;Separator))));" +
" @Contains(DefaultDataType;\"Integer\");" +
" @If(@IsError(@TextToNumber(@Explode(RawValue;Separator)));" +
" \"\";" +
" @SetField(EditField[ef];@GetField(EditField[ef]) : @TextToNumber(@Explode(RawValue;Separator))));" +
" @SetField(EditField[ef];@GetField(EditField[ef]) : @Explode(RawValue;Separator)))$"+
"Sort Ascending|@Sort(@GetField(EditField[ef]);[Ascending])$"+
"Sort Descending|@Sort(@GetField(EditField[ef]);[Descending])";"$");
DataTypes:=@Word(DataTypesCombo;"|";1);
DataTypesAction:=@Word(DataTypesCombo;"|";2);
Setup
First we need to determine the fields that are available, based on the currently selected document. Then get the last updated field based on the form of the currently selected document.REM {Get a listing of all the fields on the current document};
List:=@Sort(@DocFields);
REM {Look for last field modified in Profile Doc};
FieldList:=@Explode(@GetProfileField(ProfileName;EditLastField;@UserName);ArraySeperator;@True);
REM {Get the list of forms and field that was updated using Edit Document Fields};
FieldListForms:=@Word(FieldList;EditLastSeparator;1);
FieldListField:=@Word(FieldList;EditLastSeparator;2);
FieldListLastIndex:=@Member(Form;FieldListForms);
REM {If the FieldListLastIndex is greater than zero then set the last field to the what was in the profile document};
@If(FieldListLastIndex >0;
@Do(LastField:=FieldListField[ FieldListLastIndex ];
FieldList:=@ReplaceSubstring(FieldList;FieldList[ FieldListLastIndex ];""));
LastField :="");
List:=@Sort(@DocFields);
REM {Look for last field modified in Profile Doc};
FieldList:=@Explode(@GetProfileField(ProfileName;EditLastField;@UserName);ArraySeperator;@True);
REM {Get the list of forms and field that was updated using Edit Document Fields};
FieldListForms:=@Word(FieldList;EditLastSeparator;1);
FieldListField:=@Word(FieldList;EditLastSeparator;2);
FieldListLastIndex:=@Member(Form;FieldListForms);
REM {If the FieldListLastIndex is greater than zero then set the last field to the what was in the profile document};
@If(FieldListLastIndex >0;
@Do(LastField:=FieldListField[ FieldListLastIndex ];
FieldList:=@ReplaceSubstring(FieldList;FieldList[ FieldListLastIndex ];""));
LastField :="");
Field Selection/Add
Display a list of all available fields with the ability to enter new ones. Then set the prompt title that will be used throughout the rest of the code.REM {Prompt for which field needs to be updated. Loop until a field is selected or 'Cancel' is selected};
@DoWhile(
EditField:=@Prompt([OkCancelEditCombo];PromptTitle;"Select the field you wish to alter or enter a new field to add:";LastField;@Trim(@Unique(List : LastField)));
EditField="");
EditFieldPromptTitle:=EditField+" - "+PromptTitle;
@DoWhile(
EditField:=@Prompt([OkCancelEditCombo];PromptTitle;"Select the field you wish to alter or enter a new field to add:";LastField;@Trim(@Unique(List : LastField)));
EditField="");
EditFieldPromptTitle:=EditField+" - "+PromptTitle;
Determine Field Type
Using the form of the currently selected document and the field that was just selected to determine the last data type used and the last formula code used. If there was no previous data type try to determine the data type base on the data of the field. If there are any problems the default field type will be text.REM {This will allow the retrieval of the data type of the field that was last selected. Data is stored like Form+Field%~%DataType.};
FormFieldList:=@Explode(@GetProfileField(ProfileName;EditLastFieldDataType;@UserName);ArraySeperator;@True);
FormFieldListFormField:=@Word(FormFieldList;EditLastSeparator;1);
FormFieldListDataType:=@Word(FormFieldList;EditLastSeparator;2);
FormFieldListFormulaCode:=@Word(FormFieldList;EditLastSeparator;3);
FormFieldListIndex:=@Member(Form+EditField;FormFieldListFormField);
@If(FormFieldListIndex >0;
@Do(DefaultDataType:=FormFieldListDataType[ FormFieldListIndex ];
FormFieldListFormulaCode:=FormFieldListFormulaCode[ FormFieldListIndex ];
FormFieldList:=@ReplaceSubstring(FormFieldList;FormFieldList[ FormFieldListIndex ];""));
DefaultDataType :="");
REM {If there was no data type used for the field on the form the try to determine the data type};
DefaultDataType :=
@If(DefaultDataType!="";
DefaultDataType;
@If(
@IsNumber(@GetField(EditField));
@If(@Count(@GetField(EditField)) >1;
"Integer Multi Value";
"Integer");
@IsTime(@GetField(EditField));
@If(@Count(@GetField(EditField)) >1;
"Date Mult iValue";
"Date");
@If(@Count(@GetField(EditField)) >1;
"Text Multi Value";
"Text")
)
);
REM {If the data type is a type of error then select the data type of text};
DefaultDataType:=@IfError(DefaultDataType;"Text");
FormFieldList:=@Explode(@GetProfileField(ProfileName;EditLastFieldDataType;@UserName);ArraySeperator;@True);
FormFieldListFormField:=@Word(FormFieldList;EditLastSeparator;1);
FormFieldListDataType:=@Word(FormFieldList;EditLastSeparator;2);
FormFieldListFormulaCode:=@Word(FormFieldList;EditLastSeparator;3);
FormFieldListIndex:=@Member(Form+EditField;FormFieldListFormField);
@If(FormFieldListIndex >0;
@Do(DefaultDataType:=FormFieldListDataType[ FormFieldListIndex ];
FormFieldListFormulaCode:=FormFieldListFormulaCode[ FormFieldListIndex ];
FormFieldList:=@ReplaceSubstring(FormFieldList;FormFieldList[ FormFieldListIndex ];""));
DefaultDataType :="");
REM {If there was no data type used for the field on the form the try to determine the data type};
DefaultDataType :=
@If(DefaultDataType!="";
DefaultDataType;
@If(
@IsNumber(@GetField(EditField));
@If(@Count(@GetField(EditField)) >1;
"Integer Multi Value";
"Integer");
@IsTime(@GetField(EditField));
@If(@Count(@GetField(EditField)) >1;
"Date Mult iValue";
"Date");
@If(@Count(@GetField(EditField)) >1;
"Text Multi Value";
"Text")
)
);
REM {If the data type is a type of error then select the data type of text};
DefaultDataType:=@IfError(DefaultDataType;"Text");
Select Data Type/Set Action
Once the data type is selected determine the posistion in the array of the data type and set the data type action with the action in the same position of the array.REM {Prompt for which data type you would like the data to be. This needs to be done before value prompt to determine if the Picklist or any prompting needs to be used.};
DataType:=@Prompt([OkCancelList];EditFieldPromptTitle;"Please select the correct data type or action for field: "+EditField+".";DefaultDataType;DataTypes);
REM {The DataTypeAction will contain the formula that will be executed to retrieve the new value};
DataTypeAction:=DataTypesAction[ @Member(DataType;DataTypes) ];
DataType:=@Prompt([OkCancelList];EditFieldPromptTitle;"Please select the correct data type or action for field: "+EditField+".";DefaultDataType;DataTypes);
REM {The DataTypeAction will contain the formula that will be executed to retrieve the new value};
DataTypeAction:=DataTypesAction[ @Member(DataType;DataTypes) ];
Get Original Value
When using the formula data type there is no reason to see the selected fields value, because you would reference the field name when writing formula. Since there is no real need of the field value why not show the last formula for that field.REM {If formula was used on this field then use that instead of the fields value. Format the original value as text because the @Prompt command requires text.};
OriginalValue:=@If(DataType=formula & DefaultDataType=formula & FormFieldListFormulaCode!="";
FormFieldListFormulaCode;
@If(@Contains(DefaultDataType;MultiValue);
@Implode(@Text(@GetField(EditField));ArraySeperator);
@Text(@GetField(EditField)))
);
OriginalValue:=@If(DataType=formula & DefaultDataType=formula & FormFieldListFormulaCode!="";
FormFieldListFormulaCode;
@If(@Contains(DefaultDataType;MultiValue);
@Implode(@Text(@GetField(EditField));ArraySeperator);
@Text(@GetField(EditField)))
);
Replace & Replace Substring
The idea behind the search and replace is to update all occurances of a string, so why not search over multiple fields. This allows multiple fields to be selected.Then determine what to search for.
REM {Prompt for additional fields and determine the string that they are searching for.};
@If(DataType=@Explode("Replace Substring;Replace";ArraySeperator);
@Do(EditField:=@Unique(EditField : @Prompt([OkCancelListMult];PromptTitle;"Select any addtional fields you wish to alter:";EditField;List));
FromRawValue:=@Prompt([OkCancelEdit];EditFieldPromptTitle;"Please enter the text (Case sensitive) to search for in: "+@Implode(EditField;", ")+".";""));
@Do(EditField:=EditField;
FromRawValue:="")
);
@If(DataType=@Explode("Replace Substring;Replace";ArraySeperator);
@Do(EditField:=@Unique(EditField : @Prompt([OkCancelListMult];PromptTitle;"Select any addtional fields you wish to alter:";EditField;List));
FromRawValue:=@Prompt([OkCancelEdit];EditFieldPromptTitle;"Please enter the text (Case sensitive) to search for in: "+@Implode(EditField;", ")+".";""));
@Do(EditField:=EditField;
FromRawValue:="")
);
Implode/Explode
When exploding multiple characters can be used to separate text. If no seperator is entered a semicolon will be used. When imploding the string entered will be used to seperate the values.Separator:=@If(DataType=@Explode("Implode;Explode";ArraySeperator);
@Prompt([OkCancelEdit];PromptTitle;"Enter the "+@If(DataType="Implode";"separator";"separators")+ArraySeperator;"");
ArraySeperator);
@Prompt([OkCancelEdit];PromptTitle;"Enter the "+@If(DataType="Implode";"separator";"separators")+ArraySeperator;"");
ArraySeperator);
Get New Value
Most data types don't have any promptings like Unique or Sort, the rest the prompt depends on the data type.REM {Based on what type of data is being entered different prompts will happen if any at all.};
RawValue:=@If(
@Contains(DataType;"Name Multi Value");@PickList([Name]);
@Contains(DataType;"Name");@PickList([Name] : [Single]);
DataType=@Explode("Remove Field;Unique;Sort Ascending;Sort Descending;Implode;Explode;Proper Case Text;Proper Case Text Multi Value;Lower Case Text;Lower Case Text Multi Value;Upper Case Text;Upper Case Text Multi Value;Password Convert";ArraySeperator);"";
@Contains(DataType;"Multi Value");@Prompt([OkCancelEdit];EditFieldPromptTitle;"Please enter the new desired value for: "+@Implode(EditField;", ")+"."+PormptNewLine+"Seperated with;for each value.";OriginalValue);
@Contains(DataType;"+ Append Values");@Prompt([OkCancelEdit];EditFieldPromptTitle;"Please enter values to append: "+@Implode(EditField;", ")+"."+PormptNewLine+"Seperated with;for each value.";"");
DataType=@Explode("Replace Substring;Replace";ArraySeperator);@Prompt([OkCancelEdit];EditFieldPromptTitle;"Please enter the text to repalce with in: "+EditField+".";"");
DataType=formula;@Do(@DoWhile(
OriginalValue:=@Prompt([OkCancelEdit];EditFieldPromptTitle;"Please enter the new desired formula for: "+EditField+".";OriginalValue);
tempReturnCheck:=@CheckFormulaSyntax(OriginalValue);
@If(tempReturnCheck!="1";@Prompt([Ok];"Invalid Formula";@Text(tempReturnCheck));"");
tempReturnCheck != "1");
OriginalValue);
@Prompt([OkCancelEdit];EditFieldPromptTitle;"Please enter the new desired value for: "+EditField+".";OriginalValue)
);
RawValue:=@If(
@Contains(DataType;"Name Multi Value");@PickList([Name]);
@Contains(DataType;"Name");@PickList([Name] : [Single]);
DataType=@Explode("Remove Field;Unique;Sort Ascending;Sort Descending;Implode;Explode;Proper Case Text;Proper Case Text Multi Value;Lower Case Text;Lower Case Text Multi Value;Upper Case Text;Upper Case Text Multi Value;Password Convert";ArraySeperator);"";
@Contains(DataType;"Multi Value");@Prompt([OkCancelEdit];EditFieldPromptTitle;"Please enter the new desired value for: "+@Implode(EditField;", ")+"."+PormptNewLine+"Seperated with;for each value.";OriginalValue);
@Contains(DataType;"+ Append Values");@Prompt([OkCancelEdit];EditFieldPromptTitle;"Please enter values to append: "+@Implode(EditField;", ")+"."+PormptNewLine+"Seperated with;for each value.";"");
DataType=@Explode("Replace Substring;Replace";ArraySeperator);@Prompt([OkCancelEdit];EditFieldPromptTitle;"Please enter the text to repalce with in: "+EditField+".";"");
DataType=formula;@Do(@DoWhile(
OriginalValue:=@Prompt([OkCancelEdit];EditFieldPromptTitle;"Please enter the new desired formula for: "+EditField+".";OriginalValue);
tempReturnCheck:=@CheckFormulaSyntax(OriginalValue);
@If(tempReturnCheck!="1";@Prompt([Ok];"Invalid Formula";@Text(tempReturnCheck));"");
tempReturnCheck != "1");
OriginalValue);
@Prompt([OkCancelEdit];EditFieldPromptTitle;"Please enter the new desired value for: "+EditField+".";OriginalValue)
);
Update Profile
Now that the data type has been selected and the new value or forumula has been entered lets set the profile document so it will be available the next time.REM {Store Field in Profile doc};
@SetProfileField(ProfileName;EditLastField;@Unique(@Explode(FieldList : (Form+EditLastSeparator+EditField[1]);ArraySeperator;@False));@UserName);
REM {Store Data Type of Field in Profile doc};
@SetProfileField(ProfileName;EditLastFieldDataType;@Unique(@Explode(FormFieldList : (Form+EditField[1]+EditLastSeparator+DataType+EditLastSeparator+@If(DataType=formula;RawValue;FormFieldListFormulaCode));ArraySeperator;@False));@UserName);
@SetProfileField(ProfileName;EditLastField;@Unique(@Explode(FieldList : (Form+EditLastSeparator+EditField[1]);ArraySeperator;@False));@UserName);
REM {Store Data Type of Field in Profile doc};
@SetProfileField(ProfileName;EditLastFieldDataType;@Unique(@Explode(FormFieldList : (Form+EditField[1]+EditLastSeparator+DataType+EditLastSeparator+@If(DataType=formula;RawValue;FormFieldListFormulaCode));ArraySeperator;@False));@UserName);
Find all the documents to be updated
First need to make sure that the current document is one that is selected. Since there is no way to know if it is selected we need to move to the next selected document. The next block of code eleminates the blank value in the list of Note IDs lets set the NoteIDList with the newly selected document. Finally we can navigate through all the selected documents to gather all the documents that will be updated. The last step is to write to the status bar how many documents were found.{Store all Note IDs before manipulation in case field modifications cause categorized views or sorted columns to reorganize};
NoteIDList:=@Text(@NoteID);
@Command([NavNextSelected]);
@UpdateFormulaContext;
REM {Start Looping Selected documents};
@While((@Left(NoteIDList;NoteEntryLength)!=(@Text(@NoteID+ArraySeperator))) & (@Length(NoteIDList) <MaxSearchForSelectedDocs);
NoteIDList:=NoteIDList+ArraySeperator+@Text(@NoteID);
NoteIDList:=@ReplaceSubString(NoteIDList;CategoryNoteID+ArraySeperator;"");
@Command([NavNextSelected]);
@UpdateFormulaContext
);
NoteIDList:=@ReplaceSubString(NoteIDList;CategoryNoteID;"");
NoteIDList:=@Unique(@Explode(NoteIDList;ArraySeperator;@False));
@StatusBar("Found "+@Text(@Elements(NoteIDList))+" documents.");
NotNoteIDList:="";
NoteIDList:=@Text(@NoteID);
@Command([NavNextSelected]);
@UpdateFormulaContext;
REM {Start Looping Selected documents};
@While((@Left(NoteIDList;NoteEntryLength)!=(@Text(@NoteID+ArraySeperator))) & (@Length(NoteIDList) <MaxSearchForSelectedDocs);
NoteIDList:=NoteIDList+ArraySeperator+@Text(@NoteID);
NoteIDList:=@ReplaceSubString(NoteIDList;CategoryNoteID+ArraySeperator;"");
@Command([NavNextSelected]);
@UpdateFormulaContext
);
NoteIDList:=@ReplaceSubString(NoteIDList;CategoryNoteID;"");
NoteIDList:=@Unique(@Explode(NoteIDList;ArraySeperator;@False));
@StatusBar("Found "+@Text(@Elements(NoteIDList))+" documents.");
NotNoteIDList:="";
Update the field
For each document determine if it needs to be updated. Not all selected documents need to be updated becuase it could have moved in the view after it was updated. If there are any errors while updated the field then stop. If the max number of documents that can be navigated to is met then assume that some of the documents can not be found. This eliminates the possibility of an infinite loop.REM {Loop through selected docs taking each NoteIDList out of the list as it is processed};
DocUpdateCount:=0;
DocNavigationCount:=0;
@While(DocUpdateCount <@Elements(NoteIDList);
@If(@TextToNumber(@Text(@DocumentUniqueID))!=0;
@Do(
NoteIDList:=@Replace(NoteIDList;@NoteID;"");
NotNoteIDList:=NotNoteIDList : @NoteID;
@For(ef:=1;ef <= @Elements(EditField);ef:=ef+1;
formulaResult:=@Eval(DataTypeAction);
remark:=" **REM** The values entered above will be applied to all selected doc. If data conversion doesn't work then don't set field.";
@If(@IsError(formulaResult);
@Return(@Prompt([Ok];"Error";@Text(FormulaResult)));
@SetField(EditField[ef];formulaResult)
)
);
@If(DocNavigationCount >MaxUpdatedDocuments;
NoteIDList:="";
@Do(
DocUpdateCount:=DocUpdateCount+1;
@Command([NavNextSelected]);
@UpdateFormulaContext;
remark:=" **REM** If we haven't processed all docs yet but the current doc is not in the NoteIDList list, keep looping ... if cnt exceeds MaxUpdatedDocuments assume infinite loop and stop ";
@If(DocUpdateCount <@Elements(NoteIDList) & (!@Member(@NoteID;NoteIDList)) & (!@Member(@NoteID;NotNoteIDList));
@While((! @Member(@NoteID;NoteIDList) & DocNavigationCount <MaxUpdatedDocuments);
@Command([NavNextSelected]);
@UpdateFormulaContext;
DocNavigationCount:=DocNavigationCount+1);
"")
)
)
);
@Do(@Command([NavNextSelected]);
@UpdateFormulaContext)
)
);
DocUpdateCount:=0;
DocNavigationCount:=0;
@While(DocUpdateCount <@Elements(NoteIDList);
@If(@TextToNumber(@Text(@DocumentUniqueID))!=0;
@Do(
NoteIDList:=@Replace(NoteIDList;@NoteID;"");
NotNoteIDList:=NotNoteIDList : @NoteID;
@For(ef:=1;ef <= @Elements(EditField);ef:=ef+1;
formulaResult:=@Eval(DataTypeAction);
remark:=" **REM** The values entered above will be applied to all selected doc. If data conversion doesn't work then don't set field.";
@If(@IsError(formulaResult);
@Return(@Prompt([Ok];"Error";@Text(FormulaResult)));
@SetField(EditField[ef];formulaResult)
)
);
@If(DocNavigationCount >MaxUpdatedDocuments;
NoteIDList:="";
@Do(
DocUpdateCount:=DocUpdateCount+1;
@Command([NavNextSelected]);
@UpdateFormulaContext;
remark:=" **REM** If we haven't processed all docs yet but the current doc is not in the NoteIDList list, keep looping ... if cnt exceeds MaxUpdatedDocuments assume infinite loop and stop ";
@If(DocUpdateCount <@Elements(NoteIDList) & (!@Member(@NoteID;NoteIDList)) & (!@Member(@NoteID;NotNoteIDList));
@While((! @Member(@NoteID;NoteIDList) & DocNavigationCount <MaxUpdatedDocuments);
@Command([NavNextSelected]);
@UpdateFormulaContext;
DocNavigationCount:=DocNavigationCount+1);
"")
)
)
);
@Do(@Command([NavNextSelected]);
@UpdateFormulaContext)
)
);
Display results
If there are any documents that have not been updated the write the Note ID(s) to the status bar. Also update the status bar with the number of documents that were navigated through and the number of documents updated. That's it.@If(@Implode(@Unique(@Explode(NoteIDList;ArraySeperator;@False)))!="";@StatusBar("Unable to update the following documents: "+@Implode(@Unique(@Explode(NoteIDList;ArraySeperator;@False));", ")+".");"");
@StatusBar("Navigated through "+@Text(DocUpdateCount+DocNavigationCount)+" documents.");
@StatusBar("Performed '"+DataType+"' for '"+@Implode(EditField;", ")+"' field"+@If(@Elements(EditField) >1;"s ";" ")+"on "+@Text(DocUpdateCount)+" document"+@If(DocUpdateCount >1;"s";"")+".")
@StatusBar("Navigated through "+@Text(DocUpdateCount+DocNavigationCount)+" documents.");
@StatusBar("Performed '"+DataType+"' for '"+@Implode(EditField;", ")+"' field"+@If(@Elements(EditField) >1;"s ";" ")+"on "+@Text(DocUpdateCount)+" document"+@If(DocUpdateCount >1;"s";"")+".")
Download all the code







Comments
@Eval(@Prompt([OkCancelEdit]; "Formula Document Update"; "Please enter the formula to evaluate against the currently highlighted document"; ""))
I can then enter things like this into the prompt box
FIELD dateCreated := @Now; FIELD Creator := @Username; FIELD Subject := @ProperCase(Subject) + " plus extra subject text"
Which allows multiple fields to be updated, with a lot of flexibility of how the field updates are done. Perhaps this could be another option for your code? In other words, allow the user to enter a formula without specifying a field name or field value, and let the user supplied formula do all the work.
Posted by Jim Fricker At 03:23:00 PM On 11/16/2005 | - Website - |
I removed the explode from these two lines with no apparent change in the way it works
"CommonName Multi Value|@Name( [CN]; RawValue )$"
"Abbreviate Name Multi Value|@Name( [Abbreviate]; RawValue )$"
Great piece of work Chad!! Many thanks for sharing this.
Posted by Jim Fricker At 06:45:22 PM On 11/14/2005 | - Website - |
Posted by Chad Schelfhout At 11:35:19 AM On 11/22/2005 | - Website - |
I've a couple of suggestions, and I'd like to pass along [below] my copy of the code (not nearly as fully featured as yours) to see if it offers any ideas. I hope this form permits this much text.
1. Would it be possible to add the ability to set a field to the Authors or Readers attribute?
2. It's a wonderful help for the macro to show the current contents of the field, allowing one to edit that. Perhaps this doesn't fit nicecly within the concept of multiple documents selection?
Another feature of my version of the code is to try to minimize the number of dialog boxes, but - again - this might not suite what you're code accomplishes.
Thanks for sharing all your good development work. --David
REM {SmartIcon formula to edit field of document selected in view.};
REM {Requires R6+};
REM {2004-05-09 dmp Add file name browsing. Improve error handling.};
REM {2004-05-07 dmp Add features from Matt Holthe of Breaking Par, http://www.breakingpar.com/bkp/home.nsf/Doc!OpenNavigator&87256B280015193F87256E3700680660};
REM {2003-11-20 dmp Handle display of lists and rich text.};
REM {2003-09-02 Dave Lehman, "Set field value/type via smart icon, Version2" http://searchdomino.techtarget.com/search?query=dave+lehman};
REM {2002-12-30 dmp Change list separator to vertical bar. Remember last used field name.};
REM {2001-09-04 Janet Rogers, "Set field value/type via smart icon" http://searchdomino.techtarget.com/search?query=janet+rogers};
REM {2001-04-13 Ken Pespisa, "Change field values from a view" http://searchdomino.techtarget.com/search?query=ken+pespisa};
REM {Get field name.};
field_used_last := @Environment ("SI_FieldEdit_FieldUsedLast");
field_names := @DocFields;
@If ("" = field_names;
@Do (@Prompt ([Ok]; "Canceled"; "No document selected."); @Return (""));
""
);
field_selected := @Prompt ([OkCancelEditCombo]; "Field Edit Selection";
"For document selected in a view:" + @Char (13) +
"select field to change or delete," + @Char (13) +
"or type new field name."; field_used_last; field_names);
REM {Save name of field used last.};
@SetEnvironment ("SI_FieldEdit_FieldUsedLast"; field_selected);
REM {Get old field value.};
old_field_value := @Eval (@Text (field_selected));
REM {Handle possible error value.};
@If (@IsError (old_field_value);
old_field_value := @Text (old_field_value);
""
);
REM {Get current data type.};
old_data_type := @If (
@IsNumber (old_field_value); "Number";
@IsTime (old_field_value); "Date";
"Text"
);
REM {Prepare multi-line/non-text fields for display in prompt.};
old_field_value := @ReplaceSubstring (@Text (old_field_value); @NewLine; " ");
data_types := "DELETE field" : "Canonical name" : "Date" :
"File name" : "Number" : "Password" : "Text";
type_selected := @Prompt ([OkCancelList]:[NoSort]; "Field Data Type";
"Select data type."; old_data_type; data_types);
REM {Get list separator.};
default_separator := @If (
(1 = @Count (old_field_value)) |
@Contains (type_selected; "DELETE" : "File" : "Password");
"None";
@Environment ("SI_FieldEdit_SeparatorUsedLast")
);
separator_choices := "None" : "?" : ";" : "\\" : "|" : "~" : "+";
list_separator := @Prompt ([OkCancelList]:[NoSort]; "List Separator Selection";
"Optional: choose character to separate list values.";
default_separator; separator_choices);
@If ("None" = list_separator;
list_separator := "";
@SetEnvironment ("SI_FieldEdit_SeparatorUsedLast"; list_separator)
);
old_field_value := @Implode (old_field_value; list_separator);
REM {Get new value.};
new_value := @If (
"DELETE field" = type_selected; "";
"Canonical name" = type_selected; @PickList ([Name]);
"File name" = type_selected;
@Prompt ([LocalBrowse]; "File Name Selection"; "3");
"None" = list_separator;
@Prompt ([OkCancelEdit]; "New Value"; "Enter new " + field_selected +
" value."; old_field_value);
@Prompt ([OkCancelEdit]; "New Value"; "Enter new " + field_selected +
" values separated by " + list_separator + " character.";
old_field_value)
);
@If ("None" != list_separator;
new_value := @Explode (new_value; list_separator);
""
);
REM {Use plain text if data conversion fails.};
conversion := @If (
@Contains (type_selected; "Date"); "@TextToTime (";
@Contains (type_selected; "Name"); "@Name ([Canonicalize]; ";
@Contains (type_selected; "Number"); "@TextToNumber (";
@Contains (type_selected; "Password"); "@Password (";
"@Text ("
);
converted_value := @Eval (conversion + "new_value)");
new_value := @If (
"DELETE field" = type_selected; @DeleteField;
@IsError (converted_value); @Do (
@Prompt ([Ok]; "Error"; "Check field for data type error.");
new_value
);
converted_value
);
@SetField (field_selected; new_value)
REM {end}
Posted by David Phillips At 05:33:43 PM On 09/02/2005 | - Website - |
On this one, could you not simply use a dialog box with a single field on it and return it to the calling form? I have an application in which I am doing this with LS, but a formula implementation would not be difficult and would remove the limitation on characters.
Posted by Joel Phelan At 08:46:52 AM On 11/22/2005 | - Website - |
I'm not sure if you are aware but Notes formula has a notation for literal lists which applies to text, number and date lists. You just separate values with a colon. So instead of this code.
@If( DataType = @Explode( "Implode;Explode"; ArraySeperator ); ...etc...
You can write
@If( DataType = "Implode" : "Explode" ; ...etc...
Which is more compact and I think easier to read.
This notation applies to many places in the code including the assignment of the DataTypesCombo variable right at the start. No need for the @Explode and all those $ characters.
Jim
PS. Minor point but seperator is spelt separator.
Posted by Jim Fricker At 09:25:54 PM On 02/19/2006 | - Website - |
Posted by Ashish Sidapara At 10:20:09 PM On 08/10/2005 | - Website - |
Posted by Jim Fricker At 04:53:30 PM On 02/22/2006 | - Website - |
As for the adding of a basic formula option I have been pondering that for some time, just struggling on the implementation. The execution of just a formula almost fits with the toolbar functions http://www.chadsmiley.com/toolbarFunctions better, but it also edits fields. So the dilemma.
Posted by Chad Schelfhout At 09:47:55 AM On 11/19/2005 | - Website - |
Just curious what you are looking to add. Maybe everyone could benefit from it.
Posted by Chad Schelfhout At 06:50:37 AM On 02/23/2006 | - Website - |
I would agree that it would make the code easier to read, but my goal is to make this to run as fast as possible since it can run over many documents. Thanks for the suggestion and the spelling correction.
Posted by Chad Schelfhout At 06:46:49 PM On 02/20/2006 | - Website - |
I use the current contents of a field all the time. My favorite is to select multiple documents then move to the document that has the value I want to use then run the macro. This removes any typing mistakes and is less typing.
I did find one feature that I don't have and that is the check to see if a document is selected. I like that. I will also see if any more stream lining can be done with the dialog boxes.
Posted by Chad Schelfhout At 11:05:00 PM On 09/06/2005 | - Website - |