2007-11-08

Notes: When a space is not a space

Symptoms:

A field that receives numbers (either is a number field or is a text field that receives input from a number field on a dialog box), contains spaces (ex: 1 000 or 27 000) that can't be removed with a @ReplaceSubstring(field_name; " "; "").




Cause:


The number field has the "Punctuated at thousand" option set, which, in some user settings, causes the insertion of a space within the number. But this space is not the common ASCII character 32, but a special one: the ASCII character 160.






Solution:


Use this code:


sSpace1:=@char(32);
sSpace2:=@char(160);
sNumber:=@ReplaceSubstring(field_name; sSpace1; "");
sNumber:=@ReplaceSubstring(sNumber; sSpace2; "");

2007-10-25

Notes: Insert a DocLink into a Rich Text Field while editing the document (without the need of saving it)

There's a lotus script solution for this but it will cause a Replication Conflict if the document locking functionality is active on the database.

So, an alternative is to have a page with an embedded view showing the documents list from where the user will choose the document to link to, and then call this page on a dialog box.
On the page's QueryClose event, add this:

@Command([EditMakeDocLink])

And on the form where the RichTextField is, add a button or an Action with a formula similar to this:

@DialogBox( "pg_name" ; [AutoHorzFit] : [AutoVertFit] : [NoNewFields] :
[NoFieldUpdate] : [SizeToTable] : [ReadOnly] ; "Insert Doc Link" );
@Do
( @PostedCommand([EditGotoField];"rtf_field_name");
@PostedCommand([EditInsertText]; @NewLine);
@PostedCommand([EditInsertText]; "Document link: ");
@PostedCommand([EditInsertText]; @NewLine);
@PostedCommand([EditInsertText]; @NewLine);
@PostedCommand([EditLeft];"2"); @PostedCommand([EditPaste]))

Notes: View Entry's ColumnValues returns an array for a specific column

This will happen if the column's property "Show multiple values as selected entries" is selected.

In this case the ColumnValues property of the NotesViewEntry class, will return an Array and the common way of obtaining it's value will fail.

To access the entry's column value weather it's an array or not, use this:


Function getColValue (vCol As Variant) As String
If Isarray(vCol) Then
getColValue = Cstr(vCol(0))
Else
getColValue = Cstr(vCol)
End If
End Function

And then use it like this:

stringValue = getColValue(vwEntry.ColumnValues(1))

2007-10-03

Notes: Scheduled agents on server

If you want to run (for troubleshoot purposes, for instance) an agent directly on a server, with the server's id, and you have no access to the machine, just run it from the web:

http://your_server/your_db_path/your_db/your_agent?openAgent

Now just go to the server's log to check for debug and/or error messages.

2007-08-02

Notes: Change the size of PicklistCollection dialog box

(from a notes forum post by Sam Chung)

«I have found out the ini for resize PickList Window:
"WindowSizePickKeywords"

However, the parameter is found in Notes6 rather than 5, so chk version before writing it.

You can enlarge the PickList Window size before calling @PickList and restore it after calling

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub AssignPickWinSize

'To enlarge the PickList WIndow Size by setting system Notes.INI
'Backup the sys one with the same name as a user setting($)
On Error Goto ErrH
Const delim = " "
Const key="WindowSizePickKeywords"
Dim tmpstr As String

Set ss = New NotesSession
'get the system variable
tmpstr=Trim(ss.GetEnvironmentString(key, True))
If Len(tmpstr) Then
'BackUp the environment var
Call ss.SetEnvironmentVar(key, tmpstr, False )

tmpArr= Fulltrim(Split(tmpstr, delim))

tmpArr(2)="1000"
tmpArr(3)="900"

tmpstr=Join(tmpArr, " ")
'Set
Call ss.SetEnvironmentVar(key, tmpstr, True)
End If
Exit Sub
ErrH:
Print "Err in setting picklist window size."
End Sub


"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""

Sub RestorePickWinSize

'Restore the backup setting from the same name user setting($) in ini
On Error Goto ErrH
Const key="WindowSizePickKeywords"
Dim ss As NotesSession
Dim tmpstr As String
Set ss = New NotesSession

tmpstr=Trim(ss.GetEnvironmentString(key, False))
If Len(tmpstr) Then
'Reset the system
Call ss.SetEnvironmentVar(key, tmpstr , True )
'Clear the user one
Call ss.SetEnvironmentVar(key, "" , False )
End If

ErrH:

End Sub »

2007-05-23

Notes: a Smart Icon to update a field in a document

Many times there's the need to change a field's value to make a test or correct something. What can be more useful than a button sitting on the Smart Icon tab, with a formula to do just that?
There's many ways to accomplish this. From the simplest to the most complex:

Example 1:

FieldToChange := @Prompt([OkCancelEdit]; "Field to Change"; "Enter the name of the field to change."; "");
TypeOfData := @Prompt([OkCancelList]:[NoSort]; "Type of Field"; "Is this field Text, Number or DateTime?"; "Text"; "Text":"Number":"DateTime");
NewValue:=@Prompt([OkCancelEdit];"New Value";"Enter the new value for this field";"");

