Search code examples
peoplesoft

How do I pass bind variables to Message Explain text in message catalog?


I know that when I use messages from the message catalog, I can use bind variables in the text of the message. For example,

Message Text:

Employee (%1) has already worked %2% this month.

Description:

Employees cannot work over %3% for any given month. Decrease percent for row %5 to %4%.

I noticed that when I use MessageBox, I can pass a value into the third, fourth, and firth binds, but when I use any other function (MsgGet, MsgGetExplainText, or CreateException), the binds in the explain text are not replaced. I am doing this because I need sometimes stop processing with Error, passing in the text from the message catalog (i.e. Error(MsgGet()).


For example:

Using MessageBox() Binds replaced properly

Using Error(MsgGet()) Binds not replaced

These are produced from the following, respectively:

MessageBox(0, "", 20007, 6, "MESSAGE NOT FOUND: Too many hours. Decrease percentage.", &emplid, Round(&percentInMonth * 100, 0), Round(&workPercentCap * 100, 0), &neededPercent, &rowNumber);

And

Error (MsgGet(20007, 6, "MESSAGE NOT FOUND: Too many hours. Decrease percentage.", &emplid, Round(&percentInMonth * 100, 0), Round(&workPercentCap * 100, 0), &neededPercent, &rowNumber));

How do I get around this problem? I am running PeopleTools 8.54.


Solution

  • In the 8.54 PeopleBooks for MsgGetExplainText, it mentions the following:

    Note: This substitution only takes place in message explain text when the MsgGetExplainText function is used. If you use a message box, the parameter substitution will not occur in the explain text.

    This is an unclear way of saying that you can't do that. This also applies to MsgGet and CreateException, even though not specified in their docs.


    However, there is a workaround found at IT Toolbox.

    Users in that thread posted something close to the following code as a workaround (adapted here to suit your question):

    Local string &errorMessage = MsgGet(20007, 6, "MESSAGE NOT FOUND: Too many hours. Decrease percentage.", &emplid, Round(&percentInMonth * 100, 0), Round(&workPercentCap * 100, 0), &neededPercent, 1);
    &errorMessage = &errorMessage | Char(10) | Char(10);
    &errorMessage = &errorMessage | MsgGetExplainText(20007, 6, "MESSAGE NOT FOUND: Too many hours. Decrease percentage.", &emplid, Round(&percentInMonth * 100, 0), Round(&workPercentCap * 100, 0), &neededPercent, 1);
    Local string &temp = MsgGet(0, 0, "");
    Error &errorMessage;
    

    For whatever reason, the binding of the variables happens after the fact, meaning, if you call Error on the return value of MsgGet, the result is an error message with the unbound parameters.

    You need to append the results of MsgGet and MsgGetExplainText onto each other to mimic the functionality of MessageBox as above.

    Note that the seemingly useless statement Local string &temp = MsgGet(0, 0, ""); is necessary, otherwise the last line of the message is repeated, but without parameter substitutions:
    Repeated explain-text

    This clears away the unbound explain-text returned from MsgGet and MsgGetExplainText.


    If you wanted to throw an Exception instead, the same principles apply. You need to use the workaround. You can also strip the stack trace from the message if you don't want that displaying to end-users:

    Local string &errorMessage = MsgGet(20007, 6, "MESSAGE NOT FOUND: Too many hours. Decrease percentage.", &emplid, Round(&percentInMonth * 100, 0), Round(&workPercentCap * 100, 0), &neededPercent, 1);
    &errorMessage = &errorMessage | Char(10) | Char(10);
    &errorMessage = &errorMessage | MsgGetExplainText(20007, 6, "MESSAGE NOT FOUND: Too many hours. Decrease percentage.", &emplid, Round(&percentInMonth * 100, 0), Round(&workPercentCap * 100, 0), &neededPercent, 1);
    Local string &temp = MsgGet(0, 0, "");
    
    rem "Remove" the Stack-Trace;
    try
       throw CreateException(0, 0, &errorMessage);
    catch Exception &e
       Error &e.ToString( False); rem False means no stack trace;
    end-try;
    

    In addition, it is possible to extend the Exception class and to substitute bind values in that way. It is a little more complicated, however. Here is an example:

    class CustomException extends Exception
       method CustomException(&messageSet As integer, &messageNum As integer, &message As string, &substitutions As array of string);
    end-class;
    
    method CustomException
       /+ &messageSet as Integer, +/
       /+ &messageNum as Integer, +/
       /+ &message as String, +/
       /+ &substitutions as Array of String +/
       %Super = CreateException(&messageSet, &messageNum, &message);
       Local integer &i;
       For &i = 1 To &substitutions.Len
          %Super.SetSubstitution(&i, &substitutions.Get(&i));
       End-For;
       rem Setting Context to something else replaces the stack trace with whatever text set it to;
       %Super.Context = "";
       %Super.DefaultText = &message;
    end-method;
    

    To make this work, you can pass in an array of bind variables that you want to substitute into the message and then loop through the array and call SetSubstitution() for each variable.

    Also, as noted above, if you want the Exception to "throw" like an error message (that is, display the error message without a stack trace), you can erase the Context of the CustomException:

    %Super.Context = "";