I found the other method that needs to be overidden.  It's the XMLImportSDAString method.  This method was already making allowances for the "%" character for the HS.SDA3.CodeTableDetail.UoM class, so I follows the example and added my valid characters when it's importing SDA for the HS.Local.SDA3.CodeTableDetail.xProcedureModifier class.

Hi Harshdeep,

It's not something I read so much as figured out the hard way.  But here is the link to the HS.SDA3.CodeTableDetail.CareProvider object:


Which says, "In the Viewer Cache, CareProviders are matched on Code, SDACodingStandard,
Description, and CareProviderType (which is itself a code table). Other
properties are updated if different."

I think that the "matched on" properties are required in order to store the object.  So, setting a Name, but not a Code, will not store the object (this was my example).

As for the properties in FHIR that will map to the necessary SDA properties... you'll have to find the Schema Documentation for FHIR, on your instance's System Management Portal. I think that will be under HealthShare / HealthShareManagement  / Schema Documentation / FHIR Annotations. 

Possibly that will help.


Using Ensemble 2018.1.2, I'm using the pInput.Attributes("FTPDir") to get the full difectory of the filename (where pInput is %Stream.Object) when I have a root directory defined (e.g. /send) and the property Subdirectory Levels = 2.

I also, was trying to find a way  to use the subfolders of the file that's on the server to either run a rule or more simply, create the same folders on our side.  When the production service uses Subdirectory Levels, this subfolder information is not part of the ..Adapter.FilePath (e.g. the root folder is /send, the file is /send/sub1/sub2/testfile.txt, Subdirectory Levels=2, but the Adapter.FilePath = /send/testfile.txt)

However, using the FTPDir attribute of the %Stream.Object gives me the full directory of the source object, from which I can "piece" together appropriate storage path on our end.


Arnold, it could be a scope problem / zen expression problem.

Try something like this:

Property currentTableRow As %ZEN.Datatype.string(MAXLEN = 5000);

Method MyMethodOnServer() [ZenMethod] {

set %page.currentTableRow="1"


I had trouble with a longer string. For a string that's not a number, try this:

set %page.currentTableRow="line 1"

set str=..QuoteJS(%page.currentTableRow)


Hey Keith, how's it going from this time last year?  Caché was my first language (actually, it was MUMPS then), and I've always worked with data stored in globals.  When I learned SQL, MySQL, and Java, I realized that I could probably never go to a language where I coudln't "see" the actual data.   Having to rely on tables and SQL and not be able to get to the actual data stored on disk? I'd feel  like I did when I was using chopsticks for the first time.  

When I tell other non-InterSystems programmers how we have access to the actual data, they're flabbergasted and amazed. But, that is the key to understanding the Storage in each class (which by now, you're probably quite familiar with).   SQL will read this Storage "map" which maps the properties to the global storage, and picks it out of the global for us.  

Back in the old days, we would create our own global structure to hold the data.  Globals often looked like ^Point(pointId,"X")=3  and ^Point(pointId,"Y")=4, or more often, ^Reg(PatientId,"Name")="Smith,Amy", or ^Patient(PatientId,1)=Name^age^DOB^ZipCode and ^PatientIndex("Smith,Amy",PatientId)="" and ^Patient("Count")=last PatientId

Any IDX programmers here?  We had a entire book just to understand the globals.

These days, we let InterSystems create the global structure for us, which appears in the Storage definition when a class is compiled, but it's still the same as before -- the data is still stored in a global, and you can edit it and delete it from the global - not recommended though because IS keeps track of counters.

Anyway, I'm sure you're all set, but I thought it might help someone reading this to know the history of data storage and how it's a completely different paradigm than other databases. 

Also, I was looking for the best way to delete the storage from a deleted property.  You know - you delete the property, but don't delete the storage element, and the node is still there in the global?  Normally, this wouldn't ever be noticed, because when using SQL, you don't see the deleted property, but in our case, we are using the global's stucture to view certain structures in the data (it's a bunch of arrays of Objects).  The deleted property's storage was getting in our way.

I decided the best way to delete the leftover data was to simply kill the nodes: ^Test.Package.ClassNameD(id,"TestProperty")=""

This would have been impossible with SQL because the property doesn't exist anymore!



Hi Arnold,

Regarding you error, looks like Cristiano is correct when he said that %DrawHTML writes only to a device, which is what I am doing (%File) so that works for me.  Drawing it to a string won't work. A very roundabout way would be to write the HTML to a file, and then read the file into the body of an email ... 

As for getRowData(), you'd want to start in a ClientMethod (js) and loop through the table's rows, getting the data for each row, and sending that to the server, perhaps to be stored in a global until you're done? Perhaps passing a large JSON would be better, but I don't have that experience. When you're done on the client, you'd go back to the server, loop through your global, and build your email body.  However, I know from experience that getRowData() is very slow, and should be used to get only the 1 row that you need -- it's not good for looping through to get the all of the data.

In that case, perhaps you should revisit Crisitano's suggestion of just running the table's query again, and using the resulting ResultSet to get and print out the data in a pretty HTML string.  I haven't used %DisplayFormatted, but that might have lots of options for you.

Hi Vivek,

We bought a license for CoolUtils (coolutils.com), whch also has  command line  functionality.  There are dozens of different converters with CoolUtils, but you can buy just the one.

We use a batch file for the command line, which will take two parameters, and an "option file" (used by the converter):

"C:\Program Files (x86)\TotalHTMLConverter\HTMLConverter.exe" "%1" "%2" -c pdf -fp -combine -si -pc Normal -ps letter -OptionFile %3

then call the batch file using $zf:

set command=dir_"Converters\html\ConvertHtmlToPDF.BAT"
set params(1)=fromHTMLfilename

set params(2)=toPDFfilename

set params(3)=OptionFileName

; Fire off HTML>PDF Converter program in background (-2 = don't wait)


The OptionFile has PDF codes like:

PgHead=&b&d &t
PgFoot=&bPage &p of &P&bInsights powered by RevenueHealth Systems (C) 2018

I used the utility's "Command Line Parameters" help file to figure out how to set all the options, and how to use the option file.  I wrote a note to myself to "See http://www.verydoc.com/htmlprint-footer-header.html" for use on the pdf codes that are in the option file. 

It took a while, and I had to program in a new "language" of PDF options and stuff.

Good luck!

But what else?  How do you run it?  Do you need to be in a %csp session?

I have the %ALL:

%SYS>w $roles

Attempt to run it as a class method:

%SYS>w ##class(%CSP.Session).LogoutAll("laura1") 
W ##CLASS(%CSP.Session).LogoutAll("laura1")


Try it with a session object:

%SYS>s session=##CLASS(%CSP.Session).%OpenId("fBOZJihk0C")
%SYS>w session.LogoutAll("laura1")
 quit $$LogoutAllUserSessions^%SYS.cspServer(username, %request, %response ) }
<UNDEFINED>zLogoutAll+1^%CSP.Session.1 *%request

And from a connected session with the user that needs to logout of all sessions:

ERROR #822: Access Denied

So I'll have to connect to the application as a developer in order to kill off sessions that are causing problems.  The user can't wipe them out himself.  I get a "problem session" if the page times out and the user kills the page; this causes the session to hang around until its timeout, and due to our specific setup, he can't log in again until the session times out or a developer kills it from Session Management. I wanted to give the user the ability to wipe out all of his own sessions.

No need to discuss grouping by sessionId or anything like that - we have a very specific setup such that flags are set and the user can't login again if he kills his page. 


Now, how can I intercept this error and call the logout? That would be better...

image of the kill page or wait fromChrome



Ah, so you need to map the %SYSTEM.Status class from cache to a Java object.  I've never done that.  Did the "Java Proxy Class Mapping" have anything helpful?




I'm thinking you might have to create your own Status class that extens %Status; like creating your own exception class in Java. Then you can edit your Status class so that when compiled it will create a Java class. 

Sounds fun. Let me know if this works for you.  

I have tried every combination I can think of:


//zenThis.DoDebug(); //zenThis is null
//zenPage.DoDebug(); //Attempt to call non-ZENMethod
//this.DoDebug(); //attempt to call non-ZENMethod
//%this.DoDebug(); //available only in ObjectScript
//do ..DoDebug(); //same

(I'm actually calling a DoDebug zenmethod)

It's possible that i have to compile all the child classes; I just don't want to do that yet until no one else is on the system. I can call some other zenmethod that already exists, and it can find it. Just can't find my new zenmethod, although the javascript method is available.


Thanks -L

Thank you Eduard.  Looks like a serialized ListOfDataTypes is a $list, not a %ListOfDataTypes. 

This would not work (and is not):


TEST>s user=##class(Security.Users).%OpenId("testuser"))

TEST>w user.Roles
TEST>do ##class(%ListOfDataTypes).BuildValueArray(user.Roles,.array)


It does not work because the user.Roles is a %ListOfDataTypes, and not a $list.  Is there anything in the %ListOfDataTypes class that I can use to convert this object's Roles property to a nice looking string or array? Or must I first convert it to a $list?  Why isn't there a function to do that for me?


Thank you,