go to post Timothy Leavitt · Nov 3, 2021 I wonder if the business host is running an old version of the class. (That would explain why the <UNDEFINED> reports a weird line of code, and why the error hasn't gone away after you fixed it.) Maybe try disabling + reenabling the business operation / restarting the production?
go to post Timothy Leavitt · Oct 13, 2021 One note here, $$Quote^%qcr will represent control characters with $c(decimalAsciiCode) syntax - like the newlines in this case. I think there's some more official classmethod that's equivalent, but I don't recall where it is off the top of my head.
go to post Timothy Leavitt · Oct 13, 2021 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.
go to post Timothy Leavitt · Aug 23, 2021 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.
go to post Timothy Leavitt · Jul 9, 2021 Internally, we've developed a mock framework for ObjectScript based on Mockito that can do exactly this (and many other things). If we're able to get it out on the Open Exchange at some point I'll let you know.
go to post Timothy Leavitt · Jun 24, 2021 I'd recommend using browser developer tools to inspect the CSS and see what's causing it - I wouldn't expect the rows to be particularly tall, but there could be stray CSS from somewhere else that is making them so. See docs on using Chrome's developer tools (for example - but they're all similar these days) here: https://developer.chrome.com/docs/devtools/css/
go to post Timothy Leavitt · Jun 23, 2021 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); } }
go to post Timothy Leavitt · Jun 10, 2021 This is a little messy and I'm going to report part of the answer as a bug internally. But regardless, here's one way to make it work - in short, have all of the things that could be listed as a recipient extend a common parent class, and in that class override %JSONNew to detect which type it is. Class DC.Demo.Container Extends (%RegisteredObject, %JSON.Adaptor) { Property recipient As DC.Demo.Recipient; ClassMethod Demo() { for json = {"recipient":{"dob":"2021-06-10"}}, {"recipient":{"reference":"foo"}} { set inst = ..%New() do inst.%JSONImport(json) write !,json.%ToJSON(),!,$classname(inst.recipient),! } } } Class DC.Demo.Recipient Extends (%RegisteredObject, %JSON.Adaptor) { /// Get an instance of an JSON enabled class.<br><br> /// /// You may override this method to do custom processing (such as initializing /// the object instance) before returning an instance of this class. /// However, this method should not be called directly from user code.<br> /// Arguments:<br> /// dynamicObject is the dynamic object with thee values to be assigned to the new object.<br> /// containerOref is the containing object instance when called from JSONImport. ClassMethod %JSONNew(dynamicObject As %DynamicObject, containerOref As %RegisteredObject = "") As %RegisteredObject { // This is weird: shouldn't need to reference .recipient here if dynamicObject.recipient.%IsDefined("dob") { quit ##class(DC.Demo.Patient).%New() } elseif dynamicObject.recipient.%IsDefined("reference") { quit ##class(DC.Demo.Reference).%New() } else { quit ..%New() } } } Class DC.Demo.Reference Extends DC.Demo.Recipient { Property reference As %String; } Class DC.Demo.Patient Extends DC.Demo.Recipient { Property dob As %Date; } Output is: d ##class(DC.Demo.Container).Demo() {"recipient":{"dob":"2021-06-10"}} DC.Demo.Patient {"recipient":{"reference":"foo"}} DC.Demo.Reference Only problem is, %JSONNew (as advertised in class reference documentation) should get the %DynamicObject representing the object itself, not the parent %DynamicObject. This would only really work if each type is used in exactly one context like this, which seems unlikely.
go to post Timothy Leavitt · May 19, 2021 First off, it's generally best to avoid xecute. ;) In the first case, routine is private (it isn't visible in the xecute stack frame). In the second, it's public, so it is visible there. You could get the best of both worlds with: ClassMethod Run() { set routine="variable" set call="(routine) write routine,!" xecute (call, routine) quit } For more info see https://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY...
go to post Timothy Leavitt · Apr 12, 2021 Worldwide Response Center (WRC) For Immediate Response Phone:+1-617-621-0700+44 (0) 844 854 29170800615658 (NZ Toll Free)1800 628 181 (Aus Toll Free) Email:support@intersystems.com Online:WRC DirectEmail support@intersystems.com for a login.
go to post Timothy Leavitt · Mar 25, 2021 The closest thing to what you're looking for is %VID (link to documentation) You can use this even without an actual view as follows (for example): select %VID "RowNumber",* from (select top all AirportLocation, FAADistrictOffice from Aviation.Event order by FAADistrictOffice, AirportLocation)
go to post Timothy Leavitt · Mar 12, 2021 With what we've done the syntax ends up looking like: Class DC.Demo.Hierarchy Extends %Persistent [ MemberSuper = AppS.Index.Methods ] { Property message As %String; Property login As %String; Property parentId As DC.Demo.Hierarchy [ SqlFieldName = parent_id ]; Index parentId On parentId [ Type = bitmap ]; ClassMethod RunDemo() { Do ..%KillExtent() &sql(insert into DC_Demo.Hierarchy (message, login, parent_id) values ('Bacon ipsum dolor amet pork shoulder ribs', 'User 1', null)) &sql(insert into DC_Demo.Hierarchy (message, login, parent_id) values ('BGouda croque monsieur emmental.', 'User 2', 1)) &sql(insert into DC_Demo.Hierarchy (message, login, parent_id) values ('Manchego fromage frais airedale', 'User 3', 2)) Do ##class(%SQL.Statement).%ExecDirect(, "select id, message, parent_id from DC_Demo.Hierarchy "_ "where id %FIND DC_Demo.Hierarchy_parentIdFind(2,'all descendants')").%Display() Do ##class(%SQL.Statement).%ExecDirect(, "select id, message, parent_id from DC_Demo.Hierarchy "_ "where id %FIND DC_Demo.Hierarchy_parentIdFind(3,'all related')").%Display() } } Because there's a self-referencing property with a bitmap index, the hierarchy support is automatic via the MemberSuper class. Output is: d ##class(DC.Demo.Hierarchy).RunDemo() ID message parent_id 2 BGouda croque monsieur emmental. 1 3 Manchego fromage frais airedale 2 2 Rows(s) Affected ID message parent_id 1 Bacon ipsum dolor amet pork shoulder ribs 2 BGouda croque monsieur emmental. 1 3 Manchego fromage frais airedale 2 3 Rows(s) Affected
go to post Timothy Leavitt · Mar 12, 2021 There's nothing built-in for this, but you can simulate it via custom class queries or %SQL.AbstractFind. I have an implementation of %SQL.AbstractFind/%Library.FunctionalIndex that does some things with hierarchies but falls short of the capabilities you linked in the Oracle doc. Specifically, it can find all ancestors/descendants/both (the whole tree) in a hierarchy efficiently, but it doesn't follow the same rules around ordering and won't let you do paths and such. (I'd want to clean it up a good deal before sharing, but that's probably worthwhile at some point.)
go to post Timothy Leavitt · Mar 10, 2021 @Daniel Bertozzi , following up - I downloaded ImageMagick and the following works just fine for me (though I'm a little surprised at how slow it is): Class DC.Demo.ImageMagick { ClassMethod Convert(inFile As %String = "C:\Temp\ImageMagick\inFile.jpg", outFile As %String = "C:\Temp\ImageMagick\outFile.jpg") { Do $zf(-100,"","magick",inFile,"-resize","640x480",outFile) } } I think the likely issue is that ImageMagick isn't on your PATH. You'll need to restart your instance for it to pick up PATH changes, so this might be the root cause if you just installed ImageMagick. Could also be interesting to run with the /SHELL flag and see if that works. Hopefully this helps!
go to post Timothy Leavitt · Mar 8, 2021 In communication outside of this thread, turns out the issue stemmed from a failed %Save() - something like: Class DC.Demo.OREFOIDDemo Extends %Persistent { Property Foo As %String(VALUELIST = ",Bar"); ClassMethod RunDemo() { Do ..%KillExtent() Set thingOne = ..%New() Set thingOne.Foo = "Bar" Do thingOne.%Save() Set thingOne.Foo = "Baz" Do thingOne.%Save() // Later, and elsewhere, because thingOne happens to be in memory, // it appears that the value "Baz" has been persisted: Set thingTwo = ..%OpenId(thingOne.%Id()) Write !,"thingOne = ",thingOne Write !,"thingTwo = ",thingTwo Write !,"thingOne.Foo = ",thingOne.Foo Write !,"thingTwo.Foo = ",thingTwo.Foo Write !,"thingOne.FooGetStored(thingOne.%Id()) = ",thingOne.FooGetStored(thingOne.%Id()) Do thingTwo.%Reload() Write !,"After thingTwo.%Reload(), thingOne.Foo = ",thingOne.Foo Write !,"After thingTwo.%Reload(), thingTwo.Foo = ",thingOne.Foo } } Which produces output: Do ##class(DC.Demo.OREFOIDDemo).RunDemo() thingOne = 15@DC.Demo.OREFOIDDemo thingTwo = 15@DC.Demo.OREFOIDDemo thingOne.Foo = Baz thingTwo.Foo = Baz thingOne.FooGetStored(thingOne.%Id()) = Bar After thingTwo.%Reload(), thingOne.Foo = Bar After thingTwo.%Reload(), thingTwo.Foo = Bar
go to post Timothy Leavitt · Feb 25, 2021 Poking around a bit, here's the best explanation I could find for how to implement such behavior: https://stackoverflow.com/a/7317311/2323797
go to post Timothy Leavitt · Feb 24, 2021 Given that SQLCODE is 100 (in other comment thread), might you have an index on %ConfigName that has not been built? What do you see from the following queries? select * from X_X.X where %ConfigName IN ('X_X_X','Y_Y_Y') select * from %IGNOREINDEX * X_X.X where %ConfigName IN ('X_X_X','Y_Y_Y') Also, is X_X.X the table corresponding to the class where the property is defined?
go to post Timothy Leavitt · Jan 29, 2021 Short answer: yes, ZPM can manage whatever you want other than ObjectScript, it just takes a little extra work. Longer answer: This "extra work" involves writing a class that extends %ZPM.PackageManager.Developer.Processor.Abstract and overrides OnBeforePhase or OnAfterPhase, then implementing whatever behavior you want. You can see a bunch of classes in the %ZPM.PackageManager.Developer.Processor package that do this. I'd imagine being able to put something in the Resources element of module.xml like: <Resource Name="/external-dependencies/some-package" ProcessorClass="MyPackage.AptGetInstall" /> Where MyPackage.AptGetInstall overrides OnAfterPhase and for the "Activate" phase runs apt-get install <name of resource following "/external-dependencies/" The catch: If you want to support all operating systems, think about what the Windows equivalent would be and/or add defensive coding to avoid trying to run the command on Windows.
go to post Timothy Leavitt · Jan 22, 2021 Here's how I'd typically do something like that, going back to my example from one of your earlier questions and expanding a bit. The persistent class as a new %Boolean property named "Toggleable" (not a magical name - you can call it whatever you like), and the tablePane has <column> elements added. The query supplying data for the table has the ID and Toggleable columns added as well. The "Toggleable" column has OnDrawCell defined to provide custom HTML for the cells in the table; this references an ObjectScript method (which doesn't need to be, and in fact shouldn't be, a ZenMethod). That ObjectScript method renders HTML which calls a ZenMethod to actually do the update, using query results from the %query variable (which is a special thing that's available for use in OnDrawCell). No table refresh is needed, but if you refresh the page you'll see that the checkbox values have indeed been persisted. Here's the Zen XData change: <tablePane id="myTable" OnCreateResultSet="CreateResultSet" valueColumn="ID"> <parameter value="" /> <parameter value="" /> <parameter value="" /> <column colName="ID" hidden="true" /> <column colName="Name" /> <column colName="SomeDate" /> <column colName="Toggleable" OnDrawCell="DrawToggleCell" /> </tablePane> And corresponding ObjectScript methods: /// Note: this doesn't need to be a ZenMethod. ClassMethod DrawToggleCell(pTable As %ZEN.Component.tablePane, pColName As %String, pSeed As %String) As %Status { &html<<input type="checkbox" #($Case(%query("Toggleable"),1:"checked",:""))# onchange="zenPage.Toggle(#(..QuoteJS(%query("ID")))#,this.checked)" />> Quit $$$OK } ClassMethod Toggle(pID As %String, pValue As %Boolean) [ ZenMethod ] { Set obj = ##class(DC.Demo.SampleData).%OpenId(pID,,.sc) $$$ThrowOnError(sc) Set obj.Toggleable = pValue $$$ThrowOnError(obj.%Save()) } Here's the full sample: Class DC.Demo.SampleData Extends (%Persistent, %Populate) { Property Name As %String; Property SomeDate As %Date; Property Toggleable As %Boolean; } Class DC.Demo.ZenPage Extends %ZEN.Component.page { XData Contents [ XMLNamespace = "http://www.intersystems.com/zen" ] { <page xmlns="http://www.intersystems.com/zen"> <fieldSet legend="Filter" layout="horizontal"> <text label="Name Starts With:" onchange="zen('myTable').setProperty('parameters',1,zenThis.getValue())" /> <dateText label="Start Date:" onchange="zen('myTable').setProperty('parameters',2,zenThis.getValue())" /> <dateText label="End Date:" onchange="zen('myTable').setProperty('parameters',3,zenThis.getValue())" /> </fieldSet> <tablePane id="myTable" OnCreateResultSet="CreateResultSet" valueColumn="ID"> <parameter value="" /> <parameter value="" /> <parameter value="" /> <column colName="ID" hidden="true" /> <column colName="Name" /> <column colName="SomeDate" /> <column colName="Toggleable" OnDrawCell="DrawToggleCell" /> </tablePane> <button onclick="zenPage.Populate()" caption="Repopulate Data" /> </page> } ClassMethod CreateResultSet(Output pSC As %Status, pInfo As %ZEN.Auxiliary.QueryInfo) As %SQL.Statement { Set nameFilter = pInfo.parms(1) Set startDateFilter = pInfo.parms(2) // Will be in ODBC format Set endDateFilter = pInfo.parms(3) // Will be in ODBC format Set query = ##class(%SQL.Statement).%New() Set query.%SelectMode = 1 Set sql = "select ID, Name, SomeDate, Toggleable from DC_Demo.SampleData" Set conditions = "" If (nameFilter '= "") { Set conditions = conditions_$ListBuild("Name %STARTSWITH ?") Set parameters($i(parameters)) = nameFilter } If (startDateFilter '= "") && (endDateFilter '= "") { // Yes, this could just be independent AND'ed conditions on start/end date, // which would reduce code complexity, but you wanted to see BETWEEN, so... :) Set conditions = conditions_$ListBuild("SomeDate BETWEEN ? and ?") Set parameters($i(parameters)) = startDateFilter Set parameters($i(parameters)) = endDateFilter } ElseIf (startDateFilter '= "") { Set conditions = conditions_$ListBuild("SomeDate >= ?") Set parameters($i(parameters)) = startDateFilter } ElseIf (endDateFilter '= "") { Set conditions = conditions_$ListBuild("SomeDate <= ?") Set parameters($i(parameters)) = endDateFilter } If (conditions '= "") { Set sql = sql _ " where "_$ListToString(conditions," and ") } Set pSC = query.%Prepare(sql) If $$$ISERR(pSC) { Quit $$$NULLOREF } //Important: Reduce to only the parameters specified/used. Kill pInfo.parms Merge pInfo.parms = parameters Quit query } ClassMethod Populate() [ ZenMethod ] { Do ##class(DC.Demo.SampleData).%KillExtent() Do ##class(DC.Demo.SampleData).Populate(20,,,,0) &js<zen('myTable').executeQuery();> } /// Note: this doesn't need to be a ZenMethod. ClassMethod DrawToggleCell(pTable As %ZEN.Component.tablePane, pColName As %String, pSeed As %String) As %Status { &html<<input type="checkbox" #($Case(%query("Toggleable"),1:"checked",:""))# onchange="zenPage.Toggle(#(..QuoteJS(%query("ID")))#,this.checked)" />> Quit $$$OK } ClassMethod Toggle(pID As %String, pValue As %Boolean) [ ZenMethod ] { Set obj = ##class(DC.Demo.SampleData).%OpenId(pID,,.sc) $$$ThrowOnError(sc) Set obj.Toggleable = pValue $$$ThrowOnError(obj.%Save()) } }