go to post Timothy Leavitt · Apr 30, 2018 For completeness, here's an extension of the above example that actually works the way you'd expect (using PublicList to handle scope, and New to avoid leaking any variables): Class DC.Demo.Indirection { ClassMethod Driver() [ PublicList = x ] { New x Do ..Run() Do ..RunSets() Do ..Run() } ClassMethod Run() [ PublicList = x ] { Set x = 5 Try { Write @"x" } Catch e { Write e.DisplayString() } Write ! Try { Xecute "write x" } Catch e { Write e.DisplayString() } Write ! } ClassMethod RunSets() [ PublicList = x ] { Set x = 5 Set @"x" = 42 Write x,! Xecute "set x = 42" Write x,! } } Results: USER>d ##class(DC.Demo.Indirection).Driver() 5 5 42 42 5 5 USER>w USER> But that doesn't mean it's a good idea to do things like this.
go to post Timothy Leavitt · Apr 30, 2018 This can be done with the requiredMessage property of %ZEN.Component.control. There are two ways to accomplish this: 1. Just add the requiredMessage attribute Class DC.Demo.ZenLocalization Extends %ZEN.Component.page { XData Contents [ XMLNamespace = "http://www.intersystems.com/zen" ] { <page xmlns="http://www.intersystems.com/zen"> <form> <text label="Descrição" required="true" requiredMessage="obrigatório." /> <submit caption="Enviar" /> </form> </page> } } Problem is, you'd need to do that in a lot of different places. Instead, you could... 2. Use custom components that subclass built-in control types. Sample component: Class DC.Demo.Component.text Extends %ZEN.Component.text [ System = 3 ] { /// Feel free to customize this. Parameter NAMESPACE = "https://community.intersystems.com/post/change-language-required"; /// Value displayed in alert box by the form <method>validate</method> /// method when this control is required and does not have a value.<br> /// This is a localized value. Property requiredMessage As %ZEN.Datatype.caption [ InitialExpression = "obrigatório." ]; } Sample page using component: Class DC.Demo.ZenLocalization Extends %ZEN.Component.page { XData Contents [ XMLNamespace = "http://www.intersystems.com/zen" ] { <page xmlns="http://www.intersystems.com/zen" xmlns:custom="https://community.intersystems.com/post/change-language-required"> <form> <custom:text label="Descrição" required="true" /> <submit caption="Enviar" /> </form> </page> } } I was hoping there would be a way to use Zen's localization features to do this, but it seems like that isn't an option, unfortunately;"required." is hard-coded as the InitialExpression for requiredMessage in %ZEN.Component.control, and may only be localized within a page using the component if a non-default value is specified.
go to post Timothy Leavitt · Apr 30, 2018 Most of the time, you don't really need to use XECUTE/indirection/etc - there is usually a better way to accomplish the same thing. Furthermore, in modern coding practices, XECUTE and name indirection probably will not work the way you expect, because they can only access public variables (not those in the scope of your method). For example, given the following class: Class DC.Demo.Indirection { ClassMethod Run() { Set x = 5 Try { Write @"x" } Catch e { Write e.DisplayString() } Write ! Try { Xecute "write x" } Catch e { Write e.DisplayString() } } ClassMethod RunSets() { Set x = 5 Set @"x" = 42 Write x,! Xecute "set x = 42" Write x,! } } The output when run in terminal is: USER>Do ##class(DC.Demo.Indirection).Run() <UNDEFINED> 9 zRun+3^DC.Demo.Indirection.1 x <UNDEFINED> 9 zRun+9^DC.Demo.Indirection.1 x USER>d ##class(DC.Demo.Indirection).RunSets() 5 5 USER>d ##class(DC.Demo.Indirection).Run() 42 42 Your code certainly should not rely on such scoping behavior. There are ways around this (for example, adding [ PublicList = x ] to the method signature, using New, etc.), but they are messy and difficult to understand and maintain. In your particular case, using a local array would probably be a better approach - for example: set x(counter) = DIAGS Although if you provided more context for why you think you need to dynamically set variables, the community might have other suggestions.
go to post Timothy Leavitt · Apr 27, 2018 There was an issue with database compaction in 2014.1.1 that could cause database degradation - see this alert: https://www.intersystems.com/support-learning/support/product-news-alerts/support-alert/alert-database-compaction/Given that, I would recommend contacting the experts in the Worldwide Response Center (InterSystems Support) to help investigate. Running an integrity check on the database you compacted would be a good start - see https://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GCDI_integrity#GCDI_integrity_verify_portal.
go to post Timothy Leavitt · Apr 26, 2018 What Ensemble version are you running? (In Terminal, write $zv)
go to post Timothy Leavitt · Mar 29, 2018 Another slightly more lightweight approach: Class DC.Demo.combobox Extends %ZEN.Component.combobox [ System = 3 ] { Parameter NAMESPACE = "http://community.intersystems.com/demo"; /// Notification that this component is about to become modal. ClientMethod onStartModalHandler(zindex) [ Language = javascript ] { this.invokeSuper('onStartModalHandler',arguments); this.fixLinks(this.getDropDownDiv()); } /// Look for all children of specified element, and change links with href="#" to also have onclick = "return false;" ClientMethod fixLinks(element) [ Language = javascript ] { for (var i = 0; i < element.children.length; i++) { this.fixLinks(element.children[i]); } if (element.getAttribute("href") == "#") { element.onclick = function() { return false; }; } } }
go to post Timothy Leavitt · Mar 28, 2018 There are also complete details of this and other differences in Studio Extension support between Studio and Atelier in the Atelier documentation.
go to post Timothy Leavitt · Mar 28, 2018 We specifically chose not to support this capability in Atelier, other than for the special case of launching web pages. As an implementation of Studio Source Control, running an executable on the client only makes sense in cases where the "server" and the "client" are always the same, and for that model the source control plugins publicly and freely available in the Eclipse ecosystem are far superior in terms of support and usability.I would be interested to hear more details of your use case. What version control system are you using? Git?(Note to other readers: "UserAction = 3" and other details of the Studio Extension framework are documented here.)
go to post Timothy Leavitt · Mar 20, 2018 What are you trying to do? This? USER>Set rm=##class(%Regex.Matcher).%New("[0-9]","Word 123456") USER>Write rm.ReplaceAll(".") Word ...... Or: USER>Set rm=##class(%Regex.Matcher).%New("[0-9]+","Word 123456") USER>Write rm.ReplaceAll(".") Word .
go to post Timothy Leavitt · Feb 6, 2018 USER>d $System.OBJ.GetPackageList(.classes,"%CSP.Documatic") zw classes classes("%CSP.Documatic.CubeInfo")="" classes("%CSP.Documatic.PrintClass")=""
go to post Timothy Leavitt · Dec 12, 2017 Thanks Eduard, this looks promising. I'll try it out and post any interesting results.
go to post Timothy Leavitt · Nov 28, 2017 You could have your unit test class extend the class with the [Private] method, or (if that doesn't make sense or causes issues) write another class that your unit test loads that extends the class with the [Private] method and has another method that wraps it.
go to post Timothy Leavitt · Nov 21, 2017 There are two good approaches to this, both relying on an onchange event handler on the first dataCombo. The first, simpler solution would be to just have an onchange event handler set the value directly: <dataCombo id="Grupo_Producto" name="Grupo_Producto" dataBinding="bdGrupo" size="40" label="(*) Grupo" dropdownWidth="400" dropdownHeight="100" loadingMessage="Cargando..." columnHeaders="Codigo, Nombre" queryClass="Innova.CL.Core.BD.Configuracion.Operacional.Grupo" queryName="obtieneGrupoProd" sqlLookup="SELECT Nombre FROM Innova_CL_Core_BD_Configuracion_Operacional.Grupo Where ID = ?" onchange="zen('Linea_Producto').setProperty('parameters',1,zenThis.getValue());" > </dataCombo> However, if you have multiple components on the page that depend on the current value of Grupo_Producto, it might be simpler to use a property of the page that has the value of the component copied to it when that value changes. This is handy because you can reference the value in a Zen expression, e.g., #(%page.GrupoProducto)#, rather than needing to copy the same value to lots of places. Here is an example with two dataCombos in the SAMPLES namespace: Class DC.Demo.ComboBox Extends %ZEN.Component.page { Property homeState As %ZEN.Datatype.string; XData Contents [ XMLNamespace = "http://www.intersystems.com/zen" ] { <page xmlns="http://www.intersystems.com/zen"> <dataCombo id="homeState" label="State" sql="select distinct Home_State from Sample.Person order by Home_State" onchange="zenPage.dataComboChanged(zenThis)" /> <dataCombo id="homeCity" label="City" sql="select distinct %exact Home_City from Sample.Person where Home_State = ? order by Home_City"> <parameter value="#(%page.homeState)#" /> </dataCombo> </page> } ClientMethod dataComboChanged(component) [ Language = javascript ] { // Update the page property's value. // Note: page property and component ID don't necessarily need to match, but it's convenient if they do. // (Imagine a cascade of dataCombos with dependent values.) zenPage.setProperty(component.id,component.getValue()); // Possibly also call refreshContents() on components referencing properties that may have changed, // and/or clear values that may have been invalidated by the change. // dataCombo does not need this, but other components (e.g., tablePane) would. } }
go to post Timothy Leavitt · Sep 25, 2017 An Ensemble production with a REST passthrough service and operation (as described in https://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=EESB_passthrough_walkthrough#EESB_passthrough_rest_walk) might provide a relatively simple solution.
go to post Timothy Leavitt · Sep 20, 2017 I can't speak to a fix, but a few thoughts:There is a critical difference between Studio and Atelier projects: Atelier projects are not even present on the server / in the database. This may prevent them from being used interchangeably with Studio projects for purposes of your source control extension and change control processes.Because of this difference, it would probably not be a good idea to populate ^||%Studio.Project with the name of the project in Atelier.One option might be updating your source control extension (perhaps adding new menu items and UserAction/AfterUserAction handling for them) to support creating a %Studio.Project and associating/dissociating files with one, even from Atelier. This would support mixed use of Studio and Atelier and make migration simpler.If you want to be able to detect in your source control extension code whether it is being called from Studio or Atelier, you can look at the StudioVersion property (defined in %Studio.Extension.Base). Particularly, this will contain the string "Atelier" if invoked from Atelier.A nice enhancement for Atelier might be supporting a project definition that is shared between the client and server.
go to post Timothy Leavitt · Sep 20, 2017 The current Atelier project is not exposed. What is your intended use case for it?Regarding $Username - I suspect that the /api/atelier web application on your instance has only "Unauthenticated" selected under "Allowed Authentication Methods." If you change this to "Password" only and restart Atelier you should see $Username populated correctly.
go to post Timothy Leavitt · Sep 14, 2017 If you're really interested in efficiency, one option might be put the list into a local array with $ListNext and then iterate over the array in reverse (either with a decrementing counter in a for loop, or using $order(array(sub),-1)). For example, rather than: For i=$ListLength(tInputList):-1:1 { Set tValue = $ListGet(tInputList,i) // Do something with tValue } You could use something like the following (which accounts for null values in the $ListBuild list by skipping that index in the integer-subscripted array): Kill tArray Set tPointer = 0 While $ListNext(tInputList,tPointer,tValue) { If $Data(tValue) { Set tArray($i(tArray)) = tValue } Else { Set tArray = tArray + 1 } } Set tKey = "" For i=$Get(tArray):-1:1 { If $Data(tArray(i),tValue) { // Do something with tValue } } For "large enough" lists (the threshold for this depends on the length of the list elements), the second approach will be faster. (In testing, this performs better than putting the reversal of the original $ListBuild list into another $ListBuild list.) More generally speaking: if you have a list that you only append to the end of (rather than needing to insert/shift elements randomly in the middle of it), and will need to iterate over it in reverse, a local array with integer subscripts is a better choice of data structure than a $ListBuild list.
go to post Timothy Leavitt · Aug 22, 2017 That's not actually the case. If you look at the .INT code for the generated %CreatePage method of a Zen page with , in columnHeaders (I tested this out with ZENDemo.FormDemo in the SAMPLES namespace), you'll see something like: Set dtCmb7.columnHeaders="House number,apartment" The numeric character reference is converted to the Unicode character when the Zen XML is imported; the reason , doesn't work is that it's converted to a plain old comma, which is no different from just putting a comma in the XML in terms of what Zen does with it.
go to post Timothy Leavitt · Aug 22, 2017 Because Zen is written in XML, "," in an attribute actually does get turned in to a comma in the generated code (see %CreatePage in the .INT code), and then is treated like a comma when the column headers are $piece'd out.
go to post Timothy Leavitt · Aug 22, 2017 To expand on Sean's answer from the comment section, perhaps try the numeric character reference for the fullwidth comma: columnHeaders="House number,apartment" Another option would be to create a custom Zen component that extends %ZEN.Component.dataCombo and handles delimiters differently for the columnHeaders property. Unfortunately, this approach would require overriding %DrawDropDownContents, which is an enormous method and flagged as [Internal] - best avoided, in my opinion!