You should know, that namespace it is a just definition in which databases stored some types of data, and how it mapped.

So, you can only check in which database package stored

write ##class(%SYS.Namespace).GetPackageDest("SAMPLES","%Activate.Enum")
^/opt/cache/mgr/cachelib/

delimited output, first part is a system like in ECP-configuration, and second part is a database path

you can compare this path with default path for packages in this namespace.

w ##class(%SYS.Namespace).GetPackageDest("SAMPLES","")
^/opt/cache/mgr/samples/

Every routines stores directly in database, so, you can't just open it as any file on your filesystem as well as you do it with csp files.

but you can open it with %RoutineMgr class, something like this

USER>zn "samples"

SAMPLES>set rtn=##class(%RoutineMgr).%OpenId("hello.mac")

SAMPLES>while 'rtn.Code.AtEnd { write !,rtn.Code.ReadLine()}
 
hello ; hello world routine
 write !, "hello world"
 write !, "bye"
end quit  ; end
 

if you also need to get list of such files, you can use query StudioOpenDialog in %RoutineMgr class

SAMPLES>set st=##class(%SQL.Statement).%New()
 
SAMPLES>set sc=st.%PrepareClassQuery("%RoutineMgr","StudioOpenDialog")
 
SAMPLES>set rs=st.%Execute("*.mac")
 
SAMPLES>do rs.%Display()
 
 
Dumping result #1
Name    IsDirectory     Type    Size    Date    Description     IconType
DocBook .       9                               0
badroutine.mac          0       62      2004-09-28 13:05:45             0
CinemaData.mac          0       10258   2016-05-10 22:32:04             0
compareloop.mac         0       1201    2004-12-02 18:23:57             0
datent.mac              0       3089    2002-01-03 12:05:01             0
datentobj.mac           0       2627    2002-09-06 00:15:23             0
dbconvert.mac           0       1532    2002-01-03 12:05:52             0
fibonacci.mac           0       365     2002-01-03 12:06:03             0
forexample.mac          0       502     2004-11-30 16:00:08             0
funcexample.mac         0       333     2002-01-03 12:06:30             0
hello.mac               0       251     2016-07-13 12:07:04.520803              0
LDAP.mac                0       68720   2013-12-16 11:48:09.962335              0
lookup.mac              0       8484    2008-08-21 19:17:48             0
lookup1.mac             0       1465    2002-01-03 12:07:15             0
lookup2.mac             0       5984    2002-01-07 18:08:46             0
lookupobj.mac           0       7857    2008-08-21 21:58:25             0
loopend.mac             0       242     2002-01-03 12:08:12             0
loopstart.mac           0       217     2002-01-03 12:08:21             0
nameloop.mac            0       552     2002-01-03 12:08:33             0
passbyref.mac           0       604     2002-01-07 12:01:47             0
postcond.mac            0       341     2002-01-03 12:08:51             0
procexample.mac         0       423     2002-01-03 12:08:59             0
publicvarsexample.mac           0       357     2002-01-03 12:09:09             0
RightTriangle.mac               0       1836    2011-02-24 18:56:29             0
root.mac                0       149     2004-11-30 15:57:29             0
simpleloop.mac          0       161     2002-01-03 12:09:59             0
SQLGatewayTest.mac              0       2480    2016-05-10 22:32:04             0
survivor.mac            0       179     2002-01-03 12:10:32             0
ZAUTHENTICATE.mac               0       37302   2015-03-10 10:48:43.589807              0
ZAUTHORIZE.mac          0       12120   2016-05-10 22:32:04             0
 
30 Rows(s) Affected

but WaitMsg it is on parent's side, my control code ususaly like like this

    set resName="someresource"
    job ..someJob(jobsData,resName)
    set child=$zchild
    
    for {
        set $lb(sc,data)=$system.Event.WaitMsg(resName, 1)
        if sc<=0 {
            quit:'$data(^$Job(child))
        }
        continue:data=""
        // do some staff with incomming data
    }

But if you want to interrupt your child process, of course will be possible only from such process, if your process have some loops you may just check if parent still exists, if not, just stop working.

Steve,

I would recommend to distant webservers from ECP Application servers. In your case I would install webserver exactly on the server where ECP Application Server have already installed, so in this case your connect via superport will be secured, and in this case only web port should be opened in the firewall. Outside of this servers after firewall you need to use some load balancer, in this case I would recommend to use HAProxy. Connection between extarnal HAProxy and  internal webservers could be secured with ssl, but I'm not sure that is really needed.

And in this case it is possible to store all static content such as JS, CSS and images on server where load balancer placed, to avoid redundant request to the data servers. It is not a big problem for the production systems.