@If(FieldToChange=""|TypeOfData="";@Do(@Prompt([Ok];"Error";"You did not enter all requested data. Process halted.");@Return(""));"");

NumberValue:=@TextToNumber(NewValue);
valueerror:=@IsError(@If(TypeOfData="Number";NumberValue;3));
@If(valueerror=1;
@Do(@Prompt([Ok];"Error";"You did not specify a number for the new value. Process halted.");@Return(""));
"");

DateError:=@If(TypeOfData="DateTime";@If(@Text(@TextToTime(NewValue))="";1;0);0)
;
@If(DateError=1;
@Do(@Prompt([Ok];"Error";"You did not specify a date/time for the new value. Process halted.");@Return(""));
"");

continue := @Prompt([YesNo];"Click yes to continue.";"This agent will change the value of the *"+ FieldToChange +"* field on the selected documents to *"+ NewValue + "* . Continue?");

@If(continue=1;"";@Return(""));

@SetField(FieldToChange;
@If(TypeOfData="Text";NewValue;
TypeOfData="Number";@TextToNumber(NewValue);
@TextToTime(NewValue)));

@All


Example 2:
(from: Datatribe Softwerks)

strUNID := @Text(@DocumentUniqueID);

strFieldName := @Prompt([OkCancelEditCombo]; "Change Field Value"; "To change an existing Field, select the Field name." +
@Char(13) + "To create a new Field, enter the Field name." ;""; "":@DocFields);

strTmpCurrentValue := @If( @IsAvailable(strFieldName); @GetDocField(strUNID; strFieldName); "" );
strCurrrentValue := @If( @IsError(strTmpCurrentValue); ""; @Text(strTmpCurrentValue) );

strNewValue := @Prompt([OkCancelEdit]; "Change Field Value"; "Enter New Field Value: use semicolon (;) to separate multiple values for mulitvalue fields."; @Implode(strCurrrentValue;";"));

strFieldType := @Prompt([OkCancelList]; "Change Field Value"; "Select the Field Data Type";
"Text"; "Text" : "Time" : "Number" : "Text List" : "Number List" : "Time List":"Delete Field");

@If(

strFieldType = "Delete Field";
@SetField(strFieldName; @DeleteField);

strFieldType = "Time";
@SetField(strFieldName; @TextToTime(strNewValue));

strFieldType = "Number";
@SetField(strFieldName; @TextToNumber(strNewValue));

strFieldType = "Text List";
@SetField(strFieldName; @Trim(@Explode(strNewValue;";")));

strFieldType = "Number List";
@SetField(strFieldName; @TextToNumber(@Explode(@Trim(@ReplaceSubstring(strNewValue;" ";""));";")));

strFieldType = "Time List";
@SetField(strFieldName; @TextToTime(@Explode(strNewValue;";")));

@SetField(strFieldName; @Text(strNewValue))
)
Example 3:
(from: Datatribe Softwerks's comments)

REM {Get a listing of all the fields on the current document};
List := @DocFields;
PromptLimit := 254;

REM {Possible data types to choose from.};
REM {I called Number Integer because use keyboard to select what you want with keyboard quicker.};
DataTypes := "Text" : "Date" : "Integer" : "Password" : "Name" : "Common Name" : "Abbreviate Name" : "Remove Field" : "Text Multi Value" : "Date Multi Value" : "Integer Multi Value" : "Name Multi Value" : "Common Name Multi Value" : "Abbreviate Name Multi Value" : "Upper Case Text" : "Lower Case Text" : "Proper Case Text" : "Upper Case Text Multi Value" : "Lower Case Text Multi Value" : "Proper Case Text Multi Value" ;

REM {Prompt for which field needs to be updated.};
EditField := @Prompt( [OkCancelList] ; @DbTitle + " - " + @ViewTitle ; "Select the field you wish to alter:" ; "Subject" ; List ) ;

REM {Prompt for which data type you would like the data to be};
REM {This needs to be done before value prompt to determine if the Picklist or any prompting needs to be used.};
DataType := @Prompt( [OkCancelList] : [NoSort] ; EditField + " - " + @DbTitle + " - " + @ViewTitle; "Please select the correct data type or action for field: " + EditField + "."; "Text" ; DataTypes );

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 = "Remove Field" ; "" ;
@Contains( DataType ; "Multi Value" ); @Prompt( [OkCancelEdit] ; EditField + " - " + @DbTitle + " - " + @ViewTitle; "Please enter the new desired value for: " + EditField + "." + @Char(13) + @Char(13) + "Seperated with ; for each value." ; @Abstract([TextOnly] ; PromptLimit ; "" ; @Text( EditField ) ) );
@Prompt( [OkCancelEdit] ; EditField + " - " + @DbTitle + " - " + @ViewTitle ; "Please enter the new desired value for: " + EditField + "." ; @Abstract([TextOnly] ; PromptLimit ; "" ; @Text( EditField) ) ) );

