Hi Erica,

$$$Text generates content into the message globals at compile time. Here's one way to solve the problem:

Class Erica.DemoLocalizedXData
{

Parameter DOMAIN = "Demo";

XData LocalizedEmail [ MimeType = text/html ]
{
<body>
<p>
Text to be translated into another language
</p>
</body>
}

ClassMethod GetLocalizedContent(xDataName As %String) As %String [ CodeMode = objectgenerator ]
{
    do %code.WriteLine(" Quit $Case(xDataName,")
    set key = ""
    for {
        set xdata = %class.XDatas.GetNext(.key)
        quit:key=""
        set data = xdata.Data.Read() // Assumptions about length here...
        do %code.WriteLine("   "_$$$QUOTE(xdata.Name)_":$$$Text("_$$Quote^%qcr(data)_"),")
    }
    do %code.WriteLine("   :"""")")
}

}

After compilation you'll have:

^IRIS.Msg("Demo")="en"
^IRIS.Msg("Demo","en",3630108798)="<body>"_$c(13,10)_"<p>"_$c(13,10)_"Text to be translated into another language"_$c(13,10)_"</p>"_$c(13,10)_"</body>"_$c(13,10)

If you want to localize individual strings in the XData block independent of the HTML markup that gets a little more complicated. I'd think it's simpler/possibly better to localize the entire block at once though.

PBKDF2 is not a method of encryption, it's a method of hashing - e.g., it's one-way.

Depending on what you're looking to accomplish (e.g., validating users against some external system), delegated authentication (https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls...) with a ZAUTHETNICATE routine (see https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls...) might help.

@Craig.Regester I'd be really interested to see what you're doing with the server hooks. (Maybe @Raj Singh would be interested too?) It sounds like you're rolling your own; what are you integrating with? Git, Perforce, SVN, etc.?

A few tidbits that may or may not be relevant for your use cases:

%Studio.Extension.Base:UserAction (see https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic....) with Action=3 will also launch an external browser given a full URL provided in Target (not necessarily CSP, but could be). This isn't truly a "modal" window but would give you a little bit more control perhaps.

For the sake of VSCode specifically there's a different API (not really documented) for closing a page opened with Action = 2 - specifically, sending a message to the parent frame (a VSCode web view) with data {"result":"done"}. This looks something like (in %CSP.Page:OnPage):

    If (%request.Get("EndTemplate") = 1) {
        If (%request.UserAgent [ " Code/") {
            // For VSCode only:
            Set %response.ContentType="text/html",%response.CharSet="UTF-8"
            Write "<html>",!
            Write "<script type=""text/javascript"">",!
            &js<if (window.parent) {
                window.parent.postMessage({"result":"done"},'*')
            }>
            Write "</script>",!
            Write "</html>",!
        } Else {
            Set %response.ContentType="text/xml",%response.CharSet="UTF-8"
            Set delim = ##class(%CSP.StudioTemplateError).#DELIM
            Write "<?xml version=""1.0""?>",!
            Write "<template><![CDATA[BODY",delim
            Write delim,"]]]]><![CDATA[></template>",!
        }
        Quit $$$OK
    }

@Evgeny Shvarov, the power of generators and projections is more in the creation of frameworks/tools that speed up building solutions. This includes many parts of IRIS itself, but moves into the user space for larger/more complicated applications (e.g., what we've done with AppS.REST both for REST-enabling older ObjectScript-based applications and for quickly building out new applications using IRIS and modern web technologies).

@Evgeny Shvarov anything with CodeMode=objectgenerator, typical uses of projections, anything that works with %Dictionary.(.*Definition|Compiled.*), anything that works directly with %Library.Routine / %Library.RoutineMgr... there are examples all over the place, including throughout both of my Open Exchange projects.

https://openexchange.intersystems.com/package/Test-Coverage-Tool
https://openexchange.intersystems.com/package/apps-rest

These don't go to the level of dynamically modifying/instrumenting code, but another of my projects (a mock framework for ObjectScript, still internal to InterSystems) does.

The right way to describe a list of a particular type of object is:

ClassMethod myMethod() As %ListOfObjects(ELEMENTTYPE="MyPackage.MyClass")

This is most relevant from the perspective of XML projections - e.g., web services. You can also define a subclass of %ListOfObjects with the ELEMENTTYPE parameter overridden for reuse. See https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls?KEY=GX...

One note - many use cases that call for a composite primary key in a typical boring database would be good use cases for a parent-child relationship in IRIS. This provides nicer features from an object/SQL perspective (e.g., using arrow syntax / implicit joins instead of explicit joins on the composite key fields). See: https://docs.intersystems.com/irisforhealthlatest/csp/docbook/Doc.View.c...

@Yuri Marx there's actually a separate "API Security Top 10" which is particularly relevant for REST APIs and modern web applications: https://owasp.org/www-project-api-security/

API4, API9, and API10 are all good IAM use cases; for the others, I'd recommend looking at https://github.com/intersystems/apps-rest for a security-first generic REST API implementation.

I'd expect that to work provided the id (not name) of the combobox/dataCombo is 'MyCombo'. e.g., this works fine:

Class DC.Demo.FindElement Extends %ZEN.Component.page
{

XData Contents [ XMLNamespace = "http://www.intersystems.com/zen" ]
{
<page xmlns="http://www.intersystems.com/zen">
<form>
<tabGroup showTabBar="true">
<tab caption="Tab One">
<combobox id="MyCombo" editable="true" />
</tab>
<tab caption="Tab Two">
</tab>
</tabGroup>
</form>
<button onclick="zenPage.alertComboValue()" />
</page>
}

ClientMethod alertComboValue() [ Language = javascript ]
{
    alert(zen('MyCombo').findElement('input').value);
}

}