-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathl7app.PRG
More file actions
1554 lines (1440 loc) · 58.4 KB
/
l7app.PRG
File metadata and controls
1554 lines (1440 loc) · 58.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
* L7App.PRG
*
#INCLUDE L7.H
#UNDEF THIS_DEBUG_OBJECTS
#DEFINE THIS_DEBUG_OBJECTS .F.
#IF .F.
***** BEGIN LICENSE BLOCK *****
Version: MPL 1.1
The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.
The Original Code is "Level 7 Framework for Web Connection" and
"Level 7 Toolkit" (collectively referred to as "L7").
The Initial Developer of the Original Code is Randy Pearson of
Cycla Corporation.
Portions created by the Initial Developer are Copyright (C) 2004 by
the Initial Developer. All Rights Reserved.
***** END LICENSE BLOCK *****
#ENDIF
*!* * You create something like this:
*!* *** ========================================================= ***
*!* DEFINE CLASS MyApp AS L7App
*!* cApplication = "My"
*!* cTitle = "My Application"
*!* cPageAlias = "MyPages"
*!* nPageCreation = L7_PAGECREATION_FACTORY + L7_PAGECREATION_DIRECT
*!* cDefaultPage = "home"
*!* ENDDEFINE && MyApp
*** ========================================================= ***
DEFINE CLASS L7App AS FORM
DataSession = 2 && private datasession
cApplication = NULL && acronym passed into INIT
cTitle = "My Level 7 Application"
* Error handling:
cErrorReturnMethod = ""
lError = .F.
lErrorMsg = .F.
oDebugInfo = NULL
cErrorMessage = ""
cErrorTitle = ""
nErrorPageInfo = L7_NONE && L7_NONE && additive (see L7.H for values)
nErrorEmailInfo = L7_ERRORINFO_TYPICAL && additive (see L7.H for values)
* Hacker handling:
lHack = .F. && flag for hacker detection
cHackInfo = "" && extra logging info for hack attempts
cLogHackClass = "L7LogHack"
HIDDEN FW_Is_Setup
FW_Is_Setup = .F.
cClassPrefix = "" && for converting URL page to class name
cClassSuffix = "" && same as above
oActivePage = NULL
cActivePageClass = "" && used by framework to decide which current class to call
cActivePage = ""
cActivePageExtension = "" && hit-specific
cPageExtension = "" && determined by access method from Config setting
cVirtualPath = "" && -- ditto --
* This property is used if you want a "class factory" approach for your pages:
cPageAlias = NULL
* - defaults to cApplication + "Pages" unless specified in subclass
oPageFactory = NULL
nPageCreation = L7_PAGECREATION_FACTORY + L7_PAGECREATION_DIRECT
cUserClass = null && new attempt to get this object early
oSession = NULL
cSessionClass = "L7Session" && bridge
nSessionTimeout = 3600 && 1 hour
cSessionTableName = NULL && defaults to <cApplication> + "Session"
oBrowser = NULL
cBrowserClass = "L7Browser"
cBrowserName = "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)"
oSecurity = NULL
cSecurityClass = "" && you must provide if you have one
oConfig = NULL
cConfigClass = "L7AppConfig" && bridge (see L7wwServer, for example)
oCacheManager = NULL
oParseManager = NULL
cParseManagerClass = "L7Parsers"
oResponseManager = NULL
cResponseManagerClass = "L7ResponseManager" && collection of handlers
cDefaultResponse = "StandardResponse"
cActiveResponse = NULL && NULL means use default
cErrorResponse = NULL && NULL meand use cErrorResponse class suggested by ActiveResponse class
lOutputDelivered = .F.
cOutputFile = ""
cIniFile = "" && override in subclass [DEPRECATED - USE CONFIG OBJECT]
cDefaultPage = "" && override in your app
lAppOpen = .F.
nHits = 0
nCurrentHits = 0 && since last maintenance event
tLastMaintenanceCheck = NULL
lMaintenanceFlag = .F.
cMaintenanceMessage = "Server Maintenance in Progress -- Please Come Back Later"
nMaintenanceCheckInterval = 30 && seconds (0 = check on every hit, -1 = no checks)
cHTMLPagePath = ""
* Request logging:
lLogRequests = .T.
cLogRequestClass = "L7LogRequest" && Bridge Pattern
cLogRequestBaseName = NULL && see access method (can be overridden)
cLogPath = NULL && NULL means get it from Config
oRequestLog = NULL
* AUTH logging:
cAuthLogClass = "L7AuthLog" && Bridge Pattern
cAuthLogBaseName = NULL && see access method (can be overridden)
* cLogPath = NULL && same path as request log
oAuthLog = NULL
* Message queueing:
cMessageQueueClass = "L7MessageQueue"
lMailServerAvailable = .T.
cMessagingPath = NULL && (see Access method)
nDatabases = 0 && Must specify in your application.
* DIMENSION aDatabases[1]
* DEPRECATED (in favor of multiple L7Database classes):
nDatabaseRevision = 0 && update in application
cDataBase = ""
cDataPath = ""
cBackupDataPath = "" && See Access method.
ADD OBJECT PrivateVars AS Collection
* --------------------------------------------------------- *
FUNCTION cLogRequestBaseName_ACCESS
IF ISNULL(THIS.cLogRequestBaseName)
THIS.cLogRequestBaseName = THIS.cApplication + "RequestLog"
ENDIF
RETURN THIS.cLogRequestBaseName
ENDFUNC
* --------------------------------------------------------- *
FUNCTION cAuthLogBaseName_ACCESS
IF ISNULL(THIS.cAuthLogBaseName)
THIS.cAuthLogBaseName = THIS.cApplication + "AuthLog"
ENDIF
RETURN THIS.cAuthLogBaseName
ENDFUNC
* --------------------------------------------------------- *
FUNCTION cVirtualPath_ACCESS
LOCAL lcStr
IF VARTYPE( THIS.oConfig) <> 'O'
lcStr = GetIniSetting( THIS.cIniFile, THIS.cApplication, "VirtualPath")
ELSE
lcStr = THIS.oConfig.cVirtualPath
ENDIF
RETURN m.lcStr
ENDFUNC && cVirtualPath_ACCESS
* --------------------------------------------------------- *
FUNCTION cPageExtension_ACCESS
LOCAL lcStr
IF VARTYPE( THIS.oConfig) <> 'O'
lcStr = GetIniSetting( THIS.cIniFile, THIS.cApplication, "PageExtension")
ELSE
lcStr = THIS.oConfig.cPageExtension
ENDIF
RETURN m.lcStr
ENDFUNC && cPageExtension_ACCESS
* --------------------------------------------------------- *
FUNCTION cDataPath_ACCESS
IF VARTYPE( THIS.oConfig) <> 'O'
RETURN GetIniSetting( THIS.cIniFile, THIS.cApplication, "DataPath")
ELSE
RETURN THIS.oConfig.cDataPath
ENDIF
ENDFUNC && cDataPath_ACCESS
* --------------------------------------------------------- *
FUNCTION cBackupDataPath_ACCESS
IF EMPTY( THIS.cBackupDataPath)
LOCAL lcPath
IF VARTYPE( THIS.oConfig) <> 'O'
lcPath = GetIniSetting( THIS.cIniFile, THIS.cApplication, ;
"BackupDataPath")
ELSE
IF VARTYPE( THIS.oConfig.cBackupDataPath) = "C"
lcPath = THIS.oConfig.cBackupDataPath
ENDIF
ENDIF
IF EMPTY( m.lcPath)
lcPath = ADDBS( THIS.cDataPath) + "backup\"
ENDIF
THIS.cBackupDataPath = m.lcPath
ENDIF
RETURN THIS.cBackupDataPath
ENDFUNC && cBackupDataPath_ACCESS
* --------------------------------------------------------- *
FUNCTION cLogPath_ACCESS
IF ISNULL( THIS.cLogPath)
LOCAL lcPath
IF VARTYPE( THIS.oConfig) <> 'O'
lcPath = GetIniSetting( THIS.cIniFile, THIS.cApplication, ;
"LogPath")
ELSE
IF VARTYPE( THIS.oConfig.cLogPath) = "C"
lcPath = THIS.oConfig.cLogPath
ENDIF
ENDIF
IF EMPTY( m.lcPath)
lcPath = ADDBS( THIS.cDataPath) + "logs\"
ENDIF
IF NOT DIRECTORY(m.lcPath)
MKDIR (m.lcPath) && hope this succeeds
ENDIF
THIS.cLogPath = m.lcPath
ENDIF
RETURN THIS.cLogPath
ENDFUNC && cLogPath_ACCESS
* --------------------------------------------------------- *
FUNCTION cMessagingPath_ACCESS
IF ISNULL(THIS.cMessagingPath)
LOCAL lcPath
IF VARTYPE(THIS.oConfig) <> 'O'
lcPath = GetIniSetting( THIS.cIniFile, THIS.cApplication, ;
"MessagingPath")
ELSE && config object exists
IF VARTYPE(THIS.oConfig.cMessagingPath) = "C"
lcPath = THIS.oConfig.cMessagingPath
ENDIF
ENDIF
IF EMPTY( m.lcPath) && no path in config
lcPath = ADDBS( THIS.cDataPath) + "messaging\"
ENDIF
IF NOT DIRECTORY(m.lcPath)
MKDIR (m.lcPath) && hope this succeeds
ENDIF
THIS.cMessagingPath = m.lcPath
ENDIF
RETURN THIS.cMessagingPath
ENDFUNC && cMessagingPath_ACCESS
* --------------------------------------------------------- *
FUNCTION cHTMLPagePath_ACCESS
IF VARTYPE( THIS.oConfig) <> 'O'
RETURN GetIniSetting( THIS.cIniFile, THIS.cApplication, "HtmlPagePath")
ELSE
RETURN THIS.oConfig.cHTMLPagePath
ENDIF
ENDFUNC && cHTMPPagePath_ACCESS
* --------------------------------------------------------- *
FUNCTION cErrorTitle_ACCESS
IF EMPTY( THIS.cErrorTitle)
RETURN "Application Error"
ELSE
RETURN THIS.cErrorTitle
ENDIF
ENDFUNC && cHTMPPagePath_ACCESS
* --------------------------------------------------------- *
FUNCTION INIT(lcApp)
* NOTE: cApplication _should_ be defined as a property in your subclass!
DO StandardVfpSettings
IF ISNULL(THIS.cApplication)
THIS.cApplication = EVL(m.lcApp, "XXXX")
ENDIF
* Add all other application procedures:
THIS.SetAppProcedures()
* NOTE: Most other "init-like" stuff appears in Setup() method below.
#IF THIS_DEBUG_OBJECTS
DEBUGOUT THIS.Name + [ (] + THIS.Class + [) initialized.]
#ENDIF
ENDFUNC && INIT
* --------------------------------------------------------- *
FUNCTION BeforeSetup && (blocking) pre-processing hook
ENDFUNC
* --------------------------------------------------------- *
FUNCTION AfterSetup && post-processing hook
ENDFUNC
* --------------------------------------------------------- *
FUNCTION Setup && called by AppManager.DoSetup
IF THIS.IsDisabled()
RETURN
ENDIF
PRIVATE goL7App
goL7App = THIS && available to everything called in SetupObjects()
IF THIS.BeforeSetup() && blocking hook
*** ISAPI behavior object:
*** THIS.oConnector = CREATEOBJECT(THIS.cConnectorClass)
THIS.SetupSessionObject()
THIS.SetupBrowserObject() && added 2/23/07
THIS.SetupPageFactory()
THIS.SetupSecurity()
THIS.SetupParseManager()
* Load response handlers:
THIS.SetupResponseManager()
THIS.AfterSetup() && hook (for additional objects)
ENDIF
THIS.FW_Is_Setup = .T.
ENDFUNC
* --------------------------------------------------------- *
FUNCTION GarbageCollect
THIS.oActivePage = NULL
THIS.TearDownSecurity()
THIS.TearDownResponseManager()
THIS.oDebugInfo = NULL
THIS.oSession = NULL
THIS.oBrowser = NULL
THIS.oPageFactory = NULL
THIS.oParseManager = NULL
THIS.oRequestLog = NULL
THIS.oAuthLog = NULL
THIS.oConfig = NULL
** THIS.oConnector = NULL
* If a subclass includes any object pointers, do this:
* FUNCTION GarbageCollect
* DODEFAULT()
* THIS.oPointer1 = NULL
* THIS.oPointer2 = NULL
* etc.
* ENDFUNC
ENDFUNC && GarbageCollect
* ------------------------------------------------------------------- *
PROCEDURE RELEASE
THIS.GarbageCollect()
RELEASE THIS
ENDFUNC && RELEASE
* --------------------------------------------------------- *
FUNCTION DESTROY
THIS.OnLastHit()
THIS.GarbageCollect()
#IF THIS_DEBUG_OBJECTS
DEBUGOUT THIS.Name + [ (] + THIS.Class + [) destroyed.]
#ENDIF
ENDFUNC && Destroy
* --------------------------------------------------------- *
FUNCTION SetupDebugInfo
IF ISNULL(THIS.oDebugInfo)
THIS.oDebugInfo = CREATEOBJECT("L7DebugInfoElement")
ENDIF
ENDFUNC
* --------------------------------------------------------- *
FUNCTION AddDebugObject(loObj, llToPage, llToEmail, lcName)
THIS.SetupDebugInfo()
THIS.oDebugInfo.AddDebugObject(m.loObj, m.llToPage, m.llToEmail, m.lcName)
ENDFUNC
* --------------------------------------------------------- *
FUNCTION AddStandardDebugInfo(lnPageInfo, lnEmailInfo)
THIS.SetupDebugInfo()
THIS.oDebugInfo.AddStandardInfo(m.lnPageInfo, m.lnEmailInfo)
ENDFUNC
* --------------------------------------------------------- *
FUNCTION SetError(lcMessage, lcTitle, loException)
IF VARTYPE(m.loException) = "O" AND loException.ErrorNo = L7_CUSTOMERROR_ERRORMSG
THIS.lErrorMsg = .T.
ELSE
THIS.lError = .T.
ENDIF
THIS.cErrorMessage = EVL(m.lcMessage, "An error occurred.")
THIS.cErrorTitle = EVL(m.lcTitle, THIS.cApplication + " Application Error")
* If a real error, populate debugInfo object:
IF THIS.lError
LOCAL lnEmailInfo, lnPageInfo, llPage, llEmail
lnPageInfo = THIS.nErrorPageInfo
lnEmailInfo = THIS.nErrorEmailInfo
IF VARTYPE(THIS.oActivePage) = "O"
lnPageInfo = BITOR(m.lnPageInfo, THIS.oActivePage.nErrorPageInfo)
lnEmailInfo = BITOR(m.lnEmailInfo, THIS.oActivePage.nErrorEmailInfo)
ENDIF
* Error info:
IF VARTYPE(m.loException) = "O"
llPage = BITAND(m.lnPageInfo, L7_ERRORINFO_EXCEPTION_INFO) > 0
llEmail = BITAND(m.lnEmailInfo, L7_ERRORINFO_EXCEPTION_INFO) > 0
IF m.llEmail OR m.llPage
THIS.AddDebugObject(m.loException, m.llPage, m.llEmail, "Error Info")
ENDIF
ENDIF
* Remainder of standard L7 error info:
** THIS.AddDebugObject(THIS, m.llPage, m.llEmail, "App Info")
THIS.AddStandardDebugInfo(m.lnPageInfo, m.lnEmailInfo)
ENDIF
RETURN
ENDFUNC && SetError
* --------------------------------------------------------- *
#IF DEBUGMODE = .F.
FUNCTION Error( lnError, lcMethod, lnLine )
* Following debugging approach courtesy of Nancy Folsom.
* See: http://www.hentzenwerke.com/catalogpricelists/debugvfp.htm
SET ASSERTS ON
ASSERT .F. MESSAGE MESSAGE()
* The following is useful because in debugger you can
* set either RETURN or RETRY as the next statement to
* execute. So, you can temporarily correct the code error
* and continue execution.
IF .F.
RETURN
RETRY
ENDIF
* End: Nancy Folsom debugging approach.
* Set flag so we know error occurred:
THIS.lError = .T.
local lcMsg
lcMsg = MESSAGE()
THIS.cErrorMessage = THIS.cErrorMessage + ;
[Error: "] + EncodeHTML(m.lcMsg) + [" in line ] + TRANSFORM(m.lnLine) + ;
[ of *] + m.lcMethod + [*] + L7BR
* Try to build an exception object to describe the condition:
LOCAL loException AS Exception
TRY
loException = L7ErrorToException(m.lnError, m.lcMethod, m.lnLine)
CATCH TO loExc
loException = m.loExc
loException.Comment = "Exception occurred trying to convert application error to exception object."
ENDTRY
* Store error info to object properties and build debugging response info:
TRY
THIS.SetError( ;
THIS.cErrorMessage, ;
THIS.cApplication + " Error: " + m.lcMsg, ;
m.loException)
* THIS.cApplication + " Application Error"
CATCH
= .F.
ENDTRY
* Abort any uncommitted changes, if possible:
TRY
RevertTables()
CATCH
= .F.
ENDTRY
LOCAL lcReturnTo
lcReturnTo = THIS.cErrorReturnMethod
IF NOT EMPTY(m.lcReturnTo)
RETURN TO &lcReturnTo
ENDIF
ENDFUNC && Error
#ENDIF
* --------------------------------------------------------- *
FUNCTION ClearErrors
* NOTE -- This is called in a few places:
* a) from here at the beginning of ProcessPage()
* b) from the AppManager, after up-front errors have been reported
THIS.lError = .F.
THIS.lErrorMsg = .F.
THIS.cErrorMessage = ""
THIS.cErrorTitle = ""
THIS.oDebugInfo = NULL
ENDFUNC && ClearErrors
* --------------------------------------------------------- *
FUNCTION SetAppProcedures
* override in your subclass with lots of:
* SET PROCEDURE TO ... ADDITIVE
ENDFUNC && SetAppProcedures
* --------------------------------------------------------- *
FUNCTION SetConfigObject(loConfigObject)
THIS.oConfig = m.loConfigObject
* Note: We don't do anything further here, because the
* actual config *file* may not have been read yet.
ENDFUNC
* --------------------------------------------------------- *
FUNCTION IsDisabled
RETURN THIS.oConfig.lDisabled
ENDFUNC
* --------------------------------------------------------- *
FUNCTION SetupSessionObject
THIS.oSession = CREATEOBJECT(THIS.cSessionClass)
THIS.oSession.SetTableName(NVL(THIS.cSessionTableName, THIS.cApplication + "Session"))
THIS.oSession.SetTimeout(THIS.nSessionTimeout)
RETURN
ENDFUNC
* --------------------------------------------------------- *
FUNCTION GetSessionObject
THIS.oSession.Reset()
RETURN THIS.oSession
ENDFUNC
* --------------------------------------------------------- *
FUNCTION SetupBrowserObject
THIS.oBrowser = CREATEOBJECT(THIS.cBrowserClass)
THIS.oBrowser.SetBrowser( THIS.cBrowserName)
RETURN
ENDFUNC
* --------------------------------------------------------- *
FUNCTION GetBrowserObject
** THIS.oBrowser.ResetProperties() called eleswhere
RETURN THIS.oBrowser
ENDFUNC
* --------------------------------------------------------- *
FUNCTION SetupPageFactory
* Create Page class factory, if used:
IF BITAND(THIS.nPageCreation, L7_PAGECREATION_FACTORY) > 0
* Factory supported.
IF ISNULL(THIS.cPageAlias) OR EMPTY(THIS.cPageAlias)
THIS.cPageAlias = THIS.cApplication + "Pages"
ENDIF
THIS.oPageFactory = CREATEOBJECT("L7PageFactory") && see L7Factory.PRG
THIS.oPageFactory.SetAlias(THIS.cPageAlias)
THIS.oPageFactory.LOAD()
ENDIF
ENDFUNC
* --------------------------------------------------------- *
FUNCTION SetupParseManager
IF NOT EMPTY(THIS.cParseManagerClass)
THIS.oParseManager = CREATEOBJECT(THIS.cParseManagerClass)
THIS.oParseManager.Config(THIS.oConfig)
ENDIF
ENDFUNC
* --------------------------------------------------------- *
FUNCTION SetupSecurity
* You can provide a compliant bridge class, or override
* this factory method altogether.
IF NOT EMPTY(THIS.cSecurityClass)
THIS.oSecurity = CREATEOBJECT(THIS.cSecurityClass, THIS.oConfig)
ENDIF
RETURN
ENDFUNC
* --------------------------------------------------------- *
FUNCTION TearDownSecurity
IF VARTYPE(THIS.oSecurity) = "O"
IF PEMSTATUS(THIS.oSecurity, "GarbageCollect", 5)
* It's "OK" if developer doesn't have such a method.
THIS.oSecurity.GarbageCollect()
ENDIF
THIS.oSecurity = NULL
ENDIF
RETURN
ENDFUNC
* --------------------------------------------------------- *
FUNCTION SetupResponseManager
THIS.oResponseManager = CREATEOBJECT(THIS.cResponseManagerClass)
THIS.oResponseManager.AddItem("L7StandardResponse", "StandardResponse")
THIS.oResponseManager.AddItem("L7ErrorResponse", "ErrorResponse")
THIS.oResponseManager.AddItem("L7TemplateResponse", "TemplateResponse")
THIS.oResponseManager.AddItem("L7FileResponse", "FileResponse")
THIS.oResponseManager.AddItem("L7JsonResponse", "JsonResponse")
RETURN
ENDFUNC
* --------------------------------------------------------- *
FUNCTION TearDownResponseManager
IF VARTYPE(THIS.oResponseManager) = "O"
THIS.oResponseManager.Remove(-1)
THIS.oResponseManager = NULL
ENDIF
RETURN
ENDFUNC
* --------------------------------------------------------- *
FUNCTION IsMyRequest
* Does this App claim this hit? In multi-app situations
* be sure to set Config VirtualPath or PageExtension settings!
* Template method for extension/path pieces.
LOCAL llResult
llResult = THIS.IsMyRequest_Extension()
llResult = m.llResult and THIS.IsMyRequest_Path()
RETURN m.llResult
ENDFUNC && IsMyRequest
* --------------------------------------------------------- *
FUNCTION IsMyRequest_Extension
LOCAL lcPhysicalPath, lcExtension, llScriptMap, llResult
llResult = .T.
IF m.llResult AND NOT EMPTY(THIS.cPageExtension) AND THIS.cPageExtension <> "*"
lcPhysicalPath = Request.GetPhysicalPath()
lcExtension = UPPER( JUSTEXT( m.lcPhysicalPath))
llScriptMap = NOT INLIST( m.lcExtension, "DLL", "EXE")
IF NOT m.llScriptMap && old "wc.dll?app~class" format
lcExtension = UPPER( Request.QueryString(1))
ENDIF
llResult = m.lcExtension == UPPER( THIS.cPageExtension)
ENDIF
RETURN m.llResult
ENDFUNC && IsMyRequest_Extension
* --------------------------------------------------------- *
FUNCTION IsMyRequest_Path
LOCAL lcLogicalPath, lcExtension, llResult, lcOKPaths, lcOKPath, lnPath
llResult = .T.
lcOKPaths = THIS.cVirtualPath
IF m.llResult AND NOT EMPTY(m.lcOKPaths) AND m.lcOKPaths <> "*"
lcLogicalPath = UPPER(Request.GetLogicalPath())
llResult = .F.
lcLogicalPath = STRTRAN("/" + m.lcLogicalPath + "/", "//", "/")
FOR lnPath = 1 TO GETWORDCOUNT(m.lcOKPaths, [,])
lcOKPath = ALLTRIM(UPPER(GETWORDNUM(m.lcOKPaths, m.lnPath, [,])))
IF NOT EMPTY(m.lcOkPath)
lcOKPath = STRTRAN("/" + m.lcOKPath + "/", "//", "/")
IF m.lcOKPath $ m.lcLogicalPath && approximate, need some further pattern work here
llResult = .T.
EXIT
ENDIF
ENDIF
ENDFOR
ENDIF
RETURN m.llResult
ENDFUNC && IsMyRequest_Path
* --------------------------------------------------------- *
PROTECTED FUNCTION OnHit( llServerCheck )
** NOTE: Above parameter never used--what was our idea?
LOCAL llRetVal
llRetVal = .T.
* Parameter is a flag to allow server to check periodically even with no hit incoming.
IF THIS.lMaintenanceFlag = .T.
* App currently in maintenance mode.
IF THIS.MaintenanceInProgress()
* Check right away, so we can service hits ASAP following maintenance release.
RETURN .F.
ELSE
THIS.OnMaintenanceRelease()
THIS.lMaintenanceFlag = .F. && should OnMaintenanceRelease clear this?
ENDIF
ELSE
* App wasn't in maintenance mode when last we checked.
IF THIS.nMaintenanceCheckInterval = 0 OR ;
( THIS.nMaintenanceCheckInterval > 0 AND ;
( ISNULL( THIS.tLastMaintenanceCheck ) OR ;
DATETIME() - THIS.tLastMaintenanceCheck >= THIS.nMaintenanceCheckInterval ))
* Time for a check.
THIS.tLastMaintenanceCheck = DATETIME()
IF THIS.MaintenanceInProgress()
THIS.OnMaintenance()
THIS.lMaintenanceFlag = .T. && should OnMaintenance set this?
RETURN .F.
ENDIF
ENDIF
ENDIF
IF THIS.nHits = 0
llRetVal = THIS.OnFirstHit()
THIS.lAppOpen = .T.
ENDIF
IF m.llRetVal AND NOT THIS.lError
THIS.nHits = THIS.nHits + 1
THIS.nCurrentHits = THIS.nCurrentHits + 1
llRetVal = THIS.AfterOnHit() && Application Hook!
ENDIF
RETURN m.llRetVal AND NOT THIS.lError
ENDFUNC && OnHit
* --------------------------------------------------------- *
FUNCTION AfterOnHit
* Application Hook
ENDFUNC && AfterOnHit
* --------------------------------------------------------- *
FUNCTION OnFirstHit
RETURN THIS.OnFirstPostMaintenanceHit() AND NOT THIS.lError
ENDFUNC && OnFirstHit
* --------------------------------------------------------- *
FUNCTION OnFirstPostMaintenanceHit
* You can override this if you want.
* Good place to open database for performance reasons.
RETURN .T.
ENDFUNC && OnFirstPostMaintenanceHit
* --------------------------------------------------------- *
FUNCTION OnMaintenanceRelease
ENDFUNC && OnMaintenanceRelease
* --------------------------------------------------------- *
FUNCTION OnMaintenance
CLOSE DATABASE ALL && or something like that
THIS.nCurrentHits = 0
ENDFUNC && OnMaintenance
* --------------------------------------------------------- *
FUNCTION GetMaintenanceMessage
RETURN THIS.cMaintenanceMessage
ENDFUNC && OnMaintenance
* --------------------------------------------------------- *
FUNCTION MaintenanceInProgress
* Check both server-wide and app-specific flags.
RETURN FILE( ".\MAINTAIN.TXT" ) OR ;
FILE( ".\MAINTAIN_" + THIS.cApplication + ".TXT" )
ENDFUNC && MaintenanceInProgress
* --------------------------------------------------------- *
FUNCTION SetMaintenanceFlag
* Set app-specific flag.
STRTOFILE( "maintenance flag", ;
".\MAINTAIN_" + THIS.cApplication + ".TXT", 0 )
THIS.OnMaintenance()
THIS.lMaintenanceFlag = .T. && force flag
ENDFUNC && MaintenanceInProgress
* --------------------------------------------------------- *
FUNCTION ClearMaintenanceFlag
* Clear app-specific flag.
ERASE (".\MAINTAIN_" + THIS.cApplication + ".TXT")
ENDFUNC && MaintenanceInProgress
* --------------------------------------------------------- *
FUNCTION OnLastHit
IF THIS.lAppOpen = .T.
THIS.OnMaintenance()
ENDIF
THIS.lAppOpen = .F.
ENDFUNC && OnLastHit
* --------------------------------------------------------- *
FUNCTION GetDefaultPage
* Class method can be overridden, if necessary.
RETURN THIS.cDefaultPage
ENDFUNC && GetDefaultPage
* --------------------------------------------------------- *
FUNCTION BeforeExecute
* Hook called from ProcessPage() template method.
ENDFUNC && BeforeExecute
* --------------------------------------------------------- *
FUNCTION AfterExecute
* Hook called from ProcessPage() template method.
ENDFUNC && AfterExecute
* --------------------------------------------------------- *
FUNCTION AdjustActivePage
* Hook to alter Page based on Request params.
* EXPERIMENTAL CODE -- REFACTORING ALMOST ASSURED <S>!!
* TO DO: Need to give App a way to make this conditional.
LOCAL lnOcc
lnOcc = OCCURS("_", THIS.cActivePage)
IF m.lnOcc >= 2
Request.FilenameToQueryString("_")
THIS.cActivePage = LEFT(THIS.cActivePage, ;
AT("_", THIS.cActivePage, 1 + MOD(m.lnOcc, 2)) - 1)
ENDIF
RETURN
ENDFUNC
* --------------------------------------------------------- *
FUNCTION ResetProperties
* Called by AppManager just before ProcessPage.
THIS.ClearErrors() && from previous hits
THIS.oActivePage = NULL
** THIS.lHack = .F. && clear flag for next hit
** THIS.cHackInfo = ""
THIS.SetHack(.F.) && clear flag for next hit
THIS.cActiveResponse = NULL
THIS.cErrorResponse = NULL
THIS.lOutputDelivered = .F.
THIS.cOutputFile = ""
RETURN
ENDFUNC
* --------------------------------------------------------- *
FUNCTION DetermineActivePage
LOCAL lcPhysicalPath, lcExtension, llScriptMap, lcClass
lcPhysicalPath = Request.GetPhysicalPath()
lcExtension = UPPER( JUSTEXT( m.lcPhysicalPath))
llScriptMap = NOT INLIST( m.lcExtension, "DLL", "EXE")
IF NOT m.llScriptMap
* old "wc.dll?app~class" format
lcExtension = UPPER( Request.QueryString(1))
THIS.cActivePage = Request.QueryString(2)
THIS.cActivePageExtension = m.lcExtension
ELSE
THIS.cActivePage = JUSTSTEM( m.lcPhysicalPath )
THIS.cActivePageExtension = JUSTEXT( m.lcPhysicalPath )
* Temporary approach:
THIS.AdjustActivePage()
ENDIF
RETURN
ENDFUNC
* --------------------------------------------------------- *
function determineBrowser()
THIS.cBrowserName = Request.cBrowser
return
endfunc
* --------------------------------------------------------- *
function BeforeProcessPage && blocking hook
* determineBrowser has been run, and Environ is available here, but little else
return .T. && override should likely return dodefault() unless returning .F. (blocking)
endfunc
* --------------------------------------------------------- *
function getClientCRC() && called from Page, may be overridden (ex: IA headers)
* page: THIS.oSession.cClientCRC = THIS.oApp.getClientCRC()
return Request.cClientCRC
endfunc
* --------------------------------------------------------- *
FUNCTION ProcessPage
*** called from L7AppManager.HandleRequest()
IF NOT THIS.FW_Is_Setup
ERROR "Application " + TRANSFORM(THIS.cApplication) + " was not setup!"
ENDIF
* Get browser object available early, in case app wants to react to crawlers, for example.
*!* if !this.determineBrowser()
*!* return .t.
*!* endif
private Browser
Browser = THIS.oBrowser
Browser.ResetProperties()
this.determineBrowser()
*!* THIS.cBrowserName = Request.cBrowser
Browser.SetBrowser( THIS.cBrowserName )
*!* * 06/22/2010: experimental, add CurrentUser now so it stays available for CheckLog processing.
*!* private CurrentUser, TrueUser
*!* if not isnull(this.cUserClass)
*!* CurrentUser = createobject(this.cUserClass)
*!* TrueUser = CurrentUser
*!* else
*!* store null to CurrentUser, TrueUser
*!* endif
* 07/21/2010: moved down lower so structure mods can happen above w/o User table open
THIS.cErrorReturnMethod = "HandleRequest" && fail to AppManager for now
LOCAL llRetVal
llRetVal = .T.
IF NOT THIS.BeforeProcessPage() && alternative--must call DeliverOutput separately
this.checkLog(m.Environ) && DRY issue ... consider refactor to (another) template method
return .T. && True, because from AppManager standpoint, the page has been processed.
ENDIF
THIS.DetermineActivePage()
#IF L7_MONITOR_PROCESS
LOCAL loProcMon
loProcMon = goL7AppManager.oProcessMonitor
IF NOT m.loProcMon.BeforeAppProcessPage(THIS)
RETURN .F.
ENDIF
#ENDIF
*[[ Replace with something else! Perhaps AppManager should be involved:
*[[
*!* * Check for maintenance flag set/clear:
*!* IF THIS.cActivePage == "set_maintenance" OR THIS.cActivePage == "clear_maintenance"
*!* IF Request.IsAdministrator()
*!* IF THIS.cActivePage == "set_maintenance"
*!* THIS.SetMaintenanceFlag()
*!* ELSE
*!* THIS.ClearMaintenanceFlag()
*!* ENDIF
*!* THIS.cActivePage = ""
*!* THIS.cActivePageExtension = ""
*!* ELSE
*!* * Just let this trigger an error later (class not found), so admin
*!* * gets email (potential hack).
*!* ENDIF
*!* ENDIF
IF EMPTY( THIS.cActivePage)
* No specific page in URL, so use default page to know
* which class to call.
* (Problem here in that the QueryString does not contain this info,
* so it is missing a parameter.)
THIS.cActivePage = THIS.GetDefaultPage()
*[[[ How do we determine cActivePageExtension here??
ENDIF
THIS.BeforeExecute()
* Call OnHit() to see if it's OK to process the hit. This must be called
* before the Page object is created, so that default tables can be opened, etc.
goL7AppManager.oConnector.SetExtraStatusInfo("")
IF NOT THIS.OnHit()
* Probably a maintenance flag set.
IF NOT THIS.lError
THIS.cErrorMessage = THIS.GetMaintenanceMessage()
ENDIF
RETURN .F.
ENDIF
* 07/21/2010: moved down here so structure mods can happen above w/o User table open
private CurrentUser, TrueUser
if not isnull(this.cUserClass)
CurrentUser = createobject(this.cUserClass)
TrueUser = CurrentUser
else
store null to CurrentUser, TrueUser
endif
* Call your page class:
local loPage
loPage = THIS.GetPageObject() && factory method
local llDebugPageState, loPageState
llDebugPageState = pemstatus(_screen, "_L7DebugPageState", 5) and _screen._L7DebugPageState = .t.
* Execute to debug: addproperty(_screen, "_L7DebugPageState", .t.)
if VARTYPE( m.loPage) <> "O" && no object created
llRetVal = .F.
else
if m.llDebugPageState
loPageState = L7noteObjectState(m.loPage)
endif
THIS.cErrorReturnMethod = "ProcessPage" && fail to here
THIS.oActivePage = m.loPage
* Start the page with the application's title:
IF ISNULL(loPage.cTitle)
loPage.cTitle = THIS.cTitle
* Page objects can modify this and/or can tack on cSubTitle.
ENDIF
* Create a few handy reference vars:
PRIVATE Config, Page
Page = m.loPage
Config = THIS.oConfig
* Declare any app-specific PRIVATE vars:
* (This puts these vars in "scope" for all page processing.)
LOCAL loPrivateVar, lcVarName
FOR EACH loPrivateVar IN THIS.PrivateVars
lcVarName = m.loPrivateVar.cVarName
PRIVATE &lcVarName
IF NOT EMPTY(m.loPrivateVar.cVarExpression)
STORE EVALUATE(m.loPrivateVar.cVarExpression) TO (m.lcVarName)
ELSE
STORE .F. TO (m.lcVarName)
ENDIF
ENDFOR
RELEASE loPrivateVar, lcVarName
***********************************************************
* Main call to Page object:
IF NOT THIS.lError
loPage.ExecuteRequest()
ENDIF
* ExecuteRequest() is a template method that eventually calls the (overridable)
* DoProcessRequest() method, which in turn is responsible for
* calling the ProcessRequest() method for your individual page.
***********************************************************
* Hack alert:
IF THIS.lHack
THIS.LogHackAttempt()
THIS.SendHackAlert()
ENDIF && hack
* Error alert:
IF THIS.lError AND NOT THIS.lHack
THIS.SendErrorAlert()
ENDIF && error
IF NOT THIS.lOutputDelivered && various blocking hooks could pre-deliver, so check
THIS.DeliverOutput()
ENDIF
llRetVal = m.llRetVal AND THIS.lOutputDelivered
if m.llDebugPageState
if !L7compareObjectState(m.loPage, m.loPageState)
* look at Debug Output window
environ.debugOutput()
endif
endif
endif && loPage [not] created
* Hook:
THIS.AfterExecute()
#IF L7_MONITOR_PROCESS
m.loProcMon.AfterAppProcessPage(THIS)
#ENDIF
this.CheckLog(m.Environ) && DRY problem. If BeforeProcessPage aborts, this doesn't get called.
THIS.oActivePage = NULL
release loPage, Page, Config
release CurrentUser, TrueUser
release Browser
THIS.cErrorReturnMethod = ""
** RETURN m.llRetVal AND NOT THIS.lError
return m.llRetVal
* App Manager interprets .F. return as a failure to deliver a response. So
* even delivering an error message should result in true return value.
ENDFUNC && ProcessPage
* ---------------------------------------------------------- *
FUNCTION SetHack(llFlag, lcHackInfo, lcUserTitle, lcUserDetails)
* Support for previous interface:
IF VARTYPE(m.llFlag) = "C"
lcHackInfo = m.llFlag
llFlag = .T.
ENDIF
* Now the real method:
IF NOT m.llFlag && cleaaring
THIS.lHack = .F.
THIS.cHackInfo = ""
ELSE
THIS.lHack = .T.
LOCAL lcStr, loExc
lcStr = ""
TRY
IF VARTYPE( m.lcUserTitle) = "C"
lcStr = m.lcStr + [Message Heading: ] + m.lcUserTitle + L7BR + CR
ENDIF
IF VARTYPE( m.lcUserDetails) = "C"
lcStr = m.lcStr + [Message to User: ] + m.lcUserDetails + L7BR + CR
ENDIF
IF VARTYPE( m.lcHackInfo) = "C"
lcStr = m.lcStr + [Administrator Info: ] + m.lcHackInfo + L7BR + CR
ENDIF