REM {If data conversion doesn't work then don't set field.};
@If(
DataType = "Date" ; @If( @SetField( EditField ; @TextToTime( RawValue ) ) );
DataType = "Integer" ; @If( @IsError( @TextToNumber( RawValue ) ) ; "" ; @SetField( EditField ; @TextToNumber( RawValue ) ) ) ;
DataType = "Common Name" ; @SetField( EditField ; @Name( [CN]; RawValue ) ) ;
DataType = "Abbreviate Name" ; @SetField( EditField ; @Name( [Abbreviate]; RawValue ) ) ;
DataType = "Password" ; @SetField( EditField ; @Password( RawValue ) ) ;
DataType = "Remove Field" ; @SetField( EditField ; @DeleteField ) ;
DataType = "Text Multi Value" ; @SetField( EditField ; @Explode( RawValue ; ";" ) ) ;
DataType = "Date Multi Value" ; @SetField( EditField ; @TextToTime( @Explode( RawValue ; ";" ) ) ) ;
DataType = "Integer Multi Value" ; @If( @IsError( @TextToNumber( @Explode( RawValue ; ";" ) ) ) ; "" ; @SetField( EditField ; @TextToNumber( @Explode( RawValue ; ";" ) ) ) ) ;
DataType = "Name Multi Value" ; @SetField( EditField ; @Explode( RawValue ; ":" ) ) ;
DataType = "Common Name Multi Value" ; @SetField( EditField ; @Name( [CN]; @Explode( RawValue ; ":" ) ) );
DataType = "Abbreviate Name Multi Value" ; @SetField( EditField ; @Name( [Abbreviate]; @Explode( RawValue ; ":" ) ) );
DataType = "Upper Case Text" ; @If( @SetField( EditField ; @UpperCase( RawValue ) ) );
DataType = "Lower Case Text" ; @If( @SetField( EditField ; @LowerCase( RawValue ) ) );
DataType = "Proper Case Text" ; @If( @SetField( EditField ; @ProperCase( RawValue ) ) );
DataType = "Upper Case Text Multi Value" ; @If( @SetField( EditField ; @UpperCase( @Explode( RawValue ; ";" ) ) ) );
DataType = "Lower Case Text Multi Value" ; @If( @SetField( EditField ; @LowerCase( @Explode( RawValue ; ";" ) ) ) );
DataType = "Proper Case Text Multi Value" ; @If( @SetField( EditField ; @ProperCase( @Explode( RawValue ; ";" ) ) ) );
@SetField( EditField ; RawValue )
);
""
Example 4:
(from: ChadSmiley Blog)

REM {Edit Document Fields 6.0.0 by Chad Schelfhout.};
REM {Visit http://www.chadsmiley.com/EditDocumentFields for the latest updates};

REM {Changable constants};
cEnableConfirmation := @True;
cFieldHistoryValues := 10;
cStandardSeparators := ":" : ";" : " ";
cPromptTitle := @DbTitle + " - " + @ViewTitle;

REM {Unchangable constants};
cProfileName := "ChadSmiley Tools";
cEditLastField := "edfLastField";
cEditLastFieldDataType := "edfLastFieldDataType";
cEditLastFieldDataTypeValue := "edfLastFieldDataTypeValue";
cEditLastSeparator := "%~%";
cValueListSeparator := "^";
cFromRawValueSeparator := "%@%";
cSemicolonReplace := "#SC#";
cMaxSearchForSelectedDocs := 5520;
cMaxUpdatedDocuments := 1000;
cArraySeparator := ";";
cNoteEntryLength := 11;
cPromptNewLineOne := @Char(13);
cPromptNewLineTwo := cPromptNewLineOne + cPromptNewLineOne;
cPromptTab := @Char(9);
cCategoryNoteID := "NT00000000";
cTextExtractList := "Text Left":"Text Left Back":"Text Right":"Text Right Back";
cNoPromptList := cTextExtractList:"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":"Trim":"Trim then Unique";
cErrorCheckCode := "@Implode( @Unique( @Explode( NoteIDList : ErrorNoteID ; cArraySeparator ; @False ) ) )";
cErrorInformation := "\"Error documents: \" + @Implode( @Unique( @Explode( ErrorNoteIDList ; cArraySeparator ; @False ) ) ; \", \" ) + cPromptNewLineOne + \"Not updated documents: \" + @Implode( @Unique( @Explode( ErrorNoteIDList ; cArraySeparator ; @False ) ) ; \", \" )";


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) )$"+
"Text Left|@Left( @Text( @GetField( EditField[ef] ) ) ; ExtractValue )$"+
"Text Left Back|@LeftBack( @Text( @GetField( EditField[ef] ) ) ; ExtractValue )$"+
"Text Right|@Right( @Text( @GetField( EditField[ef] ) ) ; ExtractValue )$"+
"Text Right Back|@RightBack( @Text( @GetField( EditField[ef] ) ) ; ExtractValue )$"+
"Trim|@Trim( @Text( @GetField( EditField[ef] ) ) )$"+
"Trim then Unique|@Unique(@Trim( @Text( @GetField( EditField[ef] ) ) ) )$"+
"Name|RawValue$"+
"Name Multi Value|RawValue$"+
"Common Name|@Name( [CN]; RawValue )$"+
"Common Name 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 );

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( cProfileName ; cEditLastField ; @UserName ) ; cArraySeparator ; @True ) ;

