You can override the Page() method of %CSP.Page and redirect output to a stream, process the resulting stream and write it to the original device the page is using to send data back to client.

Here is a quick and dirty example, using IO-Redirect package available on OpenExchange.

The redirecting page : 

Class test.src.RedirectedPage Extends %CSP.Page
{

ClassMethod Page(skipHeader As %Boolean = 1) As %Status [ ServerOnly = 1 ]
{
    #dim sc as %Status
    #dim ex as %Exception.AbstractException
    #dim pageStream,processedPageStream As %Stream.Object
    #dim len as %Integer
    #dim buffer as %String
    
    s sc = $$$OK
    try {
    
    Set pageStream = ##class(%Stream.GlobalCharacter).%New()
    Do ##class(IORedirect.Redirect).ToStream(pageStream)
    $$$TOE(sc,##super(skipHeader))
    Do ##class(IORedirect.Redirect).RestoreIO()
    Set pageStream = ##class(IORedirect.Redirect).Get()
    $$$TOE(sc,..ProcessPageStream(pageStream,.processedPageStream))
    while 'processedPageStream.AtEnd {
        s len = 32768
        s buffer = processedPageStream.Read(.len,.sc)
        $$$TOE(sc,sc)
        write buffer
    }
    } catch (ex) {
      s sc = ex.AsStatus()
    }
    return sc
}

ClassMethod ProcessPageStream(pageStream As %Stream.Object, Output processedPageStream As %Stream.Object) As %Status
{
    #dim sc as %Status
    #dim ex as %Exception.AbstractException
    s sc = $$$OK
    try {
        s processedPageStream = ##class(%Stream.TmpCharacter).%New()
        $$$TOE(sc,processedPageStream.CopyFrom(pageStream))
        d processedPageStream.Write("<div><span>original page had "_pageStream.Size_" bytes </span></div>")
    } catch (ex) {
      s sc = ex.AsStatus()
    }
    return sc
}

}

The original page :

Class test.src.OriginalPage Extends test.src.RedirectedPage
{

ClassMethod OnPage() As %Status [ ServerOnly = 1 ]
{
    &html<
        <div>
         <span>Hello, world, again</span>
        </div>
    >
    return $$$OK
}

EnsLib.EDI.XML.Document implements Ens.VDoc.Interface, and exposes GetValueAt(propertyPath) method.

This method accepts XPath-like property paths such as "/foo/bar" or "/foo/bar/@attr".

This also means you can use the curly brace syntax in business rules (classes extending Ens.Rule.Definition).

For example, if the context class has a "xmlDocument" property, this expression

xmlDocument.{/foo/bar}

will be compiled into 

xmlDocument.GetValueAt("/foo/bar")

For example (in open exchange package ks-iris-lib) :

/// list of data types exposing %GetSerial and %SetSerial
Class ks.lib.collections.ListOfDataTypes Extends %Library.ListOfDataTypes
{

/// serialize object
Method %GetSerial(force As %Integer = 0) As %String
{
    return ##super(force)
}

/// deserialize object
Method %SetSerial(serialized As %String) As %Status
{
    return ##super(.serialized)
}

Storage Custom
{
<Type>%Library.CompleteCustomStorage</Type>
}

}

There is a limit to string length, as explained in the documentation.

To convert a binary stream to a base64 encoded character stream, you have to call the function in a loop using a buffer. Due the way the function works, the buffer length must be a  multiple of 57.

Here a sample implementation, from ks.lib.stream.Utils class in the ks-iris-lib package available on Open Exchange.
 

/// encode stream data into encoded using bufferSize (due the implementation of $system.Encryption.Base64Encode, this must be a multiple of 57)
ClassMethod Base64Encode(stream As %Stream.Object, Output encoded As %Stream.Object, bufferSize As %Integer = 5700) As %Status
{
  #Dim sc as %Status
  #Dim ex as %Exception.AbstractException

  #Dim len As %Integer
  
  s sc = $$$OK
  try {
    throw:((bufferSize#57)'=0) ##class(%Exception.General).%New("buffer size must be a multiple of 57")
    $$$TOE(sc,stream.Rewind())
    s:'$d(encoded) encoded = ##class(%Stream.TmpCharacter).%New()
    s len=bufferSize
    while 'stream.AtEnd {
      $$$TOE(sc,encoded.Write($system.Encryption.Base64Encode(stream.Read(.len),1)))
      s len = bufferSize
    }
  }
  catch (ex) {
    s sc = ex.AsStatus()
  }
  return sc
}

Yes Enrico, it is my understanding exactly : if your program code (i.e. the lines in the routine generated by the compiler) contains all printable ASCII characters, it should output an empty string. Of course it sounds silly to devise a very clever way to output an empty string, but after all, golf - from a practical point of view - is a silly sport isn't it ? 🤣

Hadn’t used it since Ensemble 2009 ;-), to provide custom html output displayed by the management portal when viewing a message of a class extending Ens.MessageBody, you can override the %GetContentType() and %ShowContents() methods of Ens.Util.MessageBodyMethods in your message class.

Here is a small example :

Class dc.sample.msg.Message Extends Ens.MessageBody
{

Property Name As %String [ InitialExpression = "you" ];

// returns MIME content type
Method %GetContentType() As %String
{
    return "text/html"
}

// output content
Method %ShowContents()
{
    &html<<p>Hello,&nbsp;#(..Name)#</p>>
}


Storage Default
{
<Data name="MessageDefaultData">
<Subscript>"Message"</Subscript>
<Value name="1">
<Value>Name</Value>
</Value>
</Data>
<DefaultData>MessageDefaultData</DefaultData>
<Type>%Storage.Persistent</Type>
}

}

In the management portal message viewer, the message gets displayed as :

Size 201 181 181, all unit tests passed (including undefined argument, and additional .Type("abc,de","de,abc") --> Unsorted)
Thanks Eduard, I missed the extraneous quotes (my mind is still not entirely purged of strongly typed languages habits 😅)

ClassMethod Type(a...) As %String
{
 f i=$i(r):1:$g(a){f j=1:1:$l(a(i),","){s l=$l($tr($p(a(i),",",j)," ")),c=$g(c,l),r=$s(l=c:r,r<3*l>c:2,r#2*c>l:3,1:4),c=l} k c} q $p("Constant1Increasing1Decreasing1Unsorted",1,r)
}

Hi,

To summarize the documentation :

  • ReplyCodeActions settings of HL7 operations is a comma-separated list of specifiers in the form <code>=<actions>, where <code> is an expression matching error condition(s) and <actions> is a string of one letter action codes.
  • All codes where <actions> consists of only 'W' (for 'log a Warning') will be evaluated, and a warning will be generated for each matching <code>.
  • Other <code> values will be evaluated in left-to-right order, executing the first matching <code> that has a non-warning <actions> value. As noted in the details for the 'W' flag, an error that only triggers 'W' <actions> will be treated as Completed OK.
  • if ReplyCodeActions is empty, a default setting is used. For HL7 operations, it is : 
    • :?R=RF,:?E=S,:~=S,:?A=C,:*=S,:I?=W,:T?=C

To match the application reject code in the HL7 ACK^O01 message in the example, and suspend all matching messages, use the following specifier, that matches all ACKs with MSA:1 = "AR" and suspend message, while retaining default behavior for other error conditions : 

:?R=S,:?E=S,:~=S,:?A=C,:*=S,:I?=W,:T?=C