Pravin Barton · Jan 20, 2016 go to post

I used %INLIST $ListFromString(?) as you suggest  and it solved my problem. Thank you for the help!

Pravin Barton · Apr 28, 2017 go to post

Good point that I forgot to highlight. If you don't kill the old instance, it will sit in the old database without any easy way of referencing it.

Pravin Barton · Apr 28, 2017 go to post

John Murray also provided a different solution in this post. You can view globals by database rather than namespace in the SMP. That way you can export a global from one database into a file, then import it into another database. After some digging I found this is actually what the documentation recommends. The advantage of the MERGE is that it's much simpler to do programmatically. I can't speak to the difference in performance.

Pravin Barton · May 1, 2017 go to post

Is it true that Windows doesn't have access to csession? I'm able to run it on my Windows machine:

C:\InterSystems\Cache\bin>csession CACHE1

Node: reimaged67, Instance: CACHE1

USER>w $zv
Cache for Windows (x86-64) 2017.1 (Build 730) Wed Dec 14 2016 22:43:18 EST

Pravin Barton · May 3, 2017 go to post

Hi Joyce,

I'm using Atelier version 1.0.263 which I installed as a plugin for Eclipse 4.6.3.

Thanks,

Pravin

Pravin Barton · May 11, 2017 go to post

Hi Bob,

The databag API is what the Zen MVC uses internally, you shouldn't have to worry about it.

Here's some sample code modifying the ZENTest.SVGSpriteTest sample page to display data from a datacontroller:

ClientMethod initializeCanvas() [ Language = javascript ]
{
var canvas zenPage.getComponentById('svgCanvas');
if ((!canvas) || !canvas.document) {
// ensure we don't execute code before the SVG document has been fully loaded
setTimeout('zenPage.initializeCanvas();',10);
return;
}
var inspector this.getComponentById('objectInspector');
inspector.setCurrObject(canvas);
var controller zenPage.getComponentById('spriteController');
controller.setModelId(1);
// create initial set of sprites & connectors
var sprite new Array();
sprite[0] canvas.createSprite('sprite',200,100);
sprite[0].setProperty('onclick','zenPage.selectSprite(zenThis);');
sprite[0].setProperty('caption', controller.getDataByName("sprite1"));
sprite[1] canvas.createSprite('sprite',200,300);
sprite[1].setProperty('caption', controller.getDataByName("sprite2"));
sprite[2] canvas.createSprite('sprite',400,100);
sprite[2].setProperty('caption', controller.getDataByName("sprite3"));
sprite[3] canvas.createSprite('sprite',400,300);
sprite[3].setProperty('caption', controller.getDataByName("sprite4"));
var connect canvas.createConnector('connector');
connect.addOutput(sprite[0],1);
//connect.addOutput(sprite[2],1);
connect.addInput(sprite[1],0);
//connect.addInput(sprite[3],0);
// turn off layout; turn on edit mode
canvas.setProperty('layout','');
canvas.setProperty('editMode','drag');
}

Pravin Barton · May 12, 2017 go to post

Hi Bob,

For this to work you also need to define the data controller element in the page contents. For example, you could add the following element to the XData:

<dataController id="spriteController" modelClass="User.SpriteDataModel" modelId="1"/> 

If you have a data model class defined called "User.SpriteDataModel".

I recommend taking a look at the "ZENMVC.MVCForm" class in the SAMPLES namespace if you'd like an example of defining a data controller and data model.

Pravin Barton · May 25, 2017 go to post

My question is where to put the #include statement so that runtime expressions can reference the included macros. I tried both putting it in a script tag, and directly at the top of the csp file. Both ways it fails to compile with a "Referenced macro not defined" error.

EDIT - Sorry, I just noticed that was a link. The include directive should do what I need. Thanks!

Pravin Barton · Oct 31, 2017 go to post

Thanks Jean! That will work for me.

I also found a way of accessing the html element in javascript to set the maximum length:

zen('commentTextArea').findElement('control').setAttribute('maxlength',1000);

But creating the custom component is probably a better practice, since it doesn't break the Zen abstraction.

Pravin Barton · Jan 31, 2018 go to post

Good thought, but I should have mentioned that the GUID for a specific row needs to stay the same over multiple calls of the query.

Pravin Barton · Jan 31, 2018 go to post

Currently we're using the GUID from one of the tables, but the problem is it's not unique anymore after the join.

Pravin Barton · Jan 31, 2018 go to post

We have a table for holidays and for people. Both of these tables have a country column. Each country has a list of holidays and all the people in that country have all those holidays off. The result of the join means semantically: which people have which days off. I could create a third table for this but it would have to be updated any time a holiday or a person gets added.

Your suggestion of using a hash function is good, and I think I'll do that.

Pravin Barton · Jan 31, 2018 go to post

We're uniting this person+holiday table with a different table of personal time off to create a general absences table. Client applications access this table through a web service in order to sync a schedule. They need a GUID on each absence entry so they know what needs to be updated. For example, if a holiday changes there's an absence for each person in that country, and the client needs to update each of those entries.

We're only sending across the absences that have been updated since the last sync, so the client can't just rebuild the whole schedule every time.

Pravin Barton · Jan 31, 2018 go to post

Thanks, that might work. Is there any danger of the ID from CachéStorage getting reused if an entry gets deleted?

Pravin Barton · Nov 20, 2018 go to post

Here's the code I'm using to test btw. If you uncomment the commented line it gives the SAX parser error.

