Jump to content

Norm

uniGUI Subscriber
  • Posts

    124
  • Joined

  • Last visited

  • Days Won

    7

Everything posted by Norm

  1. You should still be getting a response of some kind from the browser. Can you share that? Do you mind sharing the url so we can try to help with the diagnoses.
  2. What you should do is the app in Chrome and check for errors in the Console tab of DevTools (F12). That should tell you what's breaking down.
  3. Davie, just so you understand, Irigsoft’s suggestion is a good solution but it answers a different question, which is: How to detect that the browser is alive but idle because the user is not doing anything. Depending on what your app is for this might be an issue you’ll need to address at some point.
  4. The SessionTimeout is an integer property in the ServerModule that you can set to any value. Here is a simple solution I use: First of all I set a SessionTime value at a low value (e.g. 2 minutes) and I set the uniTimer value at in the Main form, at say 30 seconds. I also create a boolean variable called "ImStillAlive" in the MainModule and set it to True at the start of the session. When the UniMainModuleSessionTimeout event fires in the MainModule I check whether the flag "ImStillAlive" flag is true or false. If the flag is true I extend the SessionTimeout by another 2 minutes and set the ImStillAlive flag to false. If the flag has not been reset to true next time the UniMainModuleSessionTimeout event fires I do nothing and let the session die. Meanwhile the Main form has the responsibility to always reset the ImStillAlive flag to True whenever the uniTimer fires (every 30 secs). If the browser dies the uniTimer will no longer fire and the session will die next time the UniMainModuleSessionTimeout fires. Simple but reliable. The UniMainModuleSessionTimeout procedure looks like this: procedure TdmDB.UniGUIMainModuleSessionTimeout(ASession: TObject; var ExtendTimeOut: Integer); begin if bImStillAlive then begin ExtendTimeOut := iMySessionTimeOut; (e.g. 2minutes) bImStillAlive := false; end else //Here we let the session die. So do all the cleanup necessary CleaUpThings; //Call a procedure to do some cleanup end; Something important you should know: Whenever the uniTimer fires it restarts the SessionTimeOut count in the ServerModule. So if your uniTimer is permanently enabled and it has an interval value that is lower then the SessionTimeout value then the session will never expire as long as the browser is active. The reason I use the above strategy is because whenever the uniTimer fires I momentarily stop the timer while I do whatever function I created the timer for and then I restart it on completion.
  5. Davie, If the browser is not functioning the session will be automatically terminated after a short interval and the mainform will be killed. So there is nothing you need to do to make sure the session is killed if the browser if not working or the laptop has gone to sleep.
  6. Hi Eric, Putting the Safari issue aside, I looked at your project and tried to understand what you are trying to achieve.
  7. Hi John I might be interested in the project. Please supply you email address and I'll get in touch to introduce myself and ask you some questions regarding aspects of the project.
  8. I provided an alternative javascript based FileUpload function that you can modify to achieve what you are after. E.g. you can monitor the rate of upload or set a timeout for the upload and abort the upload on timeout. Have a look at the thread and let me know if you need further help. I suggest you start by downloading and examining the last project I uploaded on the thread.
  9. It depends on the type of data you are talking about. You can either use Ajax to send the data into your form or you can use Axios fetch to store the data into a local file that you can access with Delphi.
  10. Please provide a simple example of how you loaded the data into the URLFrame and I’ll give you a solution.
  11. I used the term Global Variables because that is what Fabio used. In my case these were session related private fields residing in a Data Module and accessed as public properties (getters / setters). I found that after I moved my application to IIS the field values were not retained properly. As I said I resolved the issue by moving the fields to the MainModule.
  12. FYI I had exactly the same issue, global variables stored in a unigui data module working as expected but failing when I switched to IIS. I resolved the issue by storing the variables in the MainModule instead.
  13. Norm

    Source code

    I suggest you look at this http://forums.unigui.com/index.php?/topic/18750-does-an-extension-of-the-unigui-license-makes-sense-at-all/&do=findComment&comment=106094
  14. Your question is a very valid and logical one. The reason we are using UniGUI is because it allows us to bring over our large volume of legacy code with little change. Our area of software development is in the financials field. We have invested a lot of money and time in developing what I call a Financial Accounting Engine that we now use in all our projects. I designed the software over ten years ago and contracted some Delphi experts to help with the coding. We chose the DBISAM SQL database for our product instead of the Borland BDE because it is written in Delphi and has many hooks that allowed us to design our own middleware to address things like providing a JSON based API that allowed third-party products to interface with our financial system. When we started looking at web access we seriously looked at TMS WebCore and XDATA as a potential pathway but it had too many unknowns and the effort required for the conversion was unsustainable. I am very happy with the UniGUI solution we have implemented in spite of the shortcomings I mentioned in my previous post. To give you an idea of the kinds of projects we work on we have implemented a financial system for Wholesale Bakers that supply products to cafes and restaurants (bread, bagels, croissants etc). Before the end of each day the owners use our PWA app on their devices to place orders for the next day. At the end of the day the system generates and dispatches invoices for the ordered goods and provides the baker with reports listing both the quantities of each product and recipe measurements of ingredients required for each product category (flour, yeast, sugar etc).
  15. Hi Stemon It’s not easy to provide an example of “using UniGUI as backend" because the code is all over the place. What I can do is to provide an idea of the concept I use. This is going to be a bit of a ramble and I apologize in advance but I don’t see how I can answer your question otherwise. Who knows, this might result in an interesting forum discussion. It is obvious that any web-based application can talk to your UniGUI app, regardless of the language it is written in. Also, everybody knows that a full-stack web application consists of a front-end and a back-end. I occasionally work on small projects that are built solely around UniGUI. But when it comes to complex UI’s I prefer to use a frontend specific framework that will talk to my UniGUI backend, and VueJS has become my go-to solution. VueJS is easy to learn without being an expert in HTML & JS, e.g. you don’t have to know anything about promises, async etc to produce a respectable application and there are tons of free add-on component for anything you can think of. The reason for using a frontend framework are obvious: - - Automatic PWA - - Full responsiveness - - Fast - - Offline capability etc. The question is why use UniGUI as backend when I could be using one of the traditional solutions like NodeJS which offer better session and state management. The reason is simple, Delphi. - - I think Delphi is still one of the most powerful development environments around and I love UniGUI for keeping the language alive. - - I have tons of “behind the UI” business code in Delphi that I have been able to bring into the UniGUI backend with very little change. Now to your question about how I use UniGUI as a backend: When you compile a VueJS application it spits out 3 files, an INDEX.HTML, a JS & CSS and also an ASSETS folder that contains all required images etc. In addition VueJS will spit out all the files you need to run the application as a PWA (Service Worker, Manifest.json, Favicon and multiple home screen icons). I copy all these into the “files” folder of my UniGUI exe and I simply serve the index.html to the first GET request that hits my url. For all intends and purpose that is all that is needed for the frontend application to run in any browser. Provided the browser does not need to come back to GET or POST data you can turn the internet off and the application will continue to run happily. There is no need to look after sessions etc. That is how you can create a happy website. If the website has the function of serving database based information or images this can be retrieved from UniGUI and stored in the browser-based IndexDB before turning off the internet. For larger databases Google provides the free and powerful Firebase9 that is easy to use, but obviously require an internet connection. However, in almost all of my situations the UniGUI backend (MyApp) needs to keep in touch with the browser frontend in order to serve and receive data. I think this is the crux of your question. In this case I obviously need to create sessions, validate credentials and listen to and respond to subsequent HTTP requests. I should make in clear that, so far, my projects have been transactional in nature as far as data exchange is involved. So HTTP requests only involve GET & POST commands. There is no CRUD requirement. These are the steps I follows: - The index.html file is served at initial request - A login POST in returned from the browser, with the required credentials. - MyApp validates the request and if successful generates a sessionID and feeds it back with a “successful” response - MyApp continues to maintain the session alive and respond to GET & POST request until either a LogOff request is received or the session times out. SessionID : I generate this using a combination of the user’s IP Address and the current time-stamp. The ID must accompany all subsequent HTTP requests. Session TimeOut : I use uniThreadTimer with appropriate CriticalSection locks to monitor and terminate non-active sessions. A session times out if no GET, POST or “Ping” requests have arrived within a given period. All GETs & POSTs are handled within a CriticalSection and they shepherded to a data module that does what it needs to do and returns a json response that is fed back. This is the weekest part of my solution because all HTTP requests are queued up and handled one at a time. This would not be practical for a very busy site. I don't see any other option however and so response has been excellent. That is why a FMSoft provided session management would be so helpful. I continue to pay my subs with the hope that one day.... Anyway, here is a brief idea of what the ServerModule does. procedure TUniServerModule.UniGUIServerModuleHTTPCommand( ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo; var Handled: Boolean); var aParams : String; aStream : TStream; aPostBody : string; aHTML : String; aData : TStringList; aRespNo : Integer; aRespText : String; aLogInfo : String; aIPAddress : String; begin aHTML := fsFilesPath +'index.html'; if (ARequestInfo.Command = 'GET') and (ARequestInfo.URI = '/') then begin if FileExists(aHTML) then begin aData := TStringList.Create; try aData.LoadFromFile(aHTML); aPostBody := aData.Text; AResponseInfo.FreeContentStream := True; AResponseInfo.ContentStream := TStringStream.Create(aPostBody); AResponseInfo.ContentLength := AResponseInfo.ContentStream.Size; AResponseInfo.ResponseNo := 200; AResponseInfo.ContentType := 'text/html'; Handled := True; finally aData.Free; end; exit; end else begin AResponseInfo.ResponseNo := 500; AResponseInfo.ContentType := 'text/html'; Handled := True; end; end else if ARequestInfo.Command = 'POST' then begin aParams := UpperCase(ARequestInfo.URI); aIPAddress := ARequestInfo.RemoteIP; aLogInfo := 'POST' + ', '+ aParams; aStream := ARequestInfo.PostStream; if assigned(aStream) then begin aStream.Position := 0; aPostBody := ReadStringFromStream(aStream); aRespNo := HandleHTTPPostRequest(aPostBody, aIPAddress, aRespText); AResponseInfo.ResponseNo := aRespNo; AResponseInfo.FreeContentStream := True; AResponseInfo.ContentStream := TStringStream.Create(aPostBody); AResponseInfo.ContentLength := AResponseInfo.ContentStream.Size; AResponseInfo.ContentType := 'application/json'; Handled := True; end end end;
  16. Sorry Fred, I misunderstood your question. Of course any Cordova created app would work with UniGUI as the back-end. I've been using VueJS with UniGUI as back-end successfully for some time now to create serious PWA apps that, once launched, can easily work without internet and, if necessary, buffer the data until internet is available. VueJS has added great state management with the Pinia addition. Of course I had to build my own UniGUI session management. It's a shame that your call for a bare-bones uniGUI has not been taken seriously by FMSoft. All they need to do is give us the option to have session management that is not tied to ExtJS. I hope Farshad is listening.
  17. Cordova + uniGUI is not a possibility. The way Cordova works is that it builds a wrapper around web applications written with HTML+JS+CSS and outputs it as a native mobile app targeting iOS or Android. What is crucial is that Cordava expects the components that make up the web application to consist of a single html file and accompanying js & css files. What Unigui (ExtJS) produces is too fragmented to work with Cordova. Cordova itself, while magical in what it does, is poor at producing good UI's. So it depends on frameworks like Ionic and Onsen UI for building the user interface. An interesting development is that Ionic are working on their own Cordova equivalent called Capacitor that is looking very promising and, in my view, will replace Cordova.
  18. Here is the revised project. It's got all the bells & whistles you need. If you like it you can give me some ticks. FileUploadDemo2.zip
  19. Save any data to FileStream aFileStream := TFileStream.Create(<filename>, fmOpenReadWrite or fmOpenOrCreate or fmShareDenyWrite); try with aFileStream do Write(AnyData, Sizeof(AnyData)); finally Free; end;
  20. I just noticed a bug you need to fix. You need to free aJsonObj otherwise you'll get access violation when you close the app. aJsonObj := TJSONObject.ParseJSONValue(aJSONData) as TJSONObject; try aFileName := getJsonString(aJsonObj, 'filename'); UniMemo1.Clear; if aFileName > '' then begin aXMLData := getJsonString(aJsonObj, 'filedata'); UniMemo1.Text := aXMLData; ShowMessage('Selected file is: ' + aFileName); end else ShowMessage('No file was selected') finally aJsonObj.Free; end;
  21. That's good. But be aware that it's a quick solution that would need some tidying and thorough testing to be a robust solution.
  22. To save to file just add : with TStringList.Create do try Text := aXMLData; SaveToFile('Invoice.xml'); finally free; end; Dealing with other file types is more complicated and I'll have a look when I have time.
  23. Source attached. The project contains an alternative FileUpload function that should do what you want Have a look and let me know. FileUploadDemo.zip
  24. Hi Luciano, Try the attached project and let me know if that's what you need and I'll supply the source code. It uses port 8077. fuploadDemo.zip
×
×
  • Create New...