go to post Timothy Leavitt · Mar 28, 2017 That isn't valid XML - I think it'd need to be: <Var Name="AddClassesErrors" Value="",5202,5373,"" /> The " makes it smarter about it being one string, and the extra commas should make it work with the test condition in %Installer.Install:Import: ((","_pIgnoreErrors_",")[(","_$P($system.Status.GetErrorCodes(tSC),",")_",")) This is messy! EDIT: Better option: <Var Name="AddClassesErrors" Value="#{"5202,5373"}" />
go to post Timothy Leavitt · Mar 28, 2017 This just looks like a bug - the generated code is: Do tInstaller.Import(tNSName,tInstaller.Evaluate("${AddonDir}/AddClasses.xml"),"ck","5202","5373","0") While you would expect: Do tInstaller.Import(tNSName,tInstaller.Evaluate("${AddonDir}/AddClasses.xml"),"ck","5202,5373","0") Here's a possible workaround (untested, but the generated code looks better): <Var Name="AddClassesErrors" Value="5202,5373" /> <If Condition='#{##class(%File).Exists("${AddonDir}/AddClasses.xml")}'> <Import File="${AddonDir}/AddClasses.xml" IgnoreErrors="${AddClassesErrors}" Flags="ck" /> </If> EDIT: actual workaround (see discussion below) is to use #{<COS_expression>} (see documentation). <Var Name="AddClassesErrors" Value="#{"5202,5373"}" /> <If Condition='#{##class(%File).Exists("${AddonDir}/AddClasses.xml")}'> <Import File="${AddonDir}/AddClasses.xml" IgnoreErrors="${AddClassesErrors}" Flags="ck" /> </If>
go to post Timothy Leavitt · Mar 24, 2017 Also, if I plug: do ##class(App.Use2).Test() into the original code sample, I see App.Use:Test+1 in the Location property of the exception. How are you ending up with App.Use2:Test+1?
go to post Timothy Leavitt · Mar 24, 2017 Yes - this is in the compiled class metadata. $$$comMemberKeyGet("App.Use2",$$$cCLASSmethod,"Test",$$$cMETHorigin) Or see the Origin property in %Dictionary.CompiledMethod (same for other class members as well).
go to post Timothy Leavitt · Mar 15, 2017 %Library.Routine:Delete with flag = 2 (see class reference) seems to do the trick: Set status = ##class(%Library.Routine).Delete("<name of routine including extension>",2)
go to post Timothy Leavitt · Mar 7, 2017 Here's a solution that works for me: s string="http://www.google.com" set matcher=##class(%Regex.Matcher).%New("(\b(https?|ftp)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]*[-A-Za-z0-9+&@#/%=~_|])",string) w matcher.ReplaceAll("<a href='$1' target='_blank'>$1</a>") Key changes: Remove the enclosing / /gim - this is part of the regex syntax in JS.Add two a-z ranges for case insensitivityRemove JS escaping of slashes (not necessary)
go to post Timothy Leavitt · Mar 2, 2017 I did, but I added it as a comment rather than an answer, so I can't mark it as the accepted answer. Regardless, I followed up this morning, and have been advised that the Management Portal's behavior is a bug and may be "fixed" in the future. The preferred solution would be additional configuration, either at the webserver level or adding more web applications.
go to post Timothy Leavitt · Jan 11, 2017 Actually, this seems to just work (as part of a %CSP.REST subclass, used in a properly configured web application - the Management Portal UI disables the "CSP Files Physical Path" field if you enter a dispatch class, but will save it if you add the physical path first): XData UrlMap [ XMLNamespace = "http://www.intersystems.com/urlmap" ] { <Routes> <!-- ... various Routes/Forwards like in a typical REST API ... --> <!-- Other routes: pass through to file system --> <Route Url="/(.*)" Method="GET" Call="ServeStaticFile" /> </Routes> } ClassMethod ServeStaticFile(pPath As %String) As %Status { #dim %request As %CSP.Request Do %request.Set("FILE",%request.Application_pPath) Quit ##class(%CSP.StreamServer).Page() } If there's nothing more to it than that...
go to post Timothy Leavitt · Dec 15, 2016 Looking at that documentation, one difference between LIST and GROUP_CONCAT is that GROUP_CONCAT lets you specify the separator, while LIST always uses a comma. If you wanted to use a different separator, and your data will never contain commas, then it's as easy as (for example): select home_city as "City", count(*) as "Count", REPLACE(LIST(Name),',',' ') as "Names" from sample.person group by home_city If "your data will never contain commas" is a bad assumption (as it is in the case of Name in Sample.Person), the solution is to use %DLIST and $ListToString. select home_city as "City", count(*) as "Count", $ListToString(%DLIST(Name),' ') as "Names" from sample.person group by home_city %DLIST builds a $ListBuild list, and $ListToString joins the list elements with the specified separator. %DLIST is useful in other cases too - for example, if your data might contain commas and you want to iterate over the aggregated data after running dynamic SQL.
go to post Timothy Leavitt · Dec 9, 2016 One way to do this is comparing $listbuilds of the variable in question. Note also that you can coerce any value to a number with the "+" operator. So, for example: USER>set var = 1 USER>w $lb(var)=$lb(+var) 1 <-- var is a number. USER>set var = "1" USER>w $lb(var)=$lb(+var) 0 <-- var is a not a number. There's not really a simple built-in function to do exactly what you want though (at least that I know of).
go to post Timothy Leavitt · Dec 7, 2016 It's defined in %cspInclude.inc as: #define URLENCODE(%string) $zconvert($zconvert(%string,"O",$replace($$$GETIO,"JSML","UTF8")),"O","URL") See documentation on $ZCONVERT as well. This mentions: “UTF8” which converts (output mode) 16-bit Unicode characters to a series of 8-bit characters. Thus, the characters $CHAR(0) through $CHAR(127) are the same in RAW and UTF8 mode; characters $CHAR(128) and above are converted. “URL” which adds (output mode) or removes (input mode) URL parameter escape characters to a string. Characters higher than $CHAR(255) are represented in Unicode hexadecimal notation: $CHAR(256) = %u0100. So, in short, $$$URLENCODE converts the input from (possibly) 16-bit to 8-bit characters, then adds URL parameter escape characters where they're needed. (For example, escaping slashes.)
go to post Timothy Leavitt · Nov 30, 2016 Another option to consider, if you have the flexibility to do so, would be putting an [IDKey] index on the code property of the code tables, having the code properties in ICDAutoCodeDefn refer to the code table classes rather than being of type %String, and then using implicit joins. I suspect (but haven't verified) that this would perform better than calculated/transient properties, and it's much easier to follow/maintain than normal JOINs. Here's a full example. Looking at a general code table class: Class DC.Demo.CodeTables.CodeTable Extends %Persistent [ Abstract, NoExtent ] { Index Code On Code [ IdKey ]; Property Code As %String; Property Description As %String; } In this example, a specific code table would then extend that class, but don't need to add anything: Class DC.Demo.CodeTables.Team Extends DC.Demo.CodeTables.CodeTable { } Class DC.Demo.CodeTables.Position Extends DC.Demo.CodeTables.CodeTable { } Then, in the class that refers to these code tables: Class DC.Demo.CodeTables.Players Extends %Persistent { Property Name As %String(MAXLEN = 100); Property Position As DC.Demo.CodeTables.Position; Property Team As DC.Demo.CodeTables.Team; ForeignKey Position(Position) References DC.Demo.CodeTables.Position(); ForeignKey Team(Team) References DC.Demo.CodeTables.Team(); } (It's worth considering using foreign keys in cases like this.) To demonstrate how this ends up working from an SQL perspective: Class DC.Demo.CodeTables.Driver { ClassMethod Run() { Do ##class(DC.Demo.CodeTables.Players).%KillExtent() Do ..ShowQueryResults("insert or update into DC_Demo_CodeTables.Position (Code,Description) values ('TE','Tight End')") Do ..ShowQueryResults("insert or update into DC_Demo_CodeTables.Team (Code,Description) values ('NE','New England Patriots')") Do ..ShowQueryResults("insert into DC_Demo_CodeTables.Players (Name,Position,Team) values ('Rob Gronkowski','TE','NE')") Do ..ShowQueryResults("select Name,Position->Description ""Position"",Team->Description ""Team"" from DC_Demo_CodeTables.Players") } ClassMethod ShowQueryResults(pQuery As %String, pParams...) { Write !,"Running query: ",pQuery,! Do ##class(%SQL.Statement).%ExecDirect(,pQuery,pParams...).%Display() } } The output from the Run() method is: USER>Do ##class(DC.Demo.CodeTables.Driver).Run() Running query: insert or update into DC_Demo_CodeTables.Position (Code,Description) values ('TE','Tight End') 1 Row Affected Running query: insert or update into DC_Demo_CodeTables.Team (Code,Description) values ('NE','New England Patriots') 1 Row Affected Running query: insert into DC_Demo_CodeTables.Players (Name,Position,Team) values ('Rob Gronkowski','TE','NE') 1 Row Affected Running query: select Name,Position->Description "Position",Team->Description "Team" from DC_Demo_CodeTables.Players Name Position Team Rob Gronkowski Tight End New England Patriots 1 Rows(s) Affected
go to post Timothy Leavitt · Nov 28, 2016 The Zen equivalent of ##super for JS is invokeSuper. Typically you'd just use: this.invokeSuper('nameOfMethod',arguments); All Zen components (including pages) have this. Here's an example from %ZEN.Component.dataCombo: /// Set the value of a named property.<br> ClientMethod setProperty(property, value, value2) [ Language = javascript ] { switch(property) { case 'itemCount': break; case 'parameters': // set value of parameter: note that value will // be 1-based, so we have to convert it. // changing parameter always forces a query execution, // even in snapshot mode. if ('' != value) { value = value - 1; if (this.parameters[value]) { if (this.parameters[value].value != value2) { this.parameters[value].value = value2; this.clearCache(); } } } break; default: // dispatch return this.invokeSuper('setProperty',arguments); break; } return true; }
go to post Timothy Leavitt · Nov 17, 2016 Hi Sebastian, Sorry this went unanswered for so long. In case you haven't figured it out yet, one solution is: Do tExam.parameters.Insert(tParm) Do %page.%AddComponent(tParm) Do %page.%AddChild(tExam) %AddComponent registers the parameter with the page, so you won't get that error. (There might be a better way to do this, but %AddComponent at least seems to work.) Here's a full example: Class DC.Demo.DynamicComboPage Extends %ZEN.Component.page { Property count As %ZEN.Datatype.integer [ InitialExpression = 0 ]; /// This XML block defines the contents of this page. XData Contents [ XMLNamespace = "http://www.intersystems.com/zen" ] { <page xmlns="http://www.intersystems.com/zen" title=""> <button onclick="zenPage.AddCombo();" caption="Add a Combo Box!" /> </page> } Method AddCombo() [ ZenMethod ] { #dim %page As DC.Demo.DynamicComboPage Set ..count = ..count + 1 Set tParm = ##class(%ZEN.Auxiliary.parameter).%New() Set tParm.id = "parameter_"_..count Set tParm.name = "parameter_"_..count Set tExam = ##class(%ZEN.Component.dataCombo).%New() Set tExam.id = "dataCombo_"_..count Set tExam.label = "Test" Do tExam.parameters.Insert(tParm) Do %page.%AddComponent(tParm) Do %page.%AddChild(tExam) } }
go to post Timothy Leavitt · Nov 17, 2016 I think these error codes correspond to Windows system error codes - see this page.So -2 corresponds to "ERROR_FILE_NOT_FOUND" and -3 to "ERROR_PATH_NOT_FOUND"It seems more likely that you would get -2 from ##class(%File).Delete(file,.return). passing a directory that simply doesn't exist to RemoveDirectory results in -3. Is RemoveDirectory actually returning that error code, or perhaps a different one?
go to post Timothy Leavitt · Nov 15, 2016 Certain configuration steps must be taken to enable access to % classes in non-/csp/sys/... web applications. Here's a link to the documentation about that.Also, rather than creating your own % classes, you might consider either using the %All namespace (see documentation) or starting the package name with %Z. Starting the class's package name with %Z will prevent the class from being overwritten upon upgrade (which is important!) and, as a bonus, would also allow access from non-/csp/sys/ web applications automatically (according to the documentation at the first link).
go to post Timothy Leavitt · Nov 10, 2016 Another option is: ##class(%SYS.System).GetNodeName(1) (see class reference)
go to post Timothy Leavitt · Nov 9, 2016 CSPSHARE (see old documentation) would probably do the trick. Here's some newer documentation too.