REM {Get the list of forms and field that was updated using Edit Document Fields};
FieldListForms := @Word( FieldList ; cEditLastSeparator ; 1 );
FieldListField := @Word( FieldList ; cEditLastSeparator ; 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 :="" );

REM {Prompt for which field needs to be updated. Loop until a field is selected or 'Cancel' is selected};
@DoWhile(
EditField := @Prompt( [OkCancelEditCombo] ; cPromptTitle ; "Select the field you wish to alter or enter a new field to add:" ; LastField ; @Trim( @Unique( List : LastField ) ) );
EditField = "" );
EditFieldPromptTitle := "Change '" + EditField + "' in " + cPromptTitle;

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( cProfileName ; cEditLastFieldDataType ; @UserName ) ; cArraySeparator ; @True ) ;
FormFieldListFormField := @Word( FormFieldList ; cEditLastSeparator ; 1 );
FormFieldListDataType := @Word( FormFieldList ; cEditLastSeparator ; 2 );
FormFieldListFormulaCode := @ReplaceSubstring( @Word( FormFieldList ; cEditLastSeparator ; 3 ) ; cSemicolonReplace ; ";" );
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" );

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 ) ];

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 ) ) ; cArraySeparator );
@Text( @GetField( EditField ) ) )
);

REM {This will allow the retrieval of history of values of the field. Data is stored like Form+Field+DataType%~%ValueList.};
FormFieldListDataTypeValues := @Explode( @GetProfileField( cProfileName ; cEditLastFieldDataTypeValue ; @UserName ) ; cArraySeparator ; @True ) ;
FormFieldListFormFieldDataType := @Word( FormFieldListDataTypeValues ; cEditLastSeparator ; 1 ) ;
FormFieldListValuesLists := @Word( FormFieldListDataTypeValues ; cEditLastSeparator ; 2 ) ;
FormFieldListDTIndex := @Member( Form + EditField + DataType; FormFieldListFormFieldDataType );
@If( FormFieldListDTIndex > 0;
@Do( FormFieldListValuesList := FormFieldListDataTypeValues[ FormFieldListDTIndex ];
FormFieldListValuesList := @ReplaceSubstring( @Trim( @Explode( FormFieldListValuesLists[ FormFieldListDTIndex ] ; cValueListSeparator ; @False ) ) ; cSemicolonReplace; ";" );
FormFieldListDataTypeValues := @ReplaceSubstring( FormFieldListDataTypeValues ; FormFieldListDataTypeValues[ FormFieldListDTIndex ] ; "" ) );
FormFieldListValuesList :="" );

REM {Prompt for additional fields and determine the string that they are searching for.};
@If( DataType = ("Replace Substring":"Replace" ) ;
@Do( EditField := @Unique( EditField : @Prompt( [OkCancelListMult] ; cPromptTitle ; "Select any addtional fields you wish to alter:" ; EditField ; List ) );
FromRawValue := @Prompt( [OkCancelEditCombo] ; EditFieldPromptTitle ; "Enter or select the text (case sensitive) to search for in: " + @Implode( EditField ; ", " ) + "." ; "" ; @Unique( @Word( FormFieldListValuesList ; cFromRawValueSeparator ; 2 ) ) ) );
@Do( EditField := EditField;
FromRawValue := "" )
);

REM { With the Edit combo there will be a list of standard seperators to choose from.};
Separator := @If( DataType = ("Implode":"Explode" ) ;
@Prompt( [OkCancelEditCombo] ; cPromptTitle ; "Enter or select the " + @If( DataType = "Implode" ; "separator" ; "separators" ) + ":" ; "" ; @Unique( @If( FormFieldListValuesList = "" ; cStandardSeparators ; FormFieldListValuesList : cStandardSeparators ) ) );
cArraySeparator );