So finally in simple view it my looks like below.

Well, you can't write conditions in this way, and compile error which you see, it is actually parsing error for such condition
How you can change it, You should define some function which may do all this check or partially, just return some value for check.

To define such function, you should have your child for Ens.Rule.FunctionSet class, something like this

Class Test.Utils Extends Ens.Rule.FunctionSet
{

ClassMethod GetAt(value As %String = "", index As %String) As %String
{
    if $isobject(value) {
        quit value.GetAt(index)
    }
    quit ""
}

}

and then, you can use it in this way, where GetAt your just created function.

<rule name="">
<constraint name="msgClass" value="Test.TestMessage"></constraint>
<when condition="GetAt(Document.myList,1)=&quot;AA&quot;">
<send transform="" target="DummyOperation"></send>
<return></return>
</when>
</rule>

But your function should return final value for checking, and you can't use write so,

GetAt(Document.myList,1).property1=&quot;AA&quot;

I use such code in one of my projects

ClassMethod Test()
{
    set address="One Memorial Drive, Cambridge, MA 02142, USA"
    do ..GetCoords(address, .latitude, .longitude, .partialMatch)
    zw latitude, longitude, partialMatch
}

ClassMethod GetCoords(Address As %String, Output Latitute As %Float = "", Output Longitude As %Float = "", Output PartialMatch As %Boolean) As %Status
{
    set params("address")=Address
    set sc=..CallGoogleAPI("/maps/api/geocode/json", .params, .data)
    
    if data.status="OK" {
        set result=data.results.$get(0)
        set PartialMatch = +result."partial_match"
        set Latitute = result.geometry.location.lat
        set Longitude = result.geometry.location.lng
    }
    quit $$$OK
}

ClassMethod CallGoogleAPI(Url As %String, ByRef Params, Output Data) As %Status
{
    #;set Params("key")="your api key here"
    quit ..CallApi("maps.googleapis.com", 1, Url, .Params, .Data)
}

ClassMethod CallApi(Server As %String, Secure As %Boolean = 1, Url As %String, ByRef Params, Output Data) As %Status
{
    set ht=##class(%Net.HttpRequest).%New()
    set ht.Server="maps.googleapis.com"
    set ht.Https=Secure
    set:Secure ht.SSLConfiguration=..GetSSLCertificate(Server)
    
    set param=""
    for {
        set param=$order(Params(param),1,value)
        quit:param=""
        do ht.SetParam(param, value)
    }
    
    set sc=ht.Get(Url)
    if $$$ISERR(sc) quit sc
    
    set Data={}
    set Data=Data.$fromJSON(ht.HttpResponse.Data)
    
    quit $$$OK
}

ClassMethod GetSSLCertificate(Server As %String) As %Status
{
    new $namespace
    znspace "%SYS"
    do {
        quit:##class(Security.SSLConfigs).Exists(Server)
        
        set tSC=##class(Security.SSLConfigs).Create(Server)
        $$$ThrowOnError(tSC)
    } while 0
    quit Server
}

Much better If it would be possible to migrate or merge two accounts here. I have a WRC account on email from my current company, but what happens when I chnage a job. In this case I may loose access to any my content here. And I have another registration with my own email. It would be very nice if I could merge two of this accounts or something else, if I could use WRC and commonity with the same my own not corporate account.

Hi Kenneth,

I've been working in company where we develop WEB-based ECM application. It's a quite big one, and very flexible.  We do not use CSP-files at all, only CSP-classes, no Zen. And some my own projects on Caché, also in WEB. I will not recommend to use CSP-files, or Zen. I can't say anything about Zen Mojo, just because when I last time saw it, it was not so better then Zen, now with Native JSON it may be better.  I'll recommend CSP-classes, REST and webservices.

CSP-Pages. It may be usefull, with all of their tags and stores with files. But unfortunately  all of this files, may some time confuse developer, because in any way developer should know is it compiled or not, when some files were changed, it may looks like nothing changed.  And at the end anyway it compiles to CSP-classes. 

ZEN. With ZEN, you get some ready components. In the same time, your application became too big, even if it not, and have just one page. It generate js/css files, and you should care about that files, while you deploy app to production. Data transfered from server to client side, contains waste js code.

With CSP-classess, I have flexibility, and can generate different parts of page in very different parts of code. I have much more possibilities to control how I generate HTML, receive and response requests. But I still generate html-code in Caché.

As well we have more than 1MB CSS and JS files. For me it is not so comfortably to edit that files in Studio. For HTML in classes I can use embedded &html<>. JS and CSS files I'm editing in Sublime Text. 

