go to post Julius Kavay · Sep 16, 2021 <Nitpicking ON> For the LAST() function you can also use just an expression: LAST(y,m) QUIT $ZDATEH(m=12+y*100+(m#12)+1*100+1,8)-1 <Nitpicking OFF> By the way, if you are just interested, how many days a month in a given year has, there is a simple formula: LastDay(y,m) quit $s(m-2:m\8+m#2+30, 1:y#4=0+28) In the above formula, it's OK to use short leap year calculation (y#4=0) for contemporary dates (date from 1901 until 2099) else case you have to stick to the long format: (y#4=0)-(y#100=0)+(y#400=0)
go to post Julius Kavay · Sep 6, 2021 As we know, an email can contain one or more attached email(s), which in turn can contain other attached mails ... so we have to work recursively until the end. The following program excerpt should show the concept: ClassMethod GetMails(popAddr, popUser, popPass) { set pop=##class(%Net.POP3).%New(), cnt=0 if pop.Connect(popAddr, popUser, popPass) { if pop.GetMailBoxStatus(.cnt,.siz) { for id=1:1:cnt { if pop.Fetch(id,.msg,0) do ..getParts(pop,msg,id,0) } } } } ClassMethod getParts(pop, msg, id, lev) { for i=1:1:msg.Parts.Count() { set part=msg.Parts.GetAt(i) set typ=$zcvt(part.CntentType,"l") set dsp=part.Headers.GetAt("content-disposition") if typ["message/rfc822" { // this is another email, we step one level deeper if pop.GetAttachedEmail(msg.Parts.GetAt(i), .new) do ..getParts(pop, new, id, lev+1) } elseif dsp["attachment" { // attachments if typ["application/pdf" { } elseif typ["image" { } elseif ... { } } elseif typ["text" { } elseif ... { } } }
go to post Julius Kavay · Sep 1, 2021 You could start with select * from %SYS.Task and look at the TaskClass column, this gives you the entry point for a given task.
go to post Julius Kavay · Aug 31, 2021 You could create an abstract class, which adds all the information you need to your class(es), see the example below.Then include this class into the class(es) where you need this kind of information: Class Your.Class Extends (%Persistent, DC.ClassInfo) { ... } Class Your.OtherClass Extends (%Persistent, DC.ClassInfo) { ... } and use it as follows: set allProps = ##class(Your.Class).PropertyInfo() write allProps.%ToJSON() --> [prop1, prop2, ...] // here you get the names of all properties set oneProp = ##class(Your.Class).PropertyInfo("aPropertyName") --> {"Type":"%String"} // Info about one property The same goes for the methods too. Class definition for the (example) DC.ClassInfo class: Class DC.ClassInfo [ Abstract ] { /// Return information about properties /// /// 1) return list of all properties /// obj.PropertyInfo() --> [Propertynam1, Propertyname2, Propertyname3, ...] /// /// 2) return info for a specific property /// obj.PropertyInfo(propertyname) --> {"Type":type, "MaxLen":nn, "Scale":n, ...} /// ClassMethod PropertyInfo(name = "") As %DynamicObject [ CodeMode = objectgenerator ] { set prp=%compiledclass.Properties, all={} for i=1:1:prp.Count() { set p=prp.GetAt(i), inf={} // Add all the infos you need... set inf.Type=p.Type set x=p.Parameters.GetAt("MAXLEN") set:x]"" inf.MaxLen=x set x=p.Parameters.GetAt("SCALE") set:x]"" inf.Scale=x do all.%Set(p.Name,inf) } do %code.WriteLine($c(9)_"set prp="_all.%ToJSON()) do %code.WriteLine($c(9)_"if name]"""" quit prp.%Get(name)") do %code.WriteLine($c(9)_"set itr=prp.%GetIterator(), names=[]") do %code.WriteLine($c(9)_"while itr.%GetNext(.k) { do names.%Push(k) }") do %code.WriteLine($c(9)_"quit names") quit $$$OK } /// Return information about properties /// /// 1) return list of all properties /// obj.PropertyInfo() --> [Propertynam1, Propertyname2, Propertyname3, ...] /// /// 2) return info for a specific property /// obj.PropertyInfo(propertyname) --> {"Type":type, "MaxLen":nn, "Scale":n, ...} /// ClassMethod MethodInfo(name = "") As %DynamicObject [ CodeMode = objectgenerator ] { set mth=%compiledclass.Methods, all={} for i=1:1:mth.Count() { set m=mth.GetAt(i) // Add all the infos you need... set inf={}, spc=m.FormalSpecParsed set inf.SqlProc=m.SqlProc set inf.ClassMethod=m.ClassMethod set inf.ReturnType=m.ReturnType set inf.Args=[] for j=1:1:$ll(spc) { set arg={}, itm=$li(spc,j), arg.Name=$li(itm), arg.Type=$li(itm,2) set arg.ByRef=$li(itm,3)="&", arg.DefValue=$li(itm,4) do inf.Args.%Push(arg) } do all.%Set(m.Name,inf) } do %code.WriteLine($c(9)_"set mth="_all.%ToJSON()) do %code.WriteLine($c(9)_"if name]"""" quit mth.%Get(name)") do %code.WriteLine($c(9)_"set itr=mth.%GetIterator(), names=[]") do %code.WriteLine($c(9)_"while itr.%GetNext(.k) { do names.%Push(k) }") do %code.WriteLine($c(9)_"quit names") quit $$$OK } } The class should work, but it's only partially tested. Also, you may want to omit all the inherited properties and methods set p=prp.GetAt(i) continue:p.Origin '= %class.Name // skip inherited propps set m=mth.GetAt(i) continue:m.Origin '= %class.Name // skip inherited methods it's up to you. Also you can add other informations, depending on your needs, see the respective classes (%Dictionary.CompiledClass, %Dictionary.CompiledProperties and %Dictionary.CompiledMethods)
go to post Julius Kavay · Aug 30, 2021 Just print it, no store, no nothing... myQuine ; A test for a quine x "zl @$zn p" That's all. And you can name the routine as you like...
go to post Julius Kavay · Aug 27, 2021 You are on a right way, but obviously you use indirection on local variables in block environment. the wrong way: Method testErr() { set myvar(1)=123, ref=$na(myvar) set ref=$query(@ref) --> this gives you always: ref="" } The correct way Method testOK() [ PublicList = myvar] { new myvar set myvar(1)=123, ref=$name(myvar) set ref=$query(@ref) write ref," ",@ref ---> myvar(1)," ",123 } i.e. indirection needs variables with global scope
go to post Julius Kavay · Aug 21, 2021 First, resetting a password means, the user gets a new password, in your use case, this is not an option. Second, if you want (for whatever reason) to validate the user in some stage of the application, then you must calculate the PBKDF2 from useres input (the password) and from (the stored) salt. The hash you get should be equal to the hash, storted in the database. PBKDF2 is a one way salted password hash. By the way, you have to care about not to transfer the users (clear text) input to your computation over an unsecure way!
go to post Julius Kavay · Aug 20, 2021 The sense of PBKDF2 encryption is, to prevent a decryption or, at least, to make a decryption as difficult as possible! Why do you want to decrypt an encrypted password? Why just not reset it?
go to post Julius Kavay · Aug 19, 2021 You have to use the login data (as specified, this is not a anonymous FTP). On Windows: command line (cmd.exe), on Linux: terminal. Then (on both systems): ftp ftp.kavay.at, enter username and passwprd, then: get DC.Entity.xml. Do you really want to see some 1470 lines of code here?
go to post Julius Kavay · Aug 18, 2021 I think, the $zconvert() function will cover only the necessary entities. But you can use a simple method to convert characters to currently known(*) entities. ClassMethod ToHTML(str) { for i=$length(str):-1:1 set c=$ascii(str,i) set:$data(^entityChars(0,c),c) $extract(str,i)=c quit str } ClassMethod FromHTML(str) { set i=0 while $locate(str,"&[A-Za-z]+;",i,j,v) { set:$data(^entityChars(1,v),c) s=$length(v), $extract(str,j-s,j-1)=$c(c), j=j-s+1 set i=j } quit str } I have a table (the ^entityChars() global) which contains more the 1400 entities. You can download the above class, together with the table from my FTP server (File: DC.Entity.xml): Adr: ftp.kavay.at Usr: dcmember Psw: member-of-DC A sample output: USER>write ##class(DC.Entity).ToHTML("Flávio Lúcio Naves Júnior") Flávio Lúcio Naves Júnior USER>write ##class(DC.Entity).FromHTML("Flávio Lúcio Naves Júnior") Flávio Lúcio Naves Júnior (*) Currently known, because (1) I do not have all the currently known entities in my table and (2) with each new day, the W3C and the Unicode consortium can extend the current entity list.
go to post Julius Kavay · Aug 18, 2021 www.intersystems.com --> Support&Learning --> WRC Direct --> Actions --> Online Distributions
go to post Julius Kavay · Aug 10, 2021 With the global name lengths, there are two pitfalls, - first, names could have arbitrary lengths, but only the first 31 characters are considered, but this was already mentioned (Roger Merchberger) - second, a global name can contain a period ("."), but the first (after the caret) or the last character must not be a period, where at "last" means, the period may not appear at the position 31. The following short test shows this. set glb="^abcdefghijklmnopqrstuvwxyz1234ABCDE.FG", @glb="myTest" for i=37:-1:30 set $extract(glb,i-1,i)="."_$e(glb,i-1) try { write i,?4,glb," " write:$d(@glb)!1 $zr," ",$get(@glb),! } catch e { write e.Name,! } the output is: 37 ^abcdefghijklmnopqrstuvwxyz1234ABCD.EFG ^abcdefghijklmnopqrstuvwxyz1234A myTest 36 ^abcdefghijklmnopqrstuvwxyz1234ABC.DEFG ^abcdefghijklmnopqrstuvwxyz1234A myTest 35 ^abcdefghijklmnopqrstuvwxyz1234AB.CDEFG ^abcdefghijklmnopqrstuvwxyz1234A myTest 34 ^abcdefghijklmnopqrstuvwxyz1234A.BCDEFG ^abcdefghijklmnopqrstuvwxyz1234A myTest 33 ^abcdefghijklmnopqrstuvwxyz1234.ABCDEFG <SYNTAX> 32 ^abcdefghijklmnopqrstuvwxyz123.4ABCDEFG ^abcdefghijklmnopqrstuvwxyz123.4 31 ^abcdefghijklmnopqrstuvwxyz12.34ABCDEFG ^abcdefghijklmnopqrstuvwxyz12.34 30 ^abcdefghijklmnopqrstuvwxyz1.234ABCDEFG ^abcdefghijklmnopqrstuvwxyz1.234 i = 34..37: always the same global (^abcdefghijklmnopqrstuvwxyz1234A) hence, the same content i = 33: the last character is a period, hence a syntax error i = 30..32: different globals, and the global name length is always 31 characters long. By the way, if you start this example with set glb = $name(^abcdefghijklmnopqrstuvwxyz1234ABCDE.FG) which is the preferred method over set glb="^abcdefghijklmnopqrstuvwxyz1234ABCDE.FG" the you end up with a cropped value in glb ^abcdefghijklmnopqrstuvwxyz1234A
go to post Julius Kavay · Aug 8, 2021 See this post https://community.intersystems.com/post/temporary-online-documentation-s...
go to post Julius Kavay · Aug 8, 2021 This is not so simple because parts of a namespace could be mapped in more then one database. But take a look at the %SYS.Namespace class: do ##class(%SYS.Namespace).GetAllNSInfo("Namespace", .info) write ##class(%SYS.Namespace).GetGlobalDest(,"aGlobalname") // gives you the global database for a specific global write ##class(%SYS.Namespace).GetRoutineDest(,"aRoutinename") // gives you the global database specific routine
go to post Julius Kavay · Aug 7, 2021 sorry, a doubleclick by mistake... May be the underlying software could prevent such stupid things in the future...
go to post Julius Kavay · Aug 7, 2021 If you want to remove the N-th field, in the your example, "Field8" is the 6-th field, then: set fields = "Field^Field1^Field3~Field4^Field5^Field6~Field7^Field8^Field8" set N=6 set $piece(fields, "^", N, N+1) = $piece(fields, "^", N+1) If you have to work with subfields, you have to get first those subfields in a temporary variable, for example, in the above example, you want to remove "Field3" then do this: set temp=$piece(fields, "^", 3) // get the subfields set N=1 set $piece(temp, "~", N,N+1) = $piece(temp, "~", N+1) set $piece(fields, "^", 3) = temp // put the subfields back into the outer string
go to post Julius Kavay · Aug 7, 2021 If you want to remove the N-th field, in the your example, "Field8" is the 6-th field, then: set fields = "Field^Field1^Field3~Field4^Field5^Field6~Field7^Field8^Field8" set N=6 set $piece(fields, "^", N, N+1) = $piece(fields, "^", N+1) If you have to work with subfields, you have to get first those subfields in a temporary variable, for example, in the above example, you want to remove "Field3" then do this: set temp=$piece(fields, "^", 3) // get the subfields set N=1 set $piece(temp, "~", N,N+1) = $piece(temp, "~", N+1) set $piece(fields, "^", 3) = temp // put the subfields back into the outer string
go to post Julius Kavay · Aug 5, 2021 First of all, if you want to transform a hex string into base64 (or whatever), then first you have to say, WHAT IS that hex string? According to your example set hexString = "4C5803040101020179C3913EC3BA7C4C580708010101021824584D4C" the string has 56 hex chars, so if you decode those 56 hex chars, the resulting string could be:a) 28 eigth bit characters orb) 14 sixteen bit chars or evenc) 7 characters, each 32 bits wide. For cases b) and c) you also have to define the endianness (big- or little-endian). Second, I assume, your hex-string represents 8-bit chars, so we get 28 characters after converting the hex-chars into a string. Converting to base64 means, you get for every 3 (8bit) chars four printable (8bit) chars. We add two padding chars to the 28 byte string, so we have 30 chars, now which are divisible by 3. This gives you 40 base64 encoded characters. But your Base64 encoding has 44 characters, which must be wrong. Here is a simple and working solution: Class DC.Util Extends %RegisteredObject { /// Hex to Base64 ClassMethod HexToB64(hex) { if $length(hex)#2 zt "ELEN" // trap, two hex chars should make up each byte set str="" for i=1:2:$length(hex) set str=str_$char($zhex($extract(hex,i,i+1))) quit $system.Encryption.Base64Encode(str,1) } /// Base64 to Hex ClassMethod B64ToHex(b64) { set str=$system.Encryption.Base64Decode(b64), hex="" for i=1:1:$length(str) set hex=hex_$extract($zhex($ascii(str,i)+256),2,3) quit hex } } and a short test set hexString = "4C5803040101020179C3913EC3BA7C4C580708010101021824584D4C" set b64String=##class(DC.Util).HexToB64(hexString) write b64String, !, ##class(DC.Util).B64ToHex(b64String), !,hexString TFgDBAEBAgF5w5E+w7p8TFgHCAEBAQIYJFhNTA== 4C5803040101020179C3913EC3BA7C4C580708010101021824584D4C 4C5803040101020179C3913EC3BA7C4C580708010101021824584D4C
go to post Julius Kavay · Aug 5, 2021 For such a task, the Horner's method was introduced. Fast and simple. ClassMethod BinToDec(bin) { if $translate(bin,10)="" { // formal check, bin should only contain '1' and '0' set res=0 for i=1:1:$length(bin) set res=res*2+$extract(bin,i) quit res } else { ztrap "NBIN" } } Hardcore ObjectScript programer place those few commands into one line bin2dec(bin) { s res=0 f i=1:1:$l(bin) { s res=res*2+$e(bin,i) } q res } and doesn't care about errors ;-))