REM {Determine the string to search for};
ExtractValue := @If( DataType = cTextExtractList ;
@Prompt( [OkCancelEditCombo] ; cPromptTitle ; "Enter or select the search string or string length:" ; @Subset( FormFieldListValuesList ; 1 ) ; @Unique( FormFieldListValuesList ) );
"" );

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 = ( cNoPromptList ) ; "" ;
@Contains( DataType ; "Multi Value" ) ; @Prompt( [OkCancelEditCombo] ; EditFieldPromptTitle; "Enter or select the new desired value for: " + @Implode( EditField ; ", " ) + "." + cPromptNewLineTwo + "Seperated with ; for each value." ; OriginalValue ; @Unique( OriginalValue : FormFieldListValuesList ) ) ;
@Contains( DataType ; "+ Append Values" ) ; @Prompt( [OkCancelEditCombo] ; EditFieldPromptTitle; "Enter or select values to append: " + @Implode( EditField ; ", " ) + "." + cPromptNewLineTwo + "Seperated with ; for each value." ; "" ; @Unique( FormFieldListValuesList ) ) ;
DataType = ("Replace Substring":"Replace" ) ; @Prompt( [OkCancelEditCombo] ; EditFieldPromptTitle ; "Enter or select the text to repalce with in: " + EditField + "." ; "" ; @Unique( @Word( FormFieldListValuesList ; cFromRawValueSeparator ; 1 ) ) ) ;
DataType = "Formula" ; @Do( @DoWhile(
OriginalValue := @Prompt( [OkCancelEditCombo] ; EditFieldPromptTitle ; "Enter or select the new desired formula for: " + EditField + "." ; OriginalValue ; @Unique( OriginalValue : FormFieldListValuesList ) );
tempReturnCheck := @CheckFormulaSyntax( OriginalValue );
@If( tempReturnCheck != "1"; @Prompt( [Ok] ; "Invalid Formula - " + EditFieldPromptTitle ;
"Invalid Formula entered: " +
cPromptNewLineTwo + cPromptTab + "Error: " + cPromptTab + cPromptTab + @Text( tempReturnCheck ) +
cPromptNewLineOne + cPromptTab + "Formula: " + cPromptTab + cPromptTab + OriginalValue ) ; "" );
tempReturnCheck != "1" );
OriginalValue );
@Prompt( [OkCancelEditCombo] ; EditFieldPromptTitle ; "Enter or select the new desired value for: " + EditField + "." ; OriginalValue ; @Unique( OriginalValue : FormFieldListValuesList ) )
);

REM {Store Field in Profile doc};
@SetProfileField( cProfileName ; cEditLastField ; @Unique( @Trim( FieldList : ( Form + cEditLastSeparator + EditField[1] ) ) ); @UserName );

REM {Store Data Type of Field in Profile doc};
@SetProfileField( cProfileName ; cEditLastFieldDataType ;
@Unique( @Trim( FormFieldList : ( Form + EditField[1] + cEditLastSeparator + DataType + cEditLastSeparator +
@ReplaceSubstring(
@If( DataType = "Formula" ; RawValue ; FormFieldListFormulaCode ) ;
";" ; cSemicolonReplace ) ) ) ) ;
@UserName );

REM {Store Data Value of Field in Profile doc};
@SetProfileField( cProfileName ; cEditLastFieldDataTypeValue ;
@Unique( @Trim( FormFieldListDataTypeValues : ( Form + EditField[1] + DataType + cEditLastSeparator +
@Implode(
@Subset(
@Unique(
@ReplaceSubstring(
@If( DataType = ("Implode":"Explode" ) ; Separator ;
DataType = cTextExtractList ; ExtractValue ;
DataType = ( "Replace Substring":"Replace" ) ; RawValue + cFromRawValueSeparator + FromRawValue ;
RawValue ) : FormFieldListValuesList ;
";" ; cSemicolonReplace ) ) ;
cFieldHistoryValues );
cValueListSeparator ) ) ) ) ;
@UserName );REM {If multi docs selected, only process those checked ... an unchecked doc cannot be NavNextSelected};
@Command([NavNextSelected]);
@UpdateFormulaContext;

REM {Store all Note IDs before manipulation in case field modifications cause categorized views or sorted columns to reorganize};
NoteIDList := @Text( @NoteID );
ErrorNoteIDList := "";
@Command([NavNextSelected]);
@UpdateFormulaContext;

