go to post Julius Kavay · Dec 9, 2020 To make things clear, the mentioned popup message should be seen by someone, who is (most of the time) working on the servers console. Is it so? Or you want to popup this message on an arbitrary desktop, where a user works? If I need to send a message to a user, either I send an email or I activate a popup message in my client (I have a very special client-UI). By the way, you wrote, I quote "an email is NOT a realistic expectation and far from reliable". Today, (almost) everybody has a smartphone and I think, if somebody does not read an email, he/she won't read thos popup messages either. Of course, you should send short plain text messages and not those fancy bloated colorful emails. So, a way to a solution... you could write a small program (C++, VB, Delphi, etc.) which is started after user login. The program should listen on a TCP port for messages from an arbitrary Cache/Iris (possibly background) application. If such a message arrives, this helper program changes to foreground, displays the message with or without an OK button. That's it.
go to post Julius Kavay · Dec 9, 2020 Store only hashed passwords... that's all. Class DC.MyUsers Extends %Persistent { Property Name As %String; Property Password As %String; Property passHash As %String [ Internal, Private, ReadOnly ]; Property passSalt As %String [ Internal, Private, ReadOnly ]; Parameter ITER = 1024; Parameter LENGTH = 20; Method PassCheck(psw) As %Boolean { set salt = $system.Encryption.Base64Decode(..passSalt) set hash = $system.Encryption.Base64Decode(..passHash) quit $system.Encryption.PBKDF2(psw, ..#ITER, salt, ..#LENGTH)=hash } Method PasswordSet(psw) As %Status { // optionally, quality/requirement-check if '..pswQuality(psw) quit $$Error^%apiOBJ(5001,"Poor password quality") set salt=$system.Encryption.GenCryptRand(8) set hash=$system.Encryption.PBKDF2(psw, ..#ITER, salt, ..#LENGTH) set i%passHash=$system.Encryption.Base64Encode(hash) set i%passSalt=$system.Encryption.Base64Encode(salt) quit $$$OK } Method pswQuality(psw) As %Boolean { quit 1 } }
go to post Julius Kavay · Dec 9, 2020 There are two points, the first (catchwords: server, interaction) was already answered by Dmitriy Maslennikov the second is your 10 second popup button. In my over 40 years of IT-experience, there is one thing (along with others) I have learnd, is: every timeout is wrong, but messages with timeouts are evil! Whatever time you use, it's either too short or too long. Imagine, the phone is ringing abd the user has a hot 20 minute discussion on the phone, in the meantime, your popups comes and goes! Unseen! Sometimes several times! The only resonable solutions are, - if the message is (just) informative and the message text never changes, then put it into a logfile and show nothing. If the message text is a variable text ("Data is saved" vs. "Can't save: No disk space available") then do the popup with one button (or textinput), see below, but DO NOT use timeouts! - if the situation allows the user to choose between multiple answers, then let the popup window with those OK, YES, NO, CANCEL, etc. buttons stay there, as long as the user chooses one of them, or as an alternative (application dependent), offer an ordinary text input and at sometime the user types the answer and pushes the enter key. Messages with (possible with short) timeouts requires a user all the time gazing on the display - which you can't expect. justmy2cents
go to post Julius Kavay · Dec 5, 2020 Just in addition to what Robert wrote, Mumps has the concept of multithreading for over 50 years! Long before C++ and Java was born. justmy2cents
go to post Julius Kavay · Dec 4, 2020 Instead of inserting debug_macros, try Intersystems TRACE utility. write $$DIR^TRACE("c:\Temp") ; to set an output directory write $$ON^TRACE(jobnr) ; the pid of the process you want to trace ; zn "appNamespace" ; do ^yourProgram ; zn "%SYS" write $$OFF^TRACE(jobnr) ; to stopp the trace do ^TRACE ; to display the trace result TRACE displays the function-/method-calls with arguments.
go to post Julius Kavay · Nov 30, 2020 I said nothing about a documentation. I don't do it even By the way, what's the definition of an empty set? Answer: 4(%@3%4@')6%.@"9@).4%23%#4)/.@/&@-5-03@02/'2!-%23@!.$@4(%)2@$/#5-%.4!4)/.
go to post Julius Kavay · Nov 30, 2020 A internet search engine can be of great help and, of course, some luck, Carefully reading the DC is also a good source of information, See this articles https://community.intersystems.com/post/yet-another-use-case-translate-d... https://community.intersystems.com/post/convert-numeric-expression202010... (last reply)
go to post Julius Kavay · Nov 24, 2020 After seeing several solutions I got the idea to make a comparison.The bottom line is, it's advisable to check how an algorithm (or function or method etc.) performs over another.So try the below program snippet... you will be surprised! Test // s date="20201121090000" s new="" s t0=$zh f i=1:1:1E6 s new=$e(date,1,4)_"-"_$e(date,5,6)_"-"_$e(date,7,8)_" "_$e(date,9,10)_":"_$e(date,11,12)_":"_$e(date,13,14) s t1=$zh f i=1:1:1E6 s new=$tr("abcd-ef-gh ij:kl:mn","abcdefghijklmn",date) s t2=$zh f i=1:1:1E6 s new=$zd($zdh($e(date,1,8),8),3)_" "_$e(date,9,10)_":"_$e(date,11,12)_":"_$e(date,13,14) s t3=$zh f i=1:1:1E6 s new=$system.SQL.TOTIMESTAMP(date, "YYYYMMDDHHMISS") s t4=$zh f i=1:1:1E6 &SQL(SELECT TO_TIMESTAMP(:date,'YYYYMMDDHHMISS') INTO :new) s t5=$zh w "$e() only",?12,t1-t0,! w "$tr()",?12,t2-t1,! w "$e()+$zd()",?12,t3-t2,! w "SQL/class",?12,t4-t3,! w "SQL/static",?12,t5-t4,! q Of course, the results will depend on hardware, on Cache/IRIS version and on utilisation of your system
go to post Julius Kavay · Nov 24, 2020 Use $tr() in backward mode set date=" 20201121090000" write $tr("abcd-ef-gh ij:kl:mn","abcdefghijklmn",date) --> 2020-11-21 09:00:00
go to post Julius Kavay · Nov 15, 2020 Just to see things clearer, you do this test directly on Cache server (i.e. local)?And you have logged in with your local (and not, for example with an domain\kevin) account? Cache runs also under the same local "kevin" account?
go to post Julius Kavay · Nov 15, 2020 Hello Kevin, in most of the cases (but not always) the reason for this is the exhaustion of TCP ports, see https://docs.microsoft.com/en-us/windows/client-management/troubleshoot-... too. Grab a windows command prompt and start with netsh int ipv4 show dynamicport tcp this shows you how many ports you have. netstat -ano | find "TCP" shows you all the TCP ports in use (including the process numbers) and netstat -an | find "TCP" | find "CLOSE" shows you all the bad guys. If this is your problem then the solution is: - increase the number of ports (if it's possible) - reduce the cases, where a new port is needed and immediate closing of unneeded ports
go to post Julius Kavay · Nov 11, 2020 If your number is an integer write $extract(1E10 + 12345, 2, 11) // if N is a fixed value, here N = 10 or write $extract("1E"_N + 12345, 2, N+1) // if N is variable The game ends if your number has more than 18 (decimal) digits!
go to post Julius Kavay · Nov 11, 2020 A quick and dirty way: set ^|"%SYS"|%SYS("SystemMode")="TEST" // or "LIVE" or "DEVELOPMENT" or "FAILOVER"
go to post Julius Kavay · Nov 9, 2020 Sorry, I don't quite get you. One used to say, if I don't understand something, then this something is either too complicated or very simple. So where belongs your case?
go to post Julius Kavay · Nov 9, 2020 I'm not sure about what you mean with "unlimited amount of indexes"... first, there is a limit of 255 for the parameters, this means you can have do ..setValue(key1,key2,...key254,value) but you can't have do ..setValue(key1,key2,...key254,key255,value) second, if you meant to have many-many values (in sense of, we have to much data) do ..setValue(key1,value1) do ..setValue(key2,value2) // etc. then, at some point you will reach the and of the available RAM (incl. swap/paging space. Usually swap/paging space is much less then the space for the real data). thirdly, if you meant variable number of indexes (in sense of variable number of parameters) then I can just refine Robert's solution to ClassMethod setValue(params...) As %Status { set base=$name(%session.Data) for i=1:1:params-1 { set base=$name(@base@(params(i))) set @base=params(params) quit 1 } In case, you have both, variable number of indices and too much data then you could use a mix of the above with data placed in a temp file. Somewhere, at the program start, let you point %sessionData to a temp file set %sessionData=$nam(^||SessionTemp) and then consider this in the setValue method ClassMethod setValue(params...) As %Status { set bas=%sessionData ... } Of course, in your application, you have to change only those %session.Data(...) to @%sessionData@(...) which uses the "variable-big-data" part of %session.Data(). All other (the standard ones) stay unchanged.
go to post Julius Kavay · Oct 21, 2020 Oh yes, DATEADD(ms, ....) works, but select 'Arrival Time' ... won't work. This gives the string constant of "Arrival Time"
go to post Julius Kavay · Oct 21, 2020 Sorry, I didn't noticed the space char... maybe I need new glasses ;-) But yes, if you have some unusual property names, then you need double-quotes ($char(34), he used $char(39))
go to post Julius Kavay · Oct 21, 2020 I'm pretty sure, you intended to write select COUNT (Arrival Time) FROM dbo.table where Arrival Time < DATEADD(ms, CONVERT(int,LEFT(1603173432000, 20)), '1970-01-01 00:00:00') i.e. do not place propertynames under quote By the way, I don't know, how many records you have in your table, but if you have thousands or millions of records, consider to compute constant things just one time! DATEADD(ms, CONVERT(int,LEFT(1603173432000, 20)), '1970-01-01 00:00:00') is a constant value, it's not neccessary to compute it for each record! Also, in this particular case, CONVERT() and LEFT() are also not needless, 1603173432000 is the value, and it is an integer.
go to post Julius Kavay · Oct 19, 2020 As Robert said, XSLT... but as he indicated too, in this case XSLT would be an overkill. But there is also a "forgotten" function, $locate() too for such cases! This function is almost never used in DC examples. Two or three lines of code, and you have the perfect solution... but for "simple" cases only. Beware of nested tags! SetFixedValue(str,tag,value) Public { set i=0, t="(?i)<"_tag_">[^<]+", s=$l(tag)+2 while $locate(str,t,i,j,v) { set $e(str,j+s-$l(v),j-1)=value, i=j+s-$l(v)+$l(value) } quit str } It works like a charm... set a="<Name>..." write $$SetFixedValue^test(a,"rollno","***") --> <Name>ABC</Name><RollNo>***</RollNo><Name>XYZ</Name><RollNo>***</RollNo><Name>xyz</Name><RollNo>***</RollNo>