Jump to content
uniGUI Discussion Forums


uniGUI Subscriber
  • Content Count

  • Joined

  • Last visited

Everything posted by wprins

  1. wprins

    MVC pattern in UniGUI vs traditional coding to form

    A big reason to prefer more decoupled approaches such as MVC, MVP, MVVM, MV* is testability. If all your logic is inside event handlers then typically it's quite hard to properly test and not possible to test entirely in isolation. This may not be a show stopping problem but it is a consideration at least. (Also see for example the "Humble Dialog" pattern and "Humble View".)
  2. wprins


    For what it's worth, the above example didn't work as-is in the latest UniGUI version(s). I've updated it, added a few logging calls and instructions to make it clearer what's happening, as well as changing the flow somewhat (avoiding using a custom event in favour of just triggering the actual button click instead.) The code is available here: https://github.com/ByteJuggler/uni_keycap/tree/master
  3. wprins

    Layout: advanced tutorial

    Hi, Just to note that the "layout.rar" example from the message in this post does not run on the latest UniGUI and displays the message: "'field' config has been deprecated. Please use the 'angleField' config instead." It might be useful to update it and/or include it in the official demo if it's not already there (? I've not exhaustively looked at all the included demos, my apologies if it's actually already included!) Edit: The simple way to "fix" this is to edit ClientEvents.UniEvents and open Ext.chart.CartesianChart, then open chart.beforeInit and comment out the "config.axes" assignment in the code. (There's obviously a better way of fixing it but I didn't have the time or patience to work out what exactly has changed and how it should be updated. Changing "fields" to "angleField" or "angleFields" did not work and resulted in another error message.) Walter
  4. Hi, Today upgraded our project to (from 1.0.xxx before). Mostly everything seems to be working ok except for 2 minor things: 1) Login form icon not displaying correctly. It displays correctly ad design time. The icon is supplied via the Form's Icon property, and seems fine in the object inspector. Edit: To add, it looks like the icon is there, but is not being resized to 16x16 anymore automatically for the login form thumbnail icon. (I'll see if manually resizing it fixes the issue...) Edit2: Resizing the icon doesn't fix it, it's somehow still too large it seems, and now being "upscaled" as well. I get the impression there's some code somewhere that's set to scale the supplied icon to size, except it's apparently using the wrong size. Edit3: I've removed the icon entirely from the login form. It now displays with the default Delphi icon at designtime but is somehow now displaying the correct icon correctly at runtime?! (It may be picking it up from the Application icon, I'm guessing.) 2) I usually (but not necessarily every time) during login get the following message: Event:click, Object: O50 not found in session list. It could be timed out, refresh page and try again. It's always O50, and only seems to happen on logging in. Any suggestions on what might be causing this would be appreciated. Obviously it's possible that it's something in our project but then neither of these problems existed prior to the upgrade. Thanks Walter
  5. wprins

    Max Characters in Memo

    Thanks, apologies, I should've found that myself.
  6. wprins

    Max Characters in Memo

    Sherzod, thank you. Out of interest, can one do the same via beforeInit()? (I assume so?)
  7. wprins

    Call another web application

    I assume you meant to write it as the above (note I inserted [in]). What do you mean by "another web application" and what do you mean by "call" exactly? You can call other web services in the normal way you would from non uniGUI applications by whatever means suits your purpose. You can also "call" and frame another website inside a uniGUI frame, with some work and some co-operation from the site being called/framed. You could call a uniGUI application in various ways as well, though you'd need to arrange things to suit your purpose, which is to say you'll have to arrange for the required credentials/login to be somehow handled seamlessly/in the background. (We do something like this from an iSeries green screen system which publishes a clickable URL on the green screen and it directly "calls" our UniGUI application, taking the user to a specific page/form in the UniGUI application. If the user isn't already logged in then they're prompted to log in, after which of course the browser knows the user and further logins is seamlessly handled as required.)
  8. wprins

    Max Characters in Memo

    Today I needed this feature. It would be better if TUniMemo exposed MaxLength as a property to control this. But thanks for this solution. Edit: Also setting the maximum length doesn't actually prevent the entry of more than the maximum length. It does colour the memo frame red and display a hover warning about the maximum length however. Is it possible to actually stop entry once the memo reaches capacity?
  9. wprins

    UniGUI support, feedback in general and the future?

    For what it's worth, we generally employ the same policy -- avoid the use of closed source components whenever possible, UniGUI being essentially the only exception we've allowed in this policy for a very long time as it is such a good product. Still, I really continue to worry and dislike the level of risk we're carrying due to the size of FMSoft, e.g. if something happens to Farshad or FMSoft this will have quite serious consequences. It is entirely within the realm of possibilities to end up in a situation where FMSoft stops trading with no way to obtain the source code to UniGUI, and consequently that all the investment into a UniGUI based solution then ends up having to be abandoned and entirely rewritten due to lack of access to source and consequent inability to upgrade to newer versions of the Delphi and web ecosystems. (To add this is not just a purely hypothetical possibility either, I've actually worked with a company once where exactly this happened and a system had to be replaced at great cost and trauma to the business due to another business having gone under, so much so that they then negotiated special escrow agreements with a new provider to ensure they would have access to a product's source should that partner business cease trading etc... The whole issue is worrying enough that, as much as I like UniGUI, I do continue to consider the alternatives and will likely consider switching to eliminate this risk if something becomes available that is competitive with UniGUI.)
  10. Hi, I've today become aware of the new hyper-server functionality. Fantastic. It will solve some of the issues we've sort of just had to manage/deal with as mentioned in documentation. However, I have a question: It seems from the documentation however that Nodes (worker processes) that are managed by the hyper-server must reside on the same physical/virtual box? Is this corect? If so it's slightly unfortunate as it would seemingly be very useful if nodes could also be distributed over multiple servers rather than only being hosted on one single instance (for additional ability to scale and redundancy.) Or am I missing something or looking at this the wrong way? Thanks Walter
  11. What is the use of UniSession.SendStream()? In what context might one use it? Likewise UniSession.SendFile()?
  12. Hi, Problem: I'm trying to determine the client's apparent IP address (their public IP) so it is known inside the user's uniGUI session. Background: This is needed to enable us to generate an access token for integrating with an external party/site, where the originating browsers IP address is also used in the token signature/hash calculation/verification process. Obviously determining the user's IP is something that needs to happen inside the browser itself, in order to get the apparent public IP address of the browser running the application. Now it turns out that from a web browser/JS perspective this is quite easy. In fact there are multiple services that has API's that enable one to retrieve your apparent public IP. (See here, for example.) So the obvious idea is to somehow have the browser perform this IP capture itself just prior to, or as part of Login and then pass it and store it as part of the user's session (MainModule). Now I've had a look at the "HTTP POST callback" examples which would've provided, it would seem,a way to achieve this, if our login page was in fact a TUniURLFrame. I could then write some Javascript code to GET from the above URL (on document ready/loaded etc, say) and then update the browser document accordingly to finagle things so that on submit/POST the IP would effectively get passed together with the other form variables. However, we currently use a typical uniGUI login form with edit boxes and so on for login, not a uniURLFrame. So I'm a little at a loss as to where/how to sneak the required JS into the page to fetch the IP and ensure that it gets passed to the uniGUI server session either immediately or on login. Question: Given the above, what's the best way for me to effectively get the browser to get the user's IP address using e.g. https://api.ipify.org/?format=json, and pass this back with the other login details on login to the uniGUI server session? Alternatively do you have any better ideas or have I missed some obvious solution to this problem? (Maybe perhaps using UniSession.AddJS() which has just occurred to me while writing this...?) Thanks so much in advance for any pointers/suggestions, Walter
  13. For anyone finding this in future: The URL suggested by Gerhard (freegeoIP) is being retired as of July 2018. An alternative is www.ipify.org. Code as follows: procedure TLoginForm.UniLoginFormAjaxEvent(Sender: TComponent; EventName: string; Params: TUniStrings); begin if SameText(EventName, 'ClientIPAddr') then begin //Do what you want with the IP. As example we set the IP on a frame embedded in the login form (assumes there's a BrowserIP property.) LoginFrame.IPLabel.Caption := 'Your public IP is: '+Params.Values['ip']; LoginFrame.BrowserIP := Params.Values['ip']; end; end; procedure TLoginForm.UniLoginFormBeforeShow(Sender: TObject); begin //Reference: https://www.ipify.org/ UniSession.AddJS( '$(function() {'+ ' $.getJSON("https://api.ipify.org?format=jsonp&callback=?",'+ ' function(json) {'+ ' ajaxRequest(' + self.WebForm.JSName + ', "ClientIPAddr", ["ip="+json.ip]);'+ ' }'+ ' );'+ '});' ); end;
  14. Hi Apologies for what might be a beginner question, but is there an obvious/easy/trivial way to respond to the runtime resizing of the client side browser and consequently anchored (or aligned) DBGrid, so that the page size used by the DBGrid matches the visible size in the browser? Thanks.
  15. Gerhard, sparkz thanks. I've not been on the forums much for the last 2 or so months. I eventually actually came up with a solution by myself about 2 months ago. It's not entirely unlike what you've (Gerhard) done, yours is a bit more succinct. I was planning on posting my solution at some point but have been either away or too busy. I'll still post it sometime as I avoid using AddJS which seems somehow slightly smelly to me and so may be of interest to others...(?) Edit: Gerhard, thanks again for your example. I did just have a quick look at what I did previously and I think your solution is perhaps preferable, nice and neat and to the point. Will try it out in due course. Nevertheless, for interest and in case anyone finds it useful or educational: Basically I used UniSession.CallbackURL() and UniSession.CallbackTarget() to generate a callback URL and target from within Delphi code. These are placed inside static HTML+JS page code placed a designtime (e.g. TUniHTMLFrame or TUniURLFrame content). At the bottom of the page I wrote some Javascript to retrieve the IP, after which I trigger a normal JS Form submit which fires the retrieved IP address off into the UniGui Session via UniFormAjaxEvent(). UniHTMLFrame1.HTML content: <div style="background-color:#ccc"> <br/><br/><br/><br/><br/> <form name="input" action="{URL_CALLBACK}" method="POST" target="{CALLBACK_TARGET}"> Username: <input type="text" name="user" value="Enter Text..."><br/> IP:<input type="text" name="ip" id="ip" value=""><br/> <input type="submit" value="Submit"> </form> <br/><br/><br/><br/><br/> </div> <script> var request = new XMLHttpRequest(); request.open('GET', 'https://jsonip.com', false); //synchronous request.send(null); if(request.status === 200) { data = JSON.parse(request.response); //console.log(data); //JQuery way: $("#ip").val(data.ip); document.forms["input"]["ip"].value = data.ip; //JQuery way: $("form#input").submit(); document.forms["input"].submit(); } </script> On Form create: procedure TMainForm.ReplaceTags; var S, Sc : string; begin S := UniSyntaxEdit1.Text; Sc := UniSession.CallbackUrl('mycallback', Self, []); S := StringReplace(S, '{URL_CALLBACK}', Sc, []); Sc := UniSession.CallbackTarget; S := StringReplace(S, '{CALLBACK_TARGET}', Sc, []); UniHTMLFrame1.HTML.Text := S; end; procedure TMainForm.UniFormCreate(Sender: TObject); begin UniSyntaxEdit1.Text:=UniHTMLFrame1.HTML.Text; ReplaceTags; end; Form Ajax event handler: procedure TMainForm.UniFormAjaxEvent(Sender: TComponent; EventName: string; Params: TUniStrings); begin if EventName = 'mycallback' then begin UniEdit1.Text := Params.Values['user']; UniEdit2.Text := Params.Values['ip']; UniMemo1.Text := Params.Text; end; end; (Again, this was based on and is a modified version of "HTTP Post Callback - HTMLFrame" demo project. I also similarly modified "HTTP Post Callback - URLFrame - Auto Target" which uses TUniURLFrame component instead of TUniHTMLFrame and does not require the "CALLBACK_TARGET" substitution.) Best regards.
  16. Hello, I've been away on holiday. Unfortunately I see no one's got any suggestions for my problem? So hence I post this additional response to bump it up a bit. As a reminder, my problem/question in short: What is the best way to call/run some Javascript in the browser and have something from it posted back to the corresponding UniGUI user session, and then from the UniGUI session update or set a cookie in the browser as a result? Walter
  17. Hello! Thanks for your response, but this does not help unfortunately. The properties you gave gives the "local network" IP address of the client machine on which the browser is running, which isn't usually the same as the apparent public IP address of that same machine. For example, if I run the uniGui server on my own machine and visit the server page with a browser, the above properties predictably return If I visit my machine from another machine on the LAN, these properties give the the other machine's LAN IP address. So far so good. But of course, if my machine or the other machine, were to visit some other website out on the internet, then both my machine, and the other machine will appear to be originating from a third different IP address, e.g the public IP address of the router and/or proxy server through which the traffic is being routed from the machines out to the site being visited. Now if you think back to what I'm doing: The client browser is simultaneously showing a (local network) uniGUI server, *and* framing another site (out on the internet in this case). Therefore, to the UniGUI server the browser machine appears to have a local LAN IP, but to the site on the internet the browser looks like it has the public IP address where the traffic is coming from. Now for the framing to work, the uniGUI server has to know what the browser's apparent IP address is, and generate an access token using this information (amongst other things.) E.g. it doesn't use the the local LAN IP that is directly visible to the UniGUI server and is obviously not known by the remote site, but it has to use the IP address seen by the third party site. As I mentioned before, this can in fact be easily retrieved from the context of the client browser with a bit of Javascript, but I'm not sure how to get this back to the uniGUI server. That is, my key question is what the best way is to essentially get the browser to do a request to the internet (to establish its "public" IP) and then pass this to the user's UniGUI session, so it can generate the token to place in a TUniURLFrame. Thanks in advance! Walter
  18. wprins

    uniGUI: Verification failed

    Thanks for the feedback, it helps to at least know what the cause of the issue is. I'll avoid messing with the virtualisation settings and bear in mind that doing so may affect the uniGUI installation. (Actually the fact that it was off was as a result of a BIOS upgrade some time ago, which as is usual, reset the BIOS settings to defaults. It wasn't until today that I actually needed to use Hyper-V again and then noticed that virtualisation support was in fact off in the BIOS and turned it back on. Perhaps it's worth mentioning this somewhere in the documentation somewhere.) Thanks again and have a great day, Walter
  19. wprins

    uniGUI: Verification failed

    Hello, Today I rebooted my PC due to installing newer IBM iSeries drivers on my PC and having to do the usual reboot song-and-dance, and to enable virtualization as I sometimes use Hyper-V (nothing directly related to uniGUI.) However, I now get the following trying to start Delphi: Apparently one of these actions (installing drivers, enabling hyper-v, rebooting) has caused the above. (I've had this once before, and didn't at that time know what triggered it either. It necessitated uninstalling and reinstalling uniGUI to fix as I recall. I'm assuming it's going to be the same this time.) I just wanted to note that somewhere your validation is being overzealous, and politely voice my displeasure at having to deal with this and suggest you look into why this happened to try and prevent it from occurring in future. (This type of thing, though rare, is rather irritating when it happens and wastes time and distracts from actual work.) Best wishes, Walter
  20. wprins

    uniGUI: Verification failed

    Just to confirm, I uninstalled, reinstalled, and it's working now. Not the end of the world.
  21. Hi, I'm trying to set up a TUniURLFrame (or TUniHTMLFrame perhaps) to allow me to basically do a POST to a specific URL, with specified form parameters, the key being that both the URL and the form (Post body) parameters are to be specified by the UniGUI application state and not necessarily entered by the user. That is to say, we thereby deliver a seamless integration with an external site within a UniGUI URLFrame (and within a browser IFrame.) There are obviously several ways to approach this, from the very dynamic (creating a form element at runtime to post with) or using JQuery Ajax call (say), to the more static option of creating a form in the HTML body and then doing the required URL and form parameter modifications in (say) the JS submit() event or the JS document.ready() event. Now from a simple web page I've managed to do this already, with HTML page as follows: <!doctype html> <html> <head> <meta charset="utf-8" /> <title>Gateway page</title> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js"></script> <script type="text/javascript"> $(document).ready( function () { var reference = ""; //329039512 var token = ""; //"0025215009068098f6b58417eaa9c325aa235cda7eda09e8e89490f" $("#gateway").val(token); $("#reference").val(reference); if (token != "") { $("#gateway").hide(); $("#lblgateway").hide(); } else { $("#gateway").show(); $("#lblgateway").show(); }; if (reference != "" && token != "") { $("#sub").hide(); $("form#portal").submit(); } else { $("#sub").show(); } $("form#portal").submit( function (e) { if ($("#reference").val() == "") { alert("You must supply the reference to view."); return false; } if ($("#gateway").val() == "") { alert("You must supply the token to authorize viewing."); return false; } $("#portal").attr("action", "https://some.site.com/gateway?reference="+$("#reference").val()); } ); } ); </script> </head> <body> <div id="lblreference">Reference to view:</div> <input type="text" name="reference" id="reference" value="" /> <div> <form id="portal" method="post" name="portal"> <div id="lblgateway">Access token:</div><input type="text" name="gateway" id="gateway" value=""></input> <input type="submit" name="sub" id="sub" value="submit" /> </form> </div> </body> </html> When you view this page in a browser it will determine what values are set and then automatically submit the page (e.g. via POST), updating the reference URL parameter as relevant (the "action") and setting the token value in the form parameters, thus performing a POST to the specified URL with the required URL and form param. (If the specified/hard coded values are left blank then it will wait for you to enter them manually and wait for you to click submit before performing the same.) This is great, however I'm trying to achieve the same in a seamless manner from a UniURLFrame and having trouble doing so. The goal is for the UniGUI application to supply the reference and access token (which it will obtain/calculate) and have the UniGUI frame then comply with the remote portal's API which (as above) demands that the reference parameter is passed via URL query string and the access token be passed via form parameter using a POST operation. Things I've tried: I've tried wholesale replacing the HTML of the UniURLFrame with the values embedded, using UniURLFrame.HTML.Clear() followed by UniURLFrame.HTML.Add() calls to essentially put the above HTML with the required valued in the HMTL. This fails and somehow ends up showing our UniGUI application login form in the UniURLFrame?!? Ive also tried setting the HTML as one big string (e.g. assigning to HTML.Text). This at least displays the form in the frame, but the document.ready() code does not fire and so the form parameter (token) and reference is empty. Moreover like before clicking submit results in the UniGUI login form appearing?! I've further tried various permutations of UniURLFrame.JSInterface.JSAdd() and UniURLFrame.JSInterface.JSCall, but couldn't figure out how and when these are supposed to be used. I've also tried these in UniURLFrame "OnFrameLoaded()" event handler but this never triggered(???) so I gave up on that. Ideally I'd like to set up the "static" content in the TUniURLFrame's HTML and then simply issue a call to a predefined JS method to achieve the setting and POSTing of the reference and the token (for example, see here or here). UniURLFrame.JSInterface.JSCall would seem like this should be the way to do this but as I say I've not been able to make this work. Nor have I been able to figure out how to do the "usual" UniSessoin.AddJS() way with the UniURLFrame as target instead. It's rather unobvious how these components are to be used and more generally how to interface between UniGUI server side and the JS in the browser. Some examples and documentation would be highly helpful. (I did check the demos as well but did not spot anything close to what I'm trying to do, apologies if I've missed something relevant!) Edit: To add, simply pasting the HTML code above into the HTML text (at design time) of the UniURLFrame, and then manually typing the reference and token parameters into the page and clicking submit works entirely as expected (apart from the fact that it's then a manual process which is precisely what I'm trying to avoid.) So my question is basically, how do I hook into this page and change the reference and token value from within the UniGUI application, such that the page code runs and seamlessly fetches the requested page without requiring the user to enter anything? Edit2: I've now tried modifying the HTML to insert the reference and token via string substitution and replacing the HTML.Text. This results in the URLFrame showing the application's login form in the frame again. I get the impression this is because I'm trying to submit the form from within the document.ready() code? I'll try to avoid doing this, but if not in the document ready, how do I trigger the submit from the UniURLFrame? Help?! Walter
  22. wprins

    TUniURLFrame - how to AddJS or call a JS method

    Right, I've come up with a solution myself. Posting this for the benefit of others who may want to do something similar in future. (Basically I've taken a slightly different (and likely more straightforward) tack in dealing with the HTML in the frame and avoided depending on document.ready() as trying to submit from within the document.ready() seemed to be causing the problem even though this works directly in a browser): Anyway, the HTML.Text is now as follows: <!doctype html> <html> <head> <meta charset="utf-8" /> <title>Gateway page</title> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js"></script> </head> <body> <div id="inputs" style="display: none"> <div id="lblreference">Reference to view:</div> <input type="text" name="reference" id="reference" value="" /> <form id="portal" method="post" name="portal"> <div id="lblgateway">Access token:</div><input type="text" name="gateway" id="gateway" value=""></input> <input type="submit" name="sub" id="sub" value="submit" /> </form> </div> </body> <script type="text/javascript"> var reference = "$reference$"; var token = "$token$"; //Set the above initial values into the form/fields with JQuery: $("#gateway").val(token); $("#reference").val(reference); //Setup a submit handler to check the values and abort if needed, //or modify the URL as required and then continue submission as normal: $("form#portal").submit( function (e) { if ($("#reference").val() == "") { alert("You must supply the reference to view."); $("#inputs").show(); return false; } if ($("#gateway").val() == "") { alert("You must supply the token to authorize viewing."); $("#inputs").show(); return false; } $("#portal").attr("action", "$portalurl$?$refparamname$="+$("#reference").val()); } ); //Try automatic submission at end of document load, here, now: $("form#portal").submit(); </script> </html> Then inside the Delphi code (just after TUniFrame construction, and during its subsequent setup) there is Delphi code like the following to substitute the parameter markers (delimited by $'s above) with their required values: UniURLFrame1.HTML.Text := StringReplace(UniURLFrame1.HTML.Text, '$reference$', IntToStr(LReference), [rfReplaceAll]); UniURLFrame1.HTML.Text := StringReplace(UniURLFrame1.HTML.Text, '$token$', LToken, [rfReplaceAll]); UniURLFrame1.HTML.Text := StringReplace(UniURLFrame1.HTML.Text, '$portalurl$', LPortalURL, [rfReplaceAll]); UniURLFrame1.HTML.Text := StringReplace(UniURLFrame1.HTML.Text, '$refparamname$', LRefURLParamName, [rfReplaceAll]); This has the desired result and immediately then posts the required values to the portal site and displays the results in the frame. I don't particularly like this string substitution based approach but it does work to accomplish the desired result. Comments/Improvements obviously welcome. Thanks Walter
  23. wprins

    TUniURLFrame and X-Frame-Options on Chrome 59

    Relatedly, and perhaps an obvious question, but how do you trigger the opening of a URL on (say) button click into a new browser tab? Presumably doing this won't suffer from the above X-Frame-Options limitation, so in the meantime I'm considering doing this instead in our application. Would this be basically something like the following in the button click handler, or is there a better way? UniSession.AddJS('window.open(''https://www.google.co.uk'', ''_blank'');'); Edit: The above works as expected but is there a better way? Also for the benefit of others, one solution to this question is to install the "Ignore X-Frame headers" extension for Chrome that drops and thus effectively makes Chrome ignore the X-Frame-Options headers.
  24. Hi, I'm trying the URLFrame demo (and in our own application) where trying to fetch (for example) www.google.co.uk results in failure and the following error in the browser console: From what I've read ideally this restriction should be removed at the server end. But what if this is not an option? Is there any way to avoid this problem from the UniGUI side? Thanks in advance, Walter
  25. 1) I am trying to use icons from here: https://material.io/icons/ When clicking on the "Glyph" property in Delphi and then Clicking "Load" and selecting a PNG sourced from there, the image is loaded and displayed but when you then click OK, an error message "Invalid graphic format" is displayed with call stack as follows: http://i.imgur.com/wiOns0Z.png The same happens with a conventional VCL TSpeedButton so it looks like some kind of Delphi bug, but mentioning just in case this is something UniGUI can do something about. 2) Additionally I notice TUniSpeedButton has "Images" and "ImageIndex" properties. I'd assumed that assigning a TUniImageList, populating it with PNG (which does work) and setting ImageIndex might cause the button to display the image but this appears to do nothing. Can you clarify whether this is supposed to work or why these properties appear to be ignored? Thanks Walter