REM {Start Looping Selected documents to gather all the documents that need to be updated.};
@While( ( @Left( NoteIDList ; cNoteEntryLength ) != ( @Text( @NoteID + cArraySeparator ) ) ) & ( @Length( NoteIDList ) <> 1 ; "s" ; "" ) + "." + cPromptTab + cPromptTab +
cPromptNewLineTwo + cPromptTab + "Field:" + cPromptTab + cPromptTab + EditField +
cPromptNewLineOne + cPromptTab + "Data type/action: " + cPromptTab + DataType +
cPromptNewLineOne + cPromptTab +
@If( DataType = ("Implode":"Explode" ) ; "Separator: " + cPromptTab + Separator ;
DataType = ("Text Left":"Text Left Back":"Text Right":"Text Right Back" ) ; "Search string: " + cPromptTab + ExtractValue ;
DataType = ("Replace Substring":"Replace" ) ; "Search string: " + cPromptTab + FromRawValue + cPromptNewLineOne + cPromptTab + "Replace string: " + cPromptTab + RawValue ;
DataType = cNoPromptList;
"" ;
"Value: " + cPromptTab + cPromptTab + @Text( RawValue ) ) +
cPromptNewLineTwo + "Would you like to continue?" );
@If( cEnableConfirmation ;
@Do(
@StatusBar( @Explode( tmpPrompt ; cPromptNewLineOne ; @True ) );
@If( @Prompt( [YesNo]; EditFieldPromptTitle ;
tmpPrompt );
"" ; @Return ( "" ) ) );
"" );

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 ); @Do( tmpPrompt := "Error with NoteID of " + @NoteID + ". Continue?" + cPromptTab + cPromptNewLineOne + cPromptTab + "Error: " + cPromptTab + cPromptTab + @Text( FormulaResult ) + cPromptNewLineOne + cPromptTab + "Formula: " + cPromptTab + cPromptTab + DataTypeAction + cPromptNewLineOne + cPromptTab + @If( @Contains( DataTypeAction ; "EditField[ef]" ) ; "EditField[ef]:" ; "Field:" + cPromptTab ) + cPromptTab + EditField[ef] + cPromptNewLineOne + cPromptTab + "Data type/action: " + cPromptTab + DataType + cPromptNewLineOne + cPromptTab + @If( DataType = ("Replace Substring":"Replace" ) ; "FromRawValue: " + cPromptTab + @Text( FromRawValue )+ cPromptNewLineOne + cPromptTab + "RawValue: " + cPromptTab + @Text( RawValue ) ; DataType = cNoPromptList; "" ; "RawValue: " + cPromptTab + @Text( RawValue ) ); @StatusBar( @Explode( tmpPrompt ; cPromptNewLineOne ; @True ) ); @If( @Prompt( [YesNo] ;"Error - " + EditFieldPromptTitle ; tmpPrompt ) ; ErrorNoteIDList := ErrorNoteIDList+ cArraySeparator + @Text( @NoteID ); @Return( @If( @Eval( cErrorCheckCode ) != "" ; @StatusBar( @Explode( @Eval( cErrorInformation ) ; cPromptNewLineOne ; @True ) ): @Prompt( [Ok] ; "Unable to Update - " + EditFieldPromptTitle ; @Eval( cErrorInformation ) ); "" ) ) ) ); @SetField( EditField[ef] ; formulaResult ) ) ); @If( DocNavigationCount > cMaxUpdatedDocuments ;
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 <> 1; "s " ; " " ) + "on " + @Text( DocUpdateCount ) + " document" + @If( DocUpdateCount > 1 ; "s" ; "" ) + "." )



2007-05-05

Notes: How to update rich text in a document that's open and redisplay it without saving

«The code sample below shows how to write LotusScript code that makes changes to a rich text field in a document that the user is editing, and displays those changes immediately on-screen, without saving the changes. This also works if you need to repeat other operations that only occur when a document is opened, e.g. evaluating section editor formulas and computed subform formulas.

Here's the code, which should work in Notes 5.0.2 and higher:

Dim wksp As New NotesUIWorkspace
Dim session As New NotesSession
Dim uidoc As NotesUIDocument, uidocNew As NotesUIDocument
Dim doc As NotesDocument
Dim rti As NotesRichTextItem
Dim strFieldname As String

Set uidoc = wksp.CurrentDocument
uidoc.Refresh True ' do this if the rich text field is editable, to get the current contents in case user has modified them.
Set doc = uidoc.Document ' get the back-end document for the document open on screen.
strFieldname = uidoc.CurrentField ' remember the current field if any
Set rti = doc.GetFirstItem("fieldname") ' insert your fieldname here, generally "Body"

' Make your rich text changes here, for instance:
Call rti.AddNewLine(1, True)
Call rti.AppendText(Now & ": log entry.")
If session.NotesBuildVersion >= 190 Then
rti.Update ' ND6 only
Else
Call doc.ComputeWithForm(True, False) ' caution, as this may erase some field values if you have @Db functions in formulas.
End If

doc.SaveOptions = "0" ' make it possible to close the document without a "do you want to save" prompt. If this is a mail-in doc you may need to set MailOptions="0" also to avoid being prompted.
Call uidoc.Close(True)
Set uidocNew = wksp.EditDocument(True, doc, , , , True)
Delete uidoc
uidocNew.Document.RemoveItem("SaveOptions")
If strFieldname <> "" Then uidocNew.GotoField(strFieldname) ' return focus to field that was current before.

Note: this will cause Queryclose, Queryopen, Postopen (and so on) form events to trigger. Also, uidoc.Refresh will execute computed field formulas and input validations, so you should write the validation formulas to not return @Failure unless @IsDocBeingSaved | @IsDocBeingSent is true.»



in Notes/Domino 6 and 7 Forum

Notes: Design Synopsis in HTML format

«(...) But also, the Design Synopsis has never worked very well for large
designs ("Too many paragraphs") or for large blocks of LotusScript , whose code
does not appear in full. The most important limitation of the Design Synopsis,
though, in my estimation, is that the output is not customizable. (...) The
menu item Tools / DXL Utilities / Transformer... (...) it'll put up a
dialog very similar to the Design Synopsis dialog, with one important
difference: the opportunity to select an XML stylesheet (.xsl file) to transform
the DXL descriptions of the selected design elements into human-readable form as
an HTML file. »