Class XML.Sample2 Extends (%RegisteredObject, %XML.Adaptor)
{

Property StringEmpty As %String;

Property StringZerowidth As %String;

Property IntegerEmpty As %Integer;

Property IntegerZerowidth As %Integer;

Property BoolTrue As %Boolean;

Property BoolFalse As %Boolean;

Property BoolZerowidth As %Boolean;

ClassMethod Test() As %Status
{
    set sample = ..%New()
    set sample.StringEmpty = ""
    set sample.StringZerowidth = $c(0)
    set sample.IntegerEmpty = ""
    //set sample.IntegerZerowidth = $c(0)
    set sample.BoolTrue = 1
    set sample.BoolFalse = 0
    set sample.BoolZerowidth = $c(0)
    set writer = ##class(%XML.Writer).%New()
    $$$QuitOnError(writer.OutputToString())
    $$$QuitOnError(writer.RootElement("root"))
    $$$QuitOnError(writer.Object(sample,"sample"))
    $$$QuitOnError(writer.EndRootElement())
    set string = writer.GetXMLString()
    w !, string
    set reader = ##class(%XML.Reader).%New()
    $$$QuitOnError(reader.OpenString(string))
    do reader.Correlate("sample","XML.Sample2")
    do reader.Next(.object, .sc)
    $$$QuitOnError(sc)
    for prop = "StringEmpty","StringZerowidth","IntegerEmpty","IntegerZerowidth","BoolTrue","BoolFalse","BoolZerowidth" {
        write !, prop, ": ", $replace($property(object,prop),$c(0),"$c(0)")
    }
    return $$$OK
}

}

Pravin Barton · Feb 11, 2019 go to post

Hi Pilar,
Here's an example call using password grant that works for me. You might have to change the endpoint depending on your OAuth server configuration.

POST /oauth2/token
Content-type: application/x-www-form-urlencoded
grant_type=password
username=pravin
password=1234
client_id=xxxxxx
client_secret=xxxxxx
redirect_uri=xxxxxx
response_type=token
state=1234
scope=profile

This authenticates the user and returns JSON with the access token as expected.

Pravin Barton · Mar 27, 2019 go to post

I've had this problem before where scheduled tasks just stopped running, but if I ran ##class(%Sys.Task).CheckSchedule() all the previously scheduled tasks would run once. Restarting my Ensemble instance fixed it. I recommend contacting support if this recurs.

Pravin Barton · May 21, 2019 go to post

I don't know of anything similar to Hibernate in Caché. If you want to encapsulate some data access logic inside of a class, it's helpful to define a class query that other objects can access through dynamic SQL.

More specific to your getByCode example, I use the index auto-generated methods a lot. For example in your dictionary table I would create a unique index on Code Index CodeIndex On Code [ Unique ]; and then use ##class(Whatever.Dictionary).CodeIndexOpen() to open the object I want.

Pravin Barton · Aug 23, 2019 go to post

Thanks for the reply. How can my unit test configure a BPL process to call my mock operation instead of the real operation? Do I have to rewrite the business process to use indirection in the target of each <call> element and make the target a custom setting on the business host?

Pravin Barton · Apr 7, 2017 go to post

Hi Ashok,

It looks like the %session variable isn't accessible in a zen background method. I'd suggest passing the %session.SessionId as an argument into the background method. You can store the JSON data in a global or a persistent class keyed by the session id, and access it from there. Then you can override the OnEndSession() method of the application's event class to delete the data when the session is over: http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY…

Pravin Barton · May 3, 2017 go to post

It turns out the issue is that the default HTML editor can't open files from the server. I had to manually set the Atelier HTML Editor as the default for html files in Preferences > General > Editors > File Associations. This might be because I first installed Eclipse JavaScript and then installed Atelier as a plugin.

Pravin Barton · May 8, 2017 go to post

Hi Bob,

I've done databindings on other SVG components like charts and meters. Unfortunately, sprites don't extend the %ZEN.Component.dataView class and don't have any built-in way to function as view components in Zen MVC. You'll have to set the values programmatically using the getDataByName() and setDataByName() functions of the data controller (documentation).

Pravin Barton · May 8, 2017 go to post

Hi Ruslan,

You can use the rowCount property of the table to find the index of the last row. But keep in mind that it's a string rather than integer, since any number greater than the maximum rows will be "100+" for example. Try using the following javascript:

var table zen("your_tablepane_id")
var rowcount parseInt(table.rowCount)
if (!isNaN(rowcount)) tablepane.selectRow(rowcount)

Pravin Barton · Nov 13, 2017 go to post

Hi Bob,

I've found the Terminal plugin useful for running server side code and viewing the output while I'm developing. You can dock it to the bottom of the Atelier window so it works like the Output window in Studio. The difference is that with Terminal you have to explicitly connect to the sever with SSH or Telnet. There's some relevant documentation here: https://docs.intersystems.com/atelier/latest/topic/com.intersys.eclipse…

Pravin Barton · Nov 13, 2017 go to post

Hi Victor,

You can use the class queries  of %Library.SQLCatalog to find catalog details for tables.

The SQLTables query gives you a list of tables:

select * from %Library.SQLCatalog_SQLTables()

And the SQLFields query will give you a list of fields for a given table:

select * from %Library.SQLCatalog_SQLFields('sample table name')

You can run these queries in the command line using dynamic sql, for example:

set sql = ##class(%SQL.Statement).%New()
write sql.%PrepareClassQuery("%Library.SQLCatalog","SQLTables")
set rs = sql.%Execute()
do rs.%Display()
Pravin Barton · Nov 27, 2017 go to post

If the REST service is giving you JSON data, you'll have to use the Zen proxy object to consume it. Here's some documentation on how to convert JSON data to a proxy object (it doesn't actually require using Zen).

I'll add that Caché 2016.1 added some capabilities that make working with JSON much easier. See this introductory post, and this other post explaining how the syntax changed with 2016.2.

Pravin Barton · Sep 6, 2018 go to post

Never mind, I answered my own question. I should be using the token endpoint instead of the authorize endpoint.