But I'm sure that in modern time application should be SPA, all html should be in static files, and server should speak only in JSON, with a REST and wevservices. And in all my last web-projects, I'm using this way.  Static HTML, JS, CSS files, with a Gulp I transform all sperate JS and CSS files to one, implement that files to HTML. And for production version I put all that files in one Class. In this case I can send one XML file which contains full application.  For example CacheBlocksExplorer

You can calculate such property on a class side, something like this. Index by CitizenRef and RelocationDate, and method which looking for the next date, and returns true if nothing found.

Index CitizenRelocation On (CitizenRef, RelocationDate);

Property IsLastKnownRecord As %Boolean [ Calculated, SqlComputeCode = {set {*}=##class({%%CLASSNAME}).IsLastKnownRecordCheck({CitizenRef}, {RelocationDate})}, SqlComputed ];

Method IsLastKnownRecordGet() As %Boolean
{
    quit ..IsLastKnownRecordCheck(i%CitizenRef, i%RelocationDate)
}

ClassMethod IsLastKnownRecordCheck(CitizenRef As %Integer, RelocationDate As %Date) As %Boolean [ CodeMode = objectgenerator ]
{
    set storagename="Default"
    set storageInd=%class.Storages.FindObjectId(%classname_"||"_storagename)
    set storage=%class.Storages.GetAt(storageInd)
    set indexLocation=storage.IndexLocation
    set indexLocation=$name(@indexLocation@("CitizenRelocation"))
    do %code.WriteLine($c(9)_"quit $order(@($name("_indexLocation_"))@(CitizenRef, RelocationDate))=""""")
    quit $$$OK
}

And result you can see below

I guess that you writing an function for rule.  And your class where you do it extends  Ens.Util.FunctionSet  where Lookup is located. Lookup is a method and you should call it as a method, now it is as variable.

 
/// Returns the participant code based on MSH-4
ClassMethod getParticipant(iSendingFacility As %String) As %String [ Final ]
{
   set = $PIECE(iSendingFacility,"^",1)
   set = $PIECE(iSendingFacility,"^",2)
   set sc1 = ..Lookup("ParticipantCodeMap",a)
   if sc1 = "" {
      set sc1 = ..Lookup("ParticipantCodeMap",b)
   }
   sc1
}
 

And if you want to call it by terminal, it should be so:

 write ##class(Ens.Util.FunctionSet).Lookup("ParticipantCodeMap","1083601330")

Well, with this example, I just changed that block, which works for all rest pages in a group except the first one.

<masterreference masterReference="rest" pagePosition="rest">
<document width="8.5in" height="11in" marginLeft="1.25in" 
marginRight="1.25in" marginTop="1.0in" 
marginBottom="1.0in" headerHeight="1.0in">
</document>
<pageheader>
<table orient="col" layout="fixed" width="6in">
<item style="text-align:left" value="Sales Report" />
<item field="@name" width="2in"></item>
<item style="text-align:right" special="page-number-of-with-xpath" field="@name" />
</table>

</pageheader>
</masterreference>
 

And it shows Sale rep in a header where page counter was shown before, for example

Sales Report Jack 2 of 5

Sales Report Jen 3 of 5

You can see my result here. A changed only first section.

Templates in ZEN, is a quite simple as I think. When you doing template page just add <pane paneName="MainContent"/>. And then in a children, you should add XData with the same name "MainContent", which starting with tag <pane>.

XData MainContent
{
<pane xmlns="http://www.intersystems.com/zen">
</pane>
}
 

You should not rewrite Contects XData in children pages, and such subpanes could in the same class, or in children. 

In this case, in a table you just add a new column with link 

<column header="" width="5%" linkCaption="view" link="ZenTutorial.ViewContact.cls?ID=#(%query.ID)#"/>

so, now, we got an contact's ID in every row.

and then in a ViewContact page, we should add a dataController, to our Model, and define an modelId, from url

<dataController id="contactData" 
                modelClass="ZenTutorial.ContactModel"
                 modelId="#(%url.ID)#" />

You can set modelId with URI Parameters as weel, after adding a property:

Property ContactID As %ZEN.Datatype.integer(ZENURL = "ID");

and new modelId, which now uses our new Property 

modelId="#(%page.ContactID)#"


and finally form, wich connected with previously added controllerId, and it shows two fields, read only, yet. But it is not so diffucult to add editng.

<form id="MyForm" layout="vertical" controllerId="contactData">
  <text label="ID:" id="ID" name="ID" 
    dataBinding="%id" size="5"
    readOnly="true"/>

  <text label="Name:" id="Name" name="Name" 
    dataBinding="Name" size="30"
    labelClass="required" required="true"/>
</form>