in Best Practice Makes Perfect

2006-06-23

Windows (XP): Remove or delete a service

To remove a service on windows XP, use the following command:


sc delete "<service name>"

2006-06-06

Notes: How to access .Net classes from LotusScript

IBM Technote: How to access .Net classes from LotusScript

Problem
You have created an object using Microsoft .Net and would like to create and use this object within LotusScript. How can this be done?
 
Solution
To create a .Net DLL that is accessible from LotusScript perform the following steps:

1. Create a C# Class Library Project with the following code:

using System;

public class MyTest
{
public string AppendStr(string s)
{
return s + " from inside the DLL";
}
}

Note: A Visual Basic Class Library could also be created.

2. To access this DLL from anywhere on the machine you will also need to create this DLL as a shared assembly and publish the your DLL to the Global Assembly Cache (GAC). Otherwise, the DLL will be created as a private assembly and you will only be able to create the object if the DLL is in the same directory as the calling program.

      To create this DLL as a shared assembly you will need to generate a cryptographically strong name for the assembly. To do this start up the Visual Studio Command Line prompt, and enter the following command:

      sn -k c:\MyTest.snk

      This will create an Assembly Key File name at the location c:\MyTest.snk


3. Open the AssemblyInfo.cs file of the project include the following line:

      [assembly: AssemblyKeyFile(@"c:\\MyTest.snk")]

      Note: For Visual Basic you will need to include this tag instead: <assembly: AssemblyKeyFile(@"c:\\MyTest.snk")>


4. Build the project. This will build the project with the value of the Assembly Key File and allow it to be published as a Shared Assembly to the Global Assembly Cache.

5. Using the Visual Studio Command Line, change to the directory where the DLL was generated, then publish the DLL to the Global Assembly Cache using the following command:

      GacUtil /i MyTest.dll


6. To make the objects in this DLL accessible via the COM interface, enter the following command:

      regasm MyTest.dll


7. To access the object contained in this DLL using LotusScript use the following code:

      Dim obj As Variant
      set obj = CreateObject("MyTest")
      MsgBox obj.AppendStr("This is")

      The message box will display "This is from inside the DLL."


In short, once the shared assembly is published as a COM component, it can be instantiated in LotusScript using the CreateObject() function. With the exception of Step 7, all of the steps are all specific to the deployment process of Microsoft .Net components. For further information regarding any of these utilities you should refer to the Microsoft web site or Microsoft support.

2006-05-11

Notes: After logout, authentication is no longer possible

Symptoms:

User presses the login button/link, inserts username and password and gets authenticated. Later he presses the logout button/link, and he is successfully logged out. If he tries to login again, he's unable to because the authentication form is never presented again (unless he closes the browser window and re-opens it).


Possible Solution:

In case the login link is like this:

top.location.href = "/database_path?OpenDatabase&Login">

change it to:

top.location.href = "/database_path?Login&redirectTo=database_path?OpenDatabase">

2006-05-10

Domino: Session Authentication via Lotus Script

Or how to login on the web via a notes agent:

Print "<HTML>"
Print "<BODY TEXT=""FFFFFF"" BGCOLOR=""FFFFFF"" onLoad=""document.forms[0].submit()"">"
Print "<FORM METHOD=post ACTION=""/names.nsf?Login"" NAME=""_AlfaLogin"">"
Print "<!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 4.0//EN"">"
Print "<HTML>"
Print "<INPUT NAME=""Username"" VALUE=""" + sUsername + """ TYPE=hidden maxlength=256>"
Print "<INPUT NAME=""Password"" VALUE=""" + sPassword + """ TYPE=hidden maxlength=500>"
Print "<INPUT NAME=""RedirectTo"" VALUE=" + sRedirectTo + " type=hidden>"
Print "</HTML>"
Print "</FORM>"
Print "</BODY>"
Print "</HTML>"

2006-05-09

Notes: View doesn't refresh

Symptoms:
  • permanently shows the Refresh Icon
  • the "NoCache" parameter on the dblookup "doesn't work"
  • the view.Refresh statement doesn't refresh the view

Problem:

The view probably uses @Now or @Today formulas in the Selection Formula or in a column, which prevents the view's index to be updated (it's always out-of-date as these formulas force the calculation to go to the minute:second detail).


Solution:

Use this formula instead: @TextToTime("Today") - it calculates only to the year-month-day so that the index only updates once a day.

2006-04-28

Notes client: action bar properties are not rendered in the client

If the display border property of a view or form action bar is set to Always but it doesn't show when viewed in the client, check under File -> Preferences -> User Preferences -> Additional Options and make sure the option "Standard dialog boxes" is not checked.

Windows: Usb Hub is not recognized ("Unknown device")

If Windows XP does not recognize a Usb hub, and categorizes it as an "Unknown Device" even after updating the drivers, unplug the electrical cord from the hub and try again.

2006-04-06

Notes: Bubble Sorting a Document Collection

«Searching a database using db.ftsearch is pretty fast and efficient but ordering based on a field value in the documents is not possible. What is required is something akin to a SQL ORDER BY clause but alas we don't have one and have to resort to sorting the collection the old fashioned way... by implementing a bubble sort.

Usage

Dim vCollection As NotesDocumentCollection Set vCollection = vSearchDatabase.ftsearch(aQuery, 501, FT_SCORES, FT_FUZZY) Set vCollection = Global_SortCollection(vCollection, "Person_Surname")

The sort function requires two parameters, the first a NotesDocumentCollection with documents in it and the second a field name to use to sort the documents, in the example above the documentcollection "vCollection" will be sorted by "Person_Surname".

The function works by converting the document collection into an array of documents, doing a bubble sort on the array and then converting the array back into a new document collection.


The Code

Function Global_SortCollection(aCollection As NotesDocumentCollection, aField As String) As Variant Dim vCollectionDB As NotesDatabase Dim vDocTemp As NotesDocument Dim vCollectionSorted As NotesDocumentCollection Dim vDocArray() As String Dim vLower As Integer Dim vUpper As Integer Dim vBottomMax As Integer Dim vLoop As Integer Dim vLoopTopHalf As Integer Dim vLoopBottomHalf As Integer Dim vMidPoint As Integer Dim vTarget As String Set vCollectionDB = aCollection.Parent Redim vDocArray(aCollection.count-1) As String 'CONVERT DOCUMENT COLLECTION TO ARRAY Let vLoop = 0 Set vDocTemp = aCollection.GetFirstDocument Do While Not vDocTemp Is Nothing vDocArray(vLoop) = vDocTemp.GetItemValue(aField)(0) + "~" + vDocTemp.UniversalID Set vDocTemp = aCollection.GetNextDocument(vDocTemp) vLoop = vLoop + 1 Loop 'SHELL SORT THE ARRAY vLower = Lbound( vDocArray( ) ) vUpper = Ubound( vDocArray( ) ) vMidPoint = 1 'DETERMINE A STARTING MID POINT TO THE ARRRAY Do vMidPoint = (3*vMidPoint) + 1 Loop Until vMidPoint > vUpper - vLower + 1 'LOOP THROUGH THE ARRAY Do vMidPoint = vMidPoint \ 3 vBottomMax = vLower + vMidPoint - 1 For vLoopTopHalf = vBottomMax + 1 To vUpper vTarget = vDocArray(vLoopTopHalf) vLoopBottomHalf = vLoopTopHalf 'COMPARE TOP HALF OF ARRAY WITH BOTTOM HALF Do While vDocArray( vLoopBottomHalf - vMidPoint ) > vTarget vDocArray(vLoopBottomHalf) = vDocArray(vLoopBottomHalf - vMidPoint) vLoopBottomHalf = vLoopBottomHalf - vMidPoint If (vLoopBottomHalf <= vBottomMax) Then Exit Do Loop If (vLoopBottomHalf <> vLoopTopHalf) Then vDocArray(vLoopBottomHalf) = vTarget Next Loop Until vMidPoint = 1 'CREATE A NEW EMPTY DOC COLLECTION Set vCollectionSorted = vCollectionDB.Search("",Nothing,0) 'CONVERT ARRAY TO DOC COLLECTION For vLoop = 0 To Ubound(vDocArray) Set vDocTemp = vCollectionDB.GetDocumentByUNID(Strrightback(vDocArray(vLoop), "~")) Call vCollectionSorted.AddDocument(vDocTemp) Next vLoop 'RETURN THE DOCUMENT COLLECTION Set Global_SortCollection = vCollectionSorted End Function


Conclusion
A simple yet extremely useful function that belongs in everyone's global routines script library.»




(Source)

2006-04-05

Notes - Lotus Script: Resume without error

Scenario:

A script ends and returns the "Resume without error" message, even though no error was thrown during it's execution.
Checking the Error$ function, it may return the message "Variant does not contain a container".


Possible solution:

Include in the script the "Option Declare" statement, correct all errors (undeclared variables) and save it.

2006-03-27

Domino: Extend Authentication For Websites, Single Sign-on and Persistent Sessions

Ever wanted to be able to authenticate users on your Domino Web Server using a Web Service, against a MySQL database, a text file or some other external source? Or how about changing the default Domino behaviour so that user sessions remain active for days or weeks. It's easy and I'll show you how using Apache Tomcat and a simple Java Servlet.

More >

Notes - Web: Ajax Drag-n-Drop Sorting of Documents

«Imagine the following situation - say you've got a set of documents which are all children of one container document. From within this parent container document you want to be able to quickly change the order in which the child documents appear. Sound familiar?»

More >

Tools: Yahoo! UI Library

«The Yahoo! User Interface Library is a set of utilities and controls, written in JavaScript, for building richly interactive web applications using techniques such as DOM scripting, HTML and AJAX. The UI Library Utilities facilitate the implementation of rich client-side features by enhancing and normalizing the developer's interface to important elements of the browser infrastructure (such as events, in-page HTTP requests and the DOM.»

More >