diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..a2c7983b --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/3_1_0_breaking_changes.md b/3_1_0_breaking_changes.md new file mode 100644 index 00000000..45cfa31c --- /dev/null +++ b/3_1_0_breaking_changes.md @@ -0,0 +1,4 @@ +# 3.1.0 (lithium) breaking changes + +- JSON-RPC Client Classes has been changed. Now they are interfaces based. +- JSON-RPC Server: Only public instance methods can be invoked. \ No newline at end of file diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 8dada3ed..00000000 --- a/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/License.txt b/License.txt new file mode 100644 index 00000000..ad765f91 --- /dev/null +++ b/License.txt @@ -0,0 +1,53 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of this License; and + +You must cause any modified files to carry prominent notices stating that You changed the files; and + +You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + +If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. +You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/contrib/mwAnalytics.pas b/contrib/mwAnalytics.pas new file mode 100644 index 00000000..1100fc8a --- /dev/null +++ b/contrib/mwAnalytics.pas @@ -0,0 +1,116 @@ +unit mwAnalytics; + +interface + +uses + MVCFramework, MVCFramework.Logger, System.Classes; + +type + IFileAnalytics = interface + ['{C77426C4-1D34-4B3A-BD21-3C07B7E0B8BD}'] + function GetRecentlyUsedAPI() : string; + function GetConsumeAPICount(ControllerName, ActionName : string) : Integer; + end; + + TMVCAnalyticsMiddleware = class(TInterfacedObject, IMVCMiddleware, IFileAnalytics) + private + FAnalyticsFileName: string; + FAnalyticsFile: TStringList; + procedure SetAnalyticsFilename(const Value: string); + protected + procedure OnBeforeRouting(Context: TWebContext; var Handled: Boolean); + procedure OnAfterControllerAction(Context: TWebContext; const AActionNAme: string; + const Handled: Boolean); + procedure OnBeforeControllerAction(Context: TWebContext; + const AControllerQualifiedClassName: string; const AActionNAme: string; var Handled: Boolean); + public + property AnalyticsFilename: string read FAnalyticsFileName write SetAnalyticsFilename; + + //Own Function + function GetRecentlyUsedAPI : string; + function GetConsumeAPICount(ControllerName, ActionName : string) : Integer; + end; + + +implementation + +uses + System.SysUtils, System.JSON, REST.Json; + +{ TMVCAnalyticsMiddleware } + +function TMVCAnalyticsMiddleware.GetConsumeAPICount(ControllerName, + ActionName: string): Integer; +begin + Result := 5; +end; + +function TMVCAnalyticsMiddleware.GetRecentlyUsedAPI: string; +begin + Result := 'Test' +end; + +procedure TMVCAnalyticsMiddleware.OnAfterControllerAction(Context: TWebContext; + const AActionNAme: string; const Handled: Boolean); +begin + //Context.Response.Content := Context.Response.Content + '[][][]' ; +end; + +procedure TMVCAnalyticsMiddleware.OnBeforeControllerAction(Context: TWebContext; + const AControllerQualifiedClassName, AActionNAme: string; + var Handled: Boolean); +var + Data : TJSONObject; + CSVFile : TextFile; + ApplicationPath : string; +begin + {AnalyticsFilename := 'analytics.json'; + + if not Assigned(FAnalyticsFile) then + raise Exception.Create('Analytics: Cannot read or write to file.'); + + Data := TJSONObject.Create; + Data.AddPair('TimeStamp', DateTimeToStr(Now)); + Data.AddPair('IPAddress', Context.Request.ClientIp); + Data.AddPair('ControllerName', AControllerQualifiedClassName); + Data.AddPair('ActionName', AActionNAme); + FAnalyticsFile.Add(Data.ToJSON); + FAnalyticsFile.SaveToFile(FAnalyticsFileName); + Data.Free;} + //Context.Request; + ApplicationPath := ExtractFilePath(GetModuleName(hInstance)); + AssignFile(CSVFile, ApplicationPath+'analytics.csv'); + if not FileExists('analytics.csv') then + begin + Rewrite(CSVFile); + WriteLn(CSVFile, 'DateTime, IpAddress, ControllerName, ActionName, DomainName, Host'); + end; + Append(CSVFile); + WriteLn(CSVFile, DateTimeToStr(Now), ',', Context.Request.ClientIp, ',', AControllerQualifiedClassName, ',',AActionNAme, ',' , Context.Request.RawWebRequest.Referer, ',', Context.Request.RawWebRequest.Host); + CloseFile(CSVFile); +end; + +procedure TMVCAnalyticsMiddleware.OnBeforeRouting(Context: TWebContext; + var Handled: Boolean); +begin + +end; + +procedure TMVCAnalyticsMiddleware.SetAnalyticsFilename(const Value: string); +begin + // create the class if not there + if not Assigned(FAnalyticsFile) then + FAnalyticsFile := TStringList.Create; + + // Create the file if it doesn't exist + if not FileExists(Value) then + FAnalyticsFile.SaveToFile(Value) + else + FAnalyticsFile.LoadFromFile(Value); + + // assign the name + FAnalyticsFileName := Value; +end; + + +end. diff --git a/ideexpert/DMVC.Expert.CodeGen.Templates.pas b/ideexpert/DMVC.Expert.CodeGen.Templates.pas index 9ea2aede..7562afe1 100644 --- a/ideexpert/DMVC.Expert.CodeGen.Templates.pas +++ b/ideexpert/DMVC.Expert.CodeGen.Templates.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/ideexpert/DMVC_IDE_Expert_D102Tokyo.dpk b/ideexpert/DMVC_IDE_Expert_D102Tokyo.dpk index 57ac6836..c063ea55 100644 --- a/ideexpert/DMVC_IDE_Expert_D102Tokyo.dpk +++ b/ideexpert/DMVC_IDE_Expert_D102Tokyo.dpk @@ -36,7 +36,8 @@ requires IndySystem, IndyProtocols, IndyCore, - dbrtl; + dbrtl, + dmvcframeworkRT; contains DMVC.Expert.CodeGen.NewControllerUnit in 'DMVC.Expert.CodeGen.NewControllerUnit.pas', @@ -51,7 +52,6 @@ contains DMVC.Expert.NewUnitWizardEx in 'DMVC.Expert.NewUnitWizardEx.pas', DMVC.Expert.ProjectWizardEx in 'DMVC.Expert.ProjectWizardEx.pas', DMVC.Expert.Registration in 'DMVC.Expert.Registration.pas', - DMVC.Splash.Registration in 'DMVC.Splash.Registration.pas', - MVCFramework.Commons in '..\sources\MVCFramework.Commons.pas'; + DMVC.Splash.Registration in 'DMVC.Splash.Registration.pas'; end. diff --git a/ideexpert/DMVC_IDE_Expert_D102Tokyo.dproj b/ideexpert/DMVC_IDE_Expert_D102Tokyo.dproj index 3346f4d4..bc946e2d 100644 --- a/ideexpert/DMVC_IDE_Expert_D102Tokyo.dproj +++ b/ideexpert/DMVC_IDE_Expert_D102Tokyo.dproj @@ -161,6 +161,7 @@ + @@ -178,7 +179,6 @@ - ICON DMVCNewProjectIcon diff --git a/ideexpert/DMVC_IDE_Expert_D103Rio.dpk b/ideexpert/DMVC_IDE_Expert_D103Rio.dpk index b6f37fd0..90172c58 100644 --- a/ideexpert/DMVC_IDE_Expert_D103Rio.dpk +++ b/ideexpert/DMVC_IDE_Expert_D103Rio.dpk @@ -36,7 +36,8 @@ requires IndySystem, IndyProtocols, IndyCore, - dbrtl; + dbrtl, + dmvcframeworkRT; contains DMVC.Expert.CodeGen.NewControllerUnit in 'DMVC.Expert.CodeGen.NewControllerUnit.pas', @@ -51,7 +52,6 @@ contains DMVC.Expert.NewUnitWizardEx in 'DMVC.Expert.NewUnitWizardEx.pas', DMVC.Expert.ProjectWizardEx in 'DMVC.Expert.ProjectWizardEx.pas', DMVC.Expert.Registration in 'DMVC.Expert.Registration.pas', - DMVC.Splash.Registration in 'DMVC.Splash.Registration.pas', - MVCFramework.Commons in '..\sources\MVCFramework.Commons.pas'; + DMVC.Splash.Registration in 'DMVC.Splash.Registration.pas'; end. diff --git a/ideexpert/DMVC_IDE_Expert_D103Rio.dproj b/ideexpert/DMVC_IDE_Expert_D103Rio.dproj index aff47ebe..b0ff6b25 100644 --- a/ideexpert/DMVC_IDE_Expert_D103Rio.dproj +++ b/ideexpert/DMVC_IDE_Expert_D103Rio.dproj @@ -126,6 +126,7 @@ + @@ -143,7 +144,6 @@ - ICON DMVCNewProjectIcon diff --git a/lib/loggerpro/ThreadSafeQueueU.pas b/lib/loggerpro/ThreadSafeQueueU.pas index f427e981..8152bbc4 100644 --- a/lib/loggerpro/ThreadSafeQueueU.pas +++ b/lib/loggerpro/ThreadSafeQueueU.pas @@ -2,7 +2,7 @@ // // LoggerPro // -// Copyright (c) 2010-2018 Daniele Teti +// Copyright (c) 2010-2019 Daniele Teti // // https://github.com/danieleteti/loggerpro // diff --git a/packages/d102/dmvcframeworkRT.dpk b/packages/d102/dmvcframeworkRT.dpk new file mode 100644 index 00000000..96f28d04 --- /dev/null +++ b/packages/d102/dmvcframeworkRT.dpk @@ -0,0 +1,94 @@ +package dmvcframeworkRT; + +{$R *.res} +{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION OFF} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES ON} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DEFINE DEBUG} +{$ENDIF IMPLICITBUILDING} +{$DESCRIPTION 'DMVCFramework 3.x'} +{$LIBSUFFIX '102'} +{$RUNONLY} +{$IMPLICITBUILD ON} + +requires + rtl, + inet, + dclcommon, + dcldb, + FireDAC, + IndyCore, + IndyProtocols, + FireDACIBDriver, + FireDACMySQLDriver, + loggerproRT; + +contains + JsonDataObjects in '..\..\sources\JsonDataObjects.pas', + MVCFramework.ActiveRecord in '..\..\sources\MVCFramework.ActiveRecord.pas', + MVCFramework.ActiveRecordController in '..\..\sources\MVCFramework.ActiveRecordController.pas', + MVCFramework.ApplicationSession in '..\..\sources\MVCFramework.ApplicationSession.pas', + MVCFramework.Cache in '..\..\sources\MVCFramework.Cache.pas', + MVCFramework.Commons in '..\..\sources\MVCFramework.Commons.pas', + MVCFramework.Console in '..\..\sources\MVCFramework.Console.pas', + MVCFramework.DataSet.Utils in '..\..\sources\MVCFramework.DataSet.Utils.pas', + MVCFramework.DuckTyping in '..\..\sources\MVCFramework.DuckTyping.pas', + MVCFramework.FireDAC.Utils in '..\..\sources\MVCFramework.FireDAC.Utils.pas', + MVCFramework.HMAC in '..\..\sources\MVCFramework.HMAC.pas', + MVCFramework.JSONRPC.Client in '..\..\sources\MVCFramework.JSONRPC.Client.pas', + MVCFramework.JSONRPC in '..\..\sources\MVCFramework.JSONRPC.pas', + MVCFramework.JWT in '..\..\sources\MVCFramework.JWT.pas', + MVCFramework.Logger in '..\..\sources\MVCFramework.Logger.pas', + MVCFramework.Middleware.Analytics in '..\..\sources\MVCFramework.Middleware.Analytics.pas', + MVCFramework.Middleware.Authentication in '..\..\sources\MVCFramework.Middleware.Authentication.pas', + MVCFramework.Middleware.Authentication.RoleBasedAuthHandler in '..\..\sources\MVCFramework.Middleware.Authentication.RoleBasedAuthHandler.pas', + MVCFramework.Middleware.Compression in '..\..\sources\MVCFramework.Middleware.Compression.pas', + MVCFramework.Middleware.CORS in '..\..\sources\MVCFramework.Middleware.CORS.pas', + MVCFramework.Middleware.JWT in '..\..\sources\MVCFramework.Middleware.JWT.pas', + MVCFramework.Middleware.SecurityHeaders in '..\..\sources\MVCFramework.Middleware.SecurityHeaders.pas', + MVCFramework.MultiMap in '..\..\sources\MVCFramework.MultiMap.pas', + MVCFramework in '..\..\sources\MVCFramework.pas', + MVCFramework.Patches in '..\..\sources\MVCFramework.Patches.pas', + MVCFramework.RESTAdapter in '..\..\sources\MVCFramework.RESTAdapter.pas', + MVCFramework.RESTClient in '..\..\sources\MVCFramework.RESTClient.pas', + MVCFramework.Router in '..\..\sources\MVCFramework.Router.pas', + MVCFramework.RQL.AST2FirebirdSQL in '..\..\sources\MVCFramework.RQL.AST2FirebirdSQL.pas', + MVCFramework.RQL.AST2InterbaseSQL in '..\..\sources\MVCFramework.RQL.AST2InterbaseSQL.pas', + MVCFramework.RQL.AST2MySQL in '..\..\sources\MVCFramework.RQL.AST2MySQL.pas', + MVCFramework.RQL.AST2PostgreSQL in '..\..\sources\MVCFramework.RQL.AST2PostgreSQL.pas', + MVCFramework.RQL.Parser in '..\..\sources\MVCFramework.RQL.Parser.pas', + MVCFramework.Rtti.Utils in '..\..\sources\MVCFramework.Rtti.Utils.pas', + MVCFramework.Serializer.Abstract in '..\..\sources\MVCFramework.Serializer.Abstract.pas', + MVCFramework.Serializer.Commons in '..\..\sources\MVCFramework.Serializer.Commons.pas', + MVCFramework.Serializer.Defaults in '..\..\sources\MVCFramework.Serializer.Defaults.pas', + MVCFramework.Serializer.Intf in '..\..\sources\MVCFramework.Serializer.Intf.pas', + MVCFramework.Serializer.JsonDataObjects.CustomTypes in '..\..\sources\MVCFramework.Serializer.JsonDataObjects.CustomTypes.pas', + MVCFramework.Serializer.JsonDataObjects in '..\..\sources\MVCFramework.Serializer.JsonDataObjects.pas', + MVCFramework.Session in '..\..\sources\MVCFramework.Session.pas', + MVCFramework.SQLGenerators.Firebird in '..\..\sources\MVCFramework.SQLGenerators.Firebird.pas', + MVCFramework.SQLGenerators.MySQL in '..\..\sources\MVCFramework.SQLGenerators.MySQL.pas', + MVCFramework.SysControllers in '..\..\sources\MVCFramework.SysControllers.pas', + MVCFramework.SystemJSONUtils in '..\..\sources\MVCFramework.SystemJSONUtils.pas', + MVCFramework.View.Cache in '..\..\sources\MVCFramework.View.Cache.pas'; + +end. + diff --git a/packages/d102/dmvcframeworkRT.dproj b/packages/d102/dmvcframeworkRT.dproj new file mode 100644 index 00000000..2c06cabe --- /dev/null +++ b/packages/d102/dmvcframeworkRT.dproj @@ -0,0 +1,598 @@ + + + {96D17257-AF74-48CB-9893-7BCCB56A069D} + dmvcframeworkRT.dpk + 18.5 + None + True + Debug + Win32 + 1 + Package + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + .\$(Platform)\$(Config) + .\$(Platform)\$(Config) + false + false + false + false + false + true + true + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + All + dmvcframeworkRT + true + + + None + android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services-ads-7.0.0.dex.jar;google-play-services-analytics-7.0.0.dex.jar;google-play-services-base-7.0.0.dex.jar;google-play-services-gcm-7.0.0.dex.jar;google-play-services-identity-7.0.0.dex.jar;google-play-services-maps-7.0.0.dex.jar;google-play-services-panorama-7.0.0.dex.jar;google-play-services-plus-7.0.0.dex.jar;google-play-services-wallet-7.0.0.dex.jar + + + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + 1033 + + + DEBUG;$(DCC_Define) + true + false + true + true + true + + + C:\DEV\dmscontainer_centrosoftware\bin\DMSContainerService.exe + true + 1033 + 3 + DMVCFramework 3.x + 102 + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + + MainSource + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Package + + + + dmvcframeworkRT.dpk + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + Embarcadero C++Builder Office 2000 Servers Package + Embarcadero C++Builder Office XP Servers Package + + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + dmvcframeworkRT.bpl + true + + + + + 1 + + + Contents\MacOS + 0 + + + + + classes + 1 + + + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + + + library\lib\mips + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + + + res\values + 1 + + + + + res\values-v21 + 1 + + + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + + + 1 + + + 1 + + + 0 + + + + + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + + + + 1 + + + 1 + + + 1 + + + + + + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 0 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + False + False + True + False + + + 12 + + + + + diff --git a/packages/d103/dmvcframeworkRT.dpk b/packages/d103/dmvcframeworkRT.dpk new file mode 100644 index 00000000..bd82bbb4 --- /dev/null +++ b/packages/d103/dmvcframeworkRT.dpk @@ -0,0 +1,94 @@ +package dmvcframeworkRT; + +{$R *.res} +{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION OFF} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES ON} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DEFINE DEBUG} +{$ENDIF IMPLICITBUILDING} +{$DESCRIPTION 'DMVCFramework 3.x'} +{$LIBSUFFIX '103'} +{$RUNONLY} +{$IMPLICITBUILD ON} + +requires + rtl, + inet, + dclcommon, + dcldb, + FireDAC, + IndyCore, + IndyProtocols, + FireDACIBDriver, + FireDACMySQLDriver, + loggerproRT; + +contains + JsonDataObjects in '..\..\sources\JsonDataObjects.pas', + MVCFramework.ActiveRecord in '..\..\sources\MVCFramework.ActiveRecord.pas', + MVCFramework.ActiveRecordController in '..\..\sources\MVCFramework.ActiveRecordController.pas', + MVCFramework.ApplicationSession in '..\..\sources\MVCFramework.ApplicationSession.pas', + MVCFramework.Cache in '..\..\sources\MVCFramework.Cache.pas', + MVCFramework.Commons in '..\..\sources\MVCFramework.Commons.pas', + MVCFramework.Console in '..\..\sources\MVCFramework.Console.pas', + MVCFramework.DataSet.Utils in '..\..\sources\MVCFramework.DataSet.Utils.pas', + MVCFramework.DuckTyping in '..\..\sources\MVCFramework.DuckTyping.pas', + MVCFramework.FireDAC.Utils in '..\..\sources\MVCFramework.FireDAC.Utils.pas', + MVCFramework.HMAC in '..\..\sources\MVCFramework.HMAC.pas', + MVCFramework.JSONRPC.Client in '..\..\sources\MVCFramework.JSONRPC.Client.pas', + MVCFramework.JSONRPC in '..\..\sources\MVCFramework.JSONRPC.pas', + MVCFramework.JWT in '..\..\sources\MVCFramework.JWT.pas', + MVCFramework.Logger in '..\..\sources\MVCFramework.Logger.pas', + MVCFramework.Middleware.Analytics in '..\..\sources\MVCFramework.Middleware.Analytics.pas', + MVCFramework.Middleware.Authentication in '..\..\sources\MVCFramework.Middleware.Authentication.pas', + MVCFramework.Middleware.Authentication.RoleBasedAuthHandler in '..\..\sources\MVCFramework.Middleware.Authentication.RoleBasedAuthHandler.pas', + MVCFramework.Middleware.Compression in '..\..\sources\MVCFramework.Middleware.Compression.pas', + MVCFramework.Middleware.CORS in '..\..\sources\MVCFramework.Middleware.CORS.pas', + MVCFramework.Middleware.JWT in '..\..\sources\MVCFramework.Middleware.JWT.pas', + MVCFramework.Middleware.SecurityHeaders in '..\..\sources\MVCFramework.Middleware.SecurityHeaders.pas', + MVCFramework.MultiMap in '..\..\sources\MVCFramework.MultiMap.pas', + MVCFramework in '..\..\sources\MVCFramework.pas', + MVCFramework.Patches in '..\..\sources\MVCFramework.Patches.pas', + MVCFramework.RESTAdapter in '..\..\sources\MVCFramework.RESTAdapter.pas', + MVCFramework.RESTClient in '..\..\sources\MVCFramework.RESTClient.pas', + MVCFramework.Router in '..\..\sources\MVCFramework.Router.pas', + MVCFramework.RQL.AST2FirebirdSQL in '..\..\sources\MVCFramework.RQL.AST2FirebirdSQL.pas', + MVCFramework.RQL.AST2InterbaseSQL in '..\..\sources\MVCFramework.RQL.AST2InterbaseSQL.pas', + MVCFramework.RQL.AST2MySQL in '..\..\sources\MVCFramework.RQL.AST2MySQL.pas', + MVCFramework.RQL.AST2PostgreSQL in '..\..\sources\MVCFramework.RQL.AST2PostgreSQL.pas', + MVCFramework.RQL.Parser in '..\..\sources\MVCFramework.RQL.Parser.pas', + MVCFramework.Rtti.Utils in '..\..\sources\MVCFramework.Rtti.Utils.pas', + MVCFramework.Serializer.Abstract in '..\..\sources\MVCFramework.Serializer.Abstract.pas', + MVCFramework.Serializer.Commons in '..\..\sources\MVCFramework.Serializer.Commons.pas', + MVCFramework.Serializer.Defaults in '..\..\sources\MVCFramework.Serializer.Defaults.pas', + MVCFramework.Serializer.Intf in '..\..\sources\MVCFramework.Serializer.Intf.pas', + MVCFramework.Serializer.JsonDataObjects.CustomTypes in '..\..\sources\MVCFramework.Serializer.JsonDataObjects.CustomTypes.pas', + MVCFramework.Serializer.JsonDataObjects in '..\..\sources\MVCFramework.Serializer.JsonDataObjects.pas', + MVCFramework.Session in '..\..\sources\MVCFramework.Session.pas', + MVCFramework.SQLGenerators.Firebird in '..\..\sources\MVCFramework.SQLGenerators.Firebird.pas', + MVCFramework.SQLGenerators.MySQL in '..\..\sources\MVCFramework.SQLGenerators.MySQL.pas', + MVCFramework.SysControllers in '..\..\sources\MVCFramework.SysControllers.pas', + MVCFramework.SystemJSONUtils in '..\..\sources\MVCFramework.SystemJSONUtils.pas', + MVCFramework.View.Cache in '..\..\sources\MVCFramework.View.Cache.pas'; + +end. + diff --git a/packages/dmvcframework.dproj b/packages/d103/dmvcframeworkRT.dproj similarity index 82% rename from packages/dmvcframework.dproj rename to packages/d103/dmvcframeworkRT.dproj index aa0ea4ef..5c2d8ea9 100644 --- a/packages/dmvcframework.dproj +++ b/packages/d103/dmvcframeworkRT.dproj @@ -1,7 +1,7 @@  {96D17257-AF74-48CB-9893-7BCCB56A069D} - dmvcframework.dpk + dmvcframeworkRT.dpk 18.5 None True @@ -51,7 +51,8 @@ true System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) All - dmvcframework + dmvcframeworkRT + true None @@ -73,7 +74,12 @@ true - false + C:\DEV\dmscontainer_centrosoftware\bin\DMSContainerService.exe + true + 1033 + 3 + DMVCFramework 3.x + 103 false @@ -94,52 +100,53 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Cfg_2 Base @@ -158,8 +165,15 @@ - dmvcframework.dpk + dmvcframeworkRT.dpk + + TurboPack LockBox Delphi FMX designtime package + DBExpress Enterprise Data Explorer Integration + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + FastReport 6.0 Tee Components + @@ -187,9 +201,9 @@ true - + - dmvcframework.bpl + dmvcframeworkRT.bpl true diff --git a/packages/dmvcframework.dpk b/packages/dmvcframework.dpk deleted file mode 100644 index 6b1ebe2a..00000000 --- a/packages/dmvcframework.dpk +++ /dev/null @@ -1,89 +0,0 @@ -package dmvcframework; - -{$R *.res} -{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} -{$ALIGN 8} -{$ASSERTIONS ON} -{$BOOLEVAL OFF} -{$DEBUGINFO OFF} -{$EXTENDEDSYNTAX ON} -{$IMPORTEDDATA ON} -{$IOCHECKS ON} -{$LOCALSYMBOLS ON} -{$LONGSTRINGS ON} -{$OPENSTRINGS ON} -{$OPTIMIZATION OFF} -{$OVERFLOWCHECKS OFF} -{$RANGECHECKS OFF} -{$REFERENCEINFO ON} -{$SAFEDIVIDE OFF} -{$STACKFRAMES ON} -{$TYPEDADDRESS OFF} -{$VARSTRINGCHECKS ON} -{$WRITEABLECONST OFF} -{$MINENUMSIZE 1} -{$IMAGEBASE $400000} -{$DEFINE DEBUG} -{$ENDIF IMPLICITBUILDING} -{$IMPLICITBUILD ON} - -requires - rtl, - inet, - dclcommon, - dcldb, - FireDAC, - IndyCore, - IndyProtocols, - FireDACIBDriver, - FireDACMySQLDriver, - loggerpro; - -contains - JsonDataObjects in '..\sources\JsonDataObjects.pas', - MVCFramework.ActiveRecord in '..\sources\MVCFramework.ActiveRecord.pas', - MVCFramework.ActiveRecordController in '..\sources\MVCFramework.ActiveRecordController.pas', - MVCFramework.ApplicationSession in '..\sources\MVCFramework.ApplicationSession.pas', - MVCFramework.Cache in '..\sources\MVCFramework.Cache.pas', - MVCFramework.Commons in '..\sources\MVCFramework.Commons.pas', - MVCFramework.Console in '..\sources\MVCFramework.Console.pas', - MVCFramework.DataSet.Utils in '..\sources\MVCFramework.DataSet.Utils.pas', - MVCFramework.DuckTyping in '..\sources\MVCFramework.DuckTyping.pas', - MVCFramework.FireDAC.Utils in '..\sources\MVCFramework.FireDAC.Utils.pas', - MVCFramework.HMAC in '..\sources\MVCFramework.HMAC.pas', - MVCFramework.JSONRPC.Client in '..\sources\MVCFramework.JSONRPC.Client.pas', - MVCFramework.JSONRPC in '..\sources\MVCFramework.JSONRPC.pas', - MVCFramework.JWT in '..\sources\MVCFramework.JWT.pas', - MVCFramework.Logger in '..\sources\MVCFramework.Logger.pas', - MVCFramework.Middleware.Analytics in '..\sources\MVCFramework.Middleware.Analytics.pas', - MVCFramework.Middleware.Authentication in '..\sources\MVCFramework.Middleware.Authentication.pas', - MVCFramework.Middleware.Authentication.RoleBasedAuthHandler in '..\sources\MVCFramework.Middleware.Authentication.RoleBasedAuthHandler.pas', - MVCFramework.Middleware.Compression in '..\sources\MVCFramework.Middleware.Compression.pas', - MVCFramework.Middleware.CORS in '..\sources\MVCFramework.Middleware.CORS.pas', - MVCFramework.Middleware.JWT in '..\sources\MVCFramework.Middleware.JWT.pas', - MVCFramework.Middleware.SecurityHeaders in '..\sources\MVCFramework.Middleware.SecurityHeaders.pas', - MVCFramework.MultiMap in '..\sources\MVCFramework.MultiMap.pas', - MVCFramework in '..\sources\MVCFramework.pas', - MVCFramework.Patches in '..\sources\MVCFramework.Patches.pas', - MVCFramework.RESTAdapter in '..\sources\MVCFramework.RESTAdapter.pas', - MVCFramework.RESTClient in '..\sources\MVCFramework.RESTClient.pas', - MVCFramework.Router in '..\sources\MVCFramework.Router.pas', - MVCFramework.RQL.AST2FirebirdSQL in '..\sources\MVCFramework.RQL.AST2FirebirdSQL.pas', - MVCFramework.RQL.AST2InterbaseSQL in '..\sources\MVCFramework.RQL.AST2InterbaseSQL.pas', - MVCFramework.RQL.AST2MySQL in '..\sources\MVCFramework.RQL.AST2MySQL.pas', - MVCFramework.RQL.Parser in '..\sources\MVCFramework.RQL.Parser.pas', - MVCFramework.Rtti.Utils in '..\sources\MVCFramework.Rtti.Utils.pas', - MVCFramework.Serializer.Abstract in '..\sources\MVCFramework.Serializer.Abstract.pas', - MVCFramework.Serializer.Commons in '..\sources\MVCFramework.Serializer.Commons.pas', - MVCFramework.Serializer.Defaults in '..\sources\MVCFramework.Serializer.Defaults.pas', - MVCFramework.Serializer.Intf in '..\sources\MVCFramework.Serializer.Intf.pas', - MVCFramework.Serializer.JsonDataObjects.CustomTypes in '..\sources\MVCFramework.Serializer.JsonDataObjects.CustomTypes.pas', - MVCFramework.Serializer.JsonDataObjects in '..\sources\MVCFramework.Serializer.JsonDataObjects.pas', - MVCFramework.Session in '..\sources\MVCFramework.Session.pas', - MVCFramework.SQLGenerators.Firebird in '..\sources\MVCFramework.SQLGenerators.Firebird.pas', - MVCFramework.SQLGenerators.MySQL in '..\sources\MVCFramework.SQLGenerators.MySQL.pas', - MVCFramework.SysControllers in '..\sources\MVCFramework.SysControllers.pas', - MVCFramework.SystemJSONUtils in '..\sources\MVCFramework.SystemJSONUtils.pas', - MVCFramework.View.Cache in '..\sources\MVCFramework.View.Cache.pas'; - -end. diff --git a/releases/3_1_0_lithium/3_0_0_breaking_changes.md b/releases/3_1_0_lithium/3_0_0_breaking_changes.md new file mode 100644 index 00000000..bc51e706 --- /dev/null +++ b/releases/3_1_0_lithium/3_0_0_breaking_changes.md @@ -0,0 +1,59 @@ +# 3.0.0 breaking changes +- XE6 and previous versions are not more supported. +- ```RenderListAsProperty``` has been removed from TMVCController. You can set such kind of specialized serializations in your custom base controller. +- ```RenderJSONArrayAsProperty``` has been removed from TMVCController. You can set such kind of specialized serializations in your custom base controller. +- ```Render``` has been removed from TMVCController (was deprecated). +- ```Render(TJSONValue)``` has been removed from TMVCController (use Render(TObject)). +- Trying to deserialize a ```TJSONNull``` the target instance will not be freed anymore (consistency with serialize). +- ```Context.Request.BodyAsJSONObject``` dosen't exist any more. Use BodyAs or the following pattern to migrate: +``` +JSON := TJSONObject.ParseJSONValue(Context.Request.Body) as TJSONObject; +try + if not Assigned(JSON) then + raise EMVCException.Create('Invalid JSON'); + // do something here +finally + JSON.Free; +end; +``` +- ```TMVCConfigKey``` moved to unit ```MVCFramework.Commons```. +- ```TMVCMimeType``` was renamed to ```TMVCMediaType```. +- ```TMVCController.Render;``` no parameter method do not exist anymore. If the return is a ResponseStream, use the ```RenderResponseStream;```. +- ```TMVCController.PushJSONToView;``` was renamed to ```PushToView```and Removed SystemJSON dependency, use the ToJSON method if necessary. +- There is no more a default view engine for Server Side Views (before 3.0 there was mustache). +- Mustache engine is no more the only view engine available. Now you can implement all the view engines you need (check the serversideviewsprimer). +- On Linux there is no built-in available view engine available. In other words, using only the built-in classes, you cannot use server side views on linux (dmustache is not compatible on linux). +- HTTP File Upload doesn't work on Linux because of a bug in Delphi 10.2 (https://quality.embarcadero.com/browse/RSP-17216). +- ```[MapperJSONNaming(TJSONNameCase.JSONNameLowerCase)]``` now must be changed in ```[MVCNameCase(ncLowerCase)]``` +- ```[MapperJSONNaming(TJSONNameCase.JSONNameUpperCase)]``` now must be changed in ```[MVCNameCase(ncUpperCase)]``` + +## TRESTClient +Every reference to TJSON* has been removed from the TRESTClient public interface. To port the existing code, you have to include ```MVCFramework.RESTClient.SystemJSONUtils``` and change your code as following: + +Before + ```lMyJSONObject := Response.BodyAsJsonObject.Clone as TJSONObject;``` +After + ```delphi + lMyJSONObject := TSystemJSON.BodyAsJsonObject(Response) as TJSONObject; + try + //use the object + finally + lMyJSONObject.Free; + end; + ``` + +The memory allocated for the TJSONValue descendant (e.g. TJSONObject, TJSONArray and so on) *is no more managed by the TRESTClient itself* but must be feed by the calling code. + +- DelphiStompClient has been removed from the core. The following method is no more available in TMVCController. + ```delphi + function GetNewStompClient(const AClientId: string = ''): IStompClient; + ``` + +- ```TMVCConfigKey.ISAPIPath``` has been substituted with ```TMVCConfigKey.PathPrefix``` and now is applicated to all kind of projects (not only ISAPI); +- ```MapperSerializeAsString``` attribute is now ```MVCSerializeAsString``` +- ```ContentCharset``` is no more available (everywhere). You have to properly set ContentType. To do that is available the function ```CreateContentType```. +- Removed ```LogEx``` and ```LogException```. Use ```Log.ErrorFmt``` instead. +- `PushObjectToView` has been deprecated. Use `ViewData` property; +- `PushDataSetToView` has been deprecated. Use `ViewDataSet` property; +- `ViewModels` has been renamed in `ViewModelList`; +- `ViewDataSets` has been renamed in `ViewDatasetList`; \ No newline at end of file diff --git a/releases/3_1_0_lithium/3_1_0_breaking_changes.md b/releases/3_1_0_lithium/3_1_0_breaking_changes.md new file mode 100644 index 00000000..45cfa31c --- /dev/null +++ b/releases/3_1_0_lithium/3_1_0_breaking_changes.md @@ -0,0 +1,4 @@ +# 3.1.0 (lithium) breaking changes + +- JSON-RPC Client Classes has been changed. Now they are interfaces based. +- JSON-RPC Server: Only public instance methods can be invoked. \ No newline at end of file diff --git a/releases/3_1_0_lithium/License.txt b/releases/3_1_0_lithium/License.txt new file mode 100644 index 00000000..ad765f91 --- /dev/null +++ b/releases/3_1_0_lithium/License.txt @@ -0,0 +1,53 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of this License; and + +You must cause any modified files to carry prominent notices stating that You changed the files; and + +You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + +If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. +You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/releases/3_1_0_lithium/README.md b/releases/3_1_0_lithium/README.md new file mode 100644 index 00000000..0fa624ac --- /dev/null +++ b/releases/3_1_0_lithium/README.md @@ -0,0 +1,379 @@ +![DelphiMVCFramework Logo](docs/dmvcframework_logofacebook.png) + +# DelphiMVCFramework 3.0.0-hydrogen is [here](https://github.com/danieleteti/delphimvcframework/releases/tag/v3.0.0-hydrogen)! + +## DelphiMVCFramework Main Features + + * Simple to use, check the ["Getting Started: 5 minutes guide"](https://danieleteti.gitbooks.io/delphimvcframework/content/chapter_getting_started.html) and you will be up and running in 5 minutes or less! + * [Project Roadmap](roadmap.md) is always public + * More than 40 samples to learn all the features and be proficient and productive + * RESTful (RMM Level 3) compliant + * JSON-RPC 2.0 Support + * Stable and solid, used by small/mid/big projects since 2010 + * Fast, fast, fast! + * Support group at https://www.facebook.com/groups/delphimvcframework with more than 1400 active members + * Can be used in load balanced environment + * Wizard for the Delphi IDE. It makes DelphiMVCFramework even more simple to use! + * Optional session support + * JSON Web Token Support (JWT) + * Extendable using middlewares (simple hooks to handle request/response) + * Flexible yet simple to use, authorization/authentication framework based on industry standards. + * HTTP Basic Authentication + * JWT Authentication + * Custom Authentication + * CORS support + * Controllers inheritance! You can define your own base controller and inherith from it. + * Fancy URL with parameter mappings + * Specialied renders to generate text, HTML, JSON. + * Powerful and customizable mapper to serialize/deserialize data. + * Can be packaged as stand alone server, apache module (XE6 or better) and ISAPI dll + * Integrated RESTClient + * Works with XE7, XE8, Delphi 10 Seattle, Delphi 10.1 Berlin, Delphi 10.2 Tokyo + * Works on Linux (Delphi 10.2 Tokyo or better) + * Completely unit tested + * There is a sample for each functionality + * There is a complete set of trainings about it, but the samples are included in the project + * Server side generated pages using Mustache (https://mustache.github.io/) for Delphi (https://github.com/synopse/dmustache) + * Specific trainings are available (email to professionals@bittime.it for a date and a place) + * Messaging extension using ServerSentEvents + * Automatic documentation through /system/describeserver.info + * Driven by its huge community (Facebook group https://www.facebook.com/groups/delphimvcframework) + * Semantic Versioning + * Simple and [documented](docs/ITDevCON%202013%20-%20Introduction%20to%20DelphiMVCFramework.pdf) + * Check the [DMVCFramework Developer Guide](https://danieleteti.gitbooks.io/delphimvcframework/content/) (work in progress) + + +## What users says about DMVCFramework + +>"DMVCFramework is a great framework. It's very intuitive, fast, easy to use, actually there is nothing more to ask for." -- Samir + +>"Wow! To do that in J2EE it takes 2 days" -- a training participant after a 5 minutes demo. + +>"I'm starting with the DMVCFramework and I'm finding it fantastic, congratulations for the project!" -- Rafael + +>"I'm looking at DMVCFramework project in it works great - for my use case scenarios is much better than *'Similar commercial product'*." -- Luka + +>"It's fantastic! Just define your entities and you are up and running in 5 minutes. Nothing comparable on the market." -- Marco + +## What's New + +### DelphiMVCFramework 3.1.0 lithium (currently in beta) +- New! Added `TMVCActiveRecord` framework (check sample `activerecord_showcase` and `activerecord_crud`) +- New! Added `TMVCActiveRecordController` (check sample `activerecord_crud`) +- Automatic permissions handling for `TMVCActiveRecordController` (check sample `activerecord_crud`) +- EntityProcessor for `TMVCActiveRecordController` (check sample `activerecord_crud`) +- `Config[TMVCConfigKey.FallbackResource]` is served only if request path is empty or `/`. +- New! Now the JSON-RPC executor provides methods to handle HTTP headers for JSON-RPC requests and notifications. +- `TDataSetHolder` is a new render that is able to render a dataset with a set of custom metadata (eg `count`,`page` etc). Check [issue #137](https://github.com/danieleteti/delphimvcframework/issues/137) +- `404` and `500` status code returns always a `text/plain` content-type +- Refactored ISAPI sample +- Speed improvement! Removed enhanced visibility for action methods. Now only public and published methods can be used as actions. +- `TMVCController.Create` is `virtual`! Now on your base controllers can be even more powerful! +- New! Added `MAX_REQUEST_SIZE` for limiting the size of the incoming HTTP requests. IDE Expert is updated too! +- New! Added method `TMVCJsonDataObjectsSerializer.ListToJsonArray` +- New! `TMVCResponse` for handle generic (non error) response +- New! `TMVCErrorResponse` for handle generic error response +- New! Added class `TMVCActiveRecordList` used in the manual `TMVCActiveRecord` programming +- New! Added `gzip` compression support in addition to `deflate` in `TCompressionMiddleware` +- FIX for [issue #143](https://github.com/danieleteti/delphimvcframework/issues/143) +- FIX for [issue #141](https://github.com/danieleteti/delphimvcframework/issues/141) +- Removed deprecated methods in `IRESTResponse` +- FIX misspelled header name in `IRESTResponse` +- New! Added `gzip` and `deflate` support in `TRestClient` when reading responses +- `TCompressionMiddleware` has been renamed in `TMVCCompressionMiddleware` +- New! `TMVCCompressionMiddleware` is added by IDE Expert by default +- Removed the old JSON serializer based on `System.JSON.pas', now the only available JSON serializer is based on [JsonDataObjects](https://github.com/ahausladen/JsonDataObjects) parser (Thank you Andreas Hausladen). +- Changed! Custom Types Serializer *must* be registered by media-type only, without charset definition (e.g. just `application/json` and not `application/json;charset=utf-8`) +- Changed! `IMVCTypeSerializer` is more powerful and simple to use! +- Sending wrongly formatted JSON now returns a more correctly `400 Bad Request` and not `500 Internal Server Error` as in the previous versions +- New! Support for Spring4d nullable types (check `samples\renders_spring4d_nullables`) +- New! `TMVCJSONRPCPublisher` allows to easily expose plain Delphi objects (and even datamodules) through a JSON-RPC 2.0 interface! +- *Breaking Change!* The JSON RPC Client layer is now interface based. + + +## How to correctly get the source +It is not needed to download the git repository. Just download the [latest version as zip file](https://github.com/danieleteti/delphimvcframework/releases/tag/v3.0.0-hydrogen) and you are ok. + +## Roadmap +DelphiMVCFramework roadmap is always updated as-soon-as the features planned are implemented. Check the roadmap [here](roadmap.md). + +## Trainings, consultancy or custom development service +As you know, good support on open source software is a must for professional users. +If you need trainings, consultancy or custom developments on DelphiMVCFramework, send an email to *dmvcframework at bittime dot it*. Alternatively you can send a request using the [contacts forms](http://www.bittimeprofessionals.it/contatti) on [bittimeprofessionals website](http://www.bittimeprofessionals.it). bit Time Professionals is the company behind DelphiMVCFramework, al the main developers works there. + + +### Using mapping framework in Delphi Starter Edition +A lot of users ask about it, now is possible to use the mapper framework also in Delphi Started Edition. To enable the "StarterEditionMode" open ```sources\dmvcframework.inc``` and remove the dot (.) after the curly brace in the following line + +```{.$DEFINE STARTEREDITION}``` + +become + +```{$DEFINE STARTEREDITION}``` + +## Release Notes +**3.0.0 (hydrogen)** + - First release of the 3.0.0 version + +**2.1.3 (lithium)** + - FIX https://github.com/danieleteti/delphimvcframework/issues/64 + - Added unit tests to avoid regressions + +**2.1.2 (helium)** + - FIX for Delphi versions who don't have ```TJSONBool``` (Delphi XE8 or older) + - Added new conditional define in dmvcframework.inc: JSONBOOL (defined for Delphi Seattle+) + +**2.1.1 (hydrogen)** + - Updated the IDE Expert to show the current version of the framework + - FIX to the mapper about the datasets null values (needs to be checked in old Delphi versions) + - ADDED support for boolean values in datasets serialization + - ADDED unit tests about Mapper and dataset fields nullability + - The current version is available in constant ```DMVCFRAMEWORK_VERSION``` defined in ```MVCFramework.Commons.pas``` + +## Samples and documentation +DMVCFramework is provided with a lot of examples focused on specific functionality. +All samples are in [Samples](samples) folder. +Check the [DMVCFramework Developer Guide](https://danieleteti.gitbooks.io/delphimvcframework/content/) (work in progress). + + +# Getting Started +Below the is a basic sample of a DMVCFramework server wich can be deployed as standa-alone application, as an Apache module or as ISAPI dll. This flexibility is provided by the Delphi WebBroker framework (built-in in Delphi since Delphi 4). +The project containes an IDE Expert which make creating DMVCFramework project a breeze. However not all the Delphi version are supported, so here's the manual version (which is not complicated at all). + +To create this server, you have to create a new ```Delphi Projects -> WebBroker -> WebServerApplication```. Then add the following changes to the webmodule. +```delphi +unit WebModuleUnit1; + +interface + +uses System.SysUtils, System.Classes, Web.HTTPApp, MVCFramework {this unit contains TMVCEngine class}; + +type + TWebModule1 = class(TWebModule) + procedure WebModuleCreate(Sender: TObject); + procedure WebModuleDestroy(Sender: TObject); + + private + MVC: TMVCEngine; + + public + { Public declarations } + end; + +var + WebModuleClass: TComponentClass = TWebModule1; + +implementation + +{$R *.dfm} + +uses UsersControllerU; //this is the unit where is defined the controller + +procedure TWebModule1.WebModuleCreate(Sender: TObject); +begin + MVC := TMVCEngine.Create(Self); + MVC.Config[TMVCConfigKey.DocumentRoot] := 'public_html'; //if you need some static html, javascript, etc (optional) + MVC.AddController(TUsersController); //see next section to know how to create a controller +end; + +procedure TWebModule1.WebModuleDestroy(Sender: TObject); +begin + MVC.Free; +end; + +end. +``` + +Remember that the files inside the redist folder *must* be in the executable path or in the system path. If starting the server whithin the IDE doesn't works, try to run the executable outside the IDE and check the dependencies. +That's it! You have just created your first DelphiMVCFramework. Now you have to add a controller to respond to the http request. + +## Sample Controller +Below a basic sample of a DMVCFramework controller with 2 action + +```delphi +unit UsersControllerU; + +interface + +uses + MVCFramework; + +type + [MVCPath('/users')] + TUsersController = class(TMVCController) + public + + //The following action will be with a GET request like the following + //http://myserver.com/users/3 + [MVCPath('/($id)')] + [MVCProduces('application/json')] + [MVCHTTPMethod([httpGET])] + [MVCDoc('Returns a user as a JSON object')] + procedure GetUser(id: Integer); + + + //The following action will be with a GET request like the following + //http://myserver.com/users + [MVCPath('/')] + [MVCProduces('application/json')] + [MVCHTTPMethod([httpGET])] + [MVCDoc('Returns the users list as a JSON Array of JSON Objects')] + procedure GetUsers; + + //The following action will be with a PUT request like the following + //http://myserver.com/users/3 + //and in the request body there should be a serialized TUser + [MVCPath('/($id)')] + [MVCProduce('application/json')] + [MVCHTTPMethod([httpPUT])] + [MVCDoc('Update a user')] + procedure UpdateUser(id: Integer); + + //The following action will respond to a POST request like the following + //http://myserver.com/users + //and in the request body there should be the new user to create as json + [MVCPath] + [MVCProduce('application/json')] + [MVCHTTPMethod([httpPOST])] + [MVCDoc('Create a new user, returns the id of the new user')] + procedure CreateUser; + + end; + +implementation + +uses + MyTransactionScript; //contains actual data access code + +{ TUsersController } + +procedure TUsersController.GetUsers; +var + Users: TObjectList; +begin + Users := GetUsers; + Render(Users); +end; + +procedure TUsersController.GetUser(id: Integer); +var + User: TUser; +begin + User := GetUserById(id); + Render(User); +end; + +procedure TUsersController.UpdateUser(id: Integer); +var + User: TUser; +begin + User := Context.Request.BodyAs; + UpdateUser(id, User); + Render(User); +end; + +procedure TUsersController.CreateUser; +var + User: TUser; +begin + User := Context.Request.BodyAs; + CreateUser(User); + Render(User); +end; + +end. +``` + +Now you have a performant RESTful server wich respond to the following URLs: +- GET /users/($id) (eg. /users/1, /users/45 etc) +- PUT /users/($id) (eg. /users/1, /users/45 etc with the JSON data in the request body) +- POST /users (the JSON data must be in the request body) + +### Quick Creation of DelphiMVCFramework Server + +If you dont plan to deploy your DMVCFramework server behind a webserver (apache or IIS) you can also pack more than one listener application server into one single executable. In this case, the process is a bit different and involves the creation of a listener context. However, create a new server is a simple task: + +```delphi +uses + MVCFramework.Server, + MVCFramework.Server.Impl; + +var + LServerListener: IMVCListener; +begin + LServerListener := TMVCListener.Create(TMVCListenerProperties.New + .SetName('Listener1') + .SetPort(5000) + .SetMaxConnections(1024) + .SetWebModuleClass(YourServerWebModuleClass) + ); + + LServerListener.Start; + LServerListener.Stop; +end; +``` + +If you want to add a layer of security (in its WebModule you should add the security middleware): + +```delphi +uses + MVCFramework.Server, + MVCFramework.Server.Impl, + MVCFramework.Middleware.Authentication; + +procedure TTestWebModule.WebModuleCreate(Sender: TObject); +begin + FMVCEngine := TMVCEngine.Create(Self); + + // Add Yours Controllers + FMVCEngine.AddController(TYourController); + + // Add Security Middleware + FMVCEngine.AddMiddleware(TMVCBasicAuthenticationMiddleware.Create( + TMVCDefaultAuthenticationHandler.New + .SetOnAuthentication( + procedure(const AUserName, APassword: string; + AUserRoles: TList; var IsValid: Boolean; + const ASessionData: TDictionary) + begin + IsValid := AUserName.Equals('dmvc') and APassword.Equals('123'); + end + ) + )); +end; +``` + +In stand alone mode you can work with a context that supports multiple listeners servers: + +```delphi +uses + MVCFramework.Server, + MVCFramework.Server.Impl; + +var + LServerListenerCtx: IMVCListenersContext; + +begin + LServerListenerCtx := TMVCListenersContext.Create; + + LServerListenerCtx.Add(TMVCListenerProperties.New + .SetName('Listener1') + .SetPort(6000) + .SetMaxConnections(1024) + .SetWebModuleClass(WebModuleClass1) + ); + + LServerListenerCtx.Add(TMVCListenerProperties.New + .SetName('Listener2') + .SetPort(7000) + .SetMaxConnections(1024) + .SetWebModuleClass(WebModuleClass2) + ); + + LServerListenerCtx.StartAll; +end; +``` + +### Links +Feel free to ask questions on the "Delphi MVC Framework" facebook group (https://www.facebook.com/groups/delphimvcframework). + diff --git a/releases/dmvcframework_3_0_0_hydrogen_RC12.zip b/releases/dmvcframework_3_0_0_hydrogen_RC12.zip deleted file mode 100644 index 179037a7..00000000 Binary files a/releases/dmvcframework_3_0_0_hydrogen_RC12.zip and /dev/null differ diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..efd939ce --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +colorama==0.4.1 +invoke==1.2.0 diff --git a/samples/CustomAuth/AuthHandlerU.pas b/samples/CustomAuth/AuthHandlerU.pas index bda02fe8..ac0c81f6 100644 --- a/samples/CustomAuth/AuthHandlerU.pas +++ b/samples/CustomAuth/AuthHandlerU.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/samples/CustomAuth/MainClientFormU.pas b/samples/CustomAuth/MainClientFormU.pas index 241a221e..08484ddd 100644 --- a/samples/CustomAuth/MainClientFormU.pas +++ b/samples/CustomAuth/MainClientFormU.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/samples/CustomAuth/MyWebModuleU.pas b/samples/CustomAuth/MyWebModuleU.pas index 945e9ac3..0787712d 100644 --- a/samples/CustomAuth/MyWebModuleU.pas +++ b/samples/CustomAuth/MyWebModuleU.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/samples/CustomAuth/PrivateControllerU.pas b/samples/CustomAuth/PrivateControllerU.pas index 44f7571a..cb5dbaf7 100644 --- a/samples/CustomAuth/PrivateControllerU.pas +++ b/samples/CustomAuth/PrivateControllerU.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/samples/CustomAuth/PublicControllerU.pas b/samples/CustomAuth/PublicControllerU.pas index e80a02ca..c7b03c6f 100644 --- a/samples/CustomAuth/PublicControllerU.pas +++ b/samples/CustomAuth/PublicControllerU.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/samples/activerecord_showcase/EntitiesU.pas b/samples/activerecord_showcase/EntitiesU.pas index 60b00334..ad35fcd0 100644 --- a/samples/activerecord_showcase/EntitiesU.pas +++ b/samples/activerecord_showcase/EntitiesU.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/samples/activerecord_showcase/MainFormU.dfm b/samples/activerecord_showcase/MainFormU.dfm index 04c3a0ad..735e3abd 100644 --- a/samples/activerecord_showcase/MainFormU.dfm +++ b/samples/activerecord_showcase/MainFormU.dfm @@ -12,6 +12,7 @@ object MainForm: TMainForm Font.Style = [] OldCreateOrder = False OnCreate = FormCreate + OnDestroy = FormDestroy DesignSize = ( 635 299) diff --git a/samples/activerecord_showcase/MainFormU.pas b/samples/activerecord_showcase/MainFormU.pas index 1a16ed0a..75a489ef 100644 --- a/samples/activerecord_showcase/MainFormU.pas +++ b/samples/activerecord_showcase/MainFormU.pas @@ -47,6 +47,7 @@ type procedure btnSelectClick(Sender: TObject); procedure btnValidationClick(Sender: TObject); procedure FormCreate(Sender: TObject); + procedure FormDestroy(Sender: TObject); private procedure Log(const Value: string); public @@ -60,7 +61,6 @@ implementation {$R *.dfm} - uses MVCFramework.ActiveRecord, EntitiesU, @@ -142,14 +142,11 @@ begin else raise Exception.Create('Unknown backend for direct SQL execution'); - lProc := - procedure + lProc := procedure var lConn: TFDConnection; - lCustomer: - TCustomer; - I: - Integer; + lCustomer: TCustomer; + I: Integer; begin lConn := TFDConnection.Create(nil); try @@ -174,17 +171,8 @@ begin end; end; - lTasks := [ - TTask.Run(lProc) - , TTask.Run(lProc) - , TTask.Run(lProc) - , TTask.Run(lProc) - , TTask.Run(lProc) - , TTask.Run(lProc) - , TTask.Run(lProc) - , TTask.Run(lProc) - , TTask.Run(lProc) - ]; + lTasks := [TTask.Run(lProc), TTask.Run(lProc), TTask.Run(lProc), TTask.Run(lProc), TTask.Run(lProc), TTask.Run(lProc), + TTask.Run(lProc), TTask.Run(lProc), TTask.Run(lProc)]; TTask.WaitForAll(lTasks); end; @@ -360,10 +348,12 @@ begin ActiveRecordConnectionsRegistry.AddConnection('default', FDConnection1); end; -procedure TMainForm.Log( - const - Value: - string); +procedure TMainForm.FormDestroy(Sender: TObject); +begin + ActiveRecordConnectionsRegistry.RemoveConnection('default'); +end; + +procedure TMainForm.Log(const Value: string); begin Memo1.Lines.Add(Value); Memo1.Update; diff --git a/samples/activerecord_showcase/activerecord_showcase.dpr b/samples/activerecord_showcase/activerecord_showcase.dpr index 45aa1217..e78d5d96 100644 --- a/samples/activerecord_showcase/activerecord_showcase.dpr +++ b/samples/activerecord_showcase/activerecord_showcase.dpr @@ -9,7 +9,9 @@ uses MVCFramework.RQL.AST2FirebirdSQL in '..\..\sources\MVCFramework.RQL.AST2FirebirdSQL.pas', MVCFramework.SQLGenerators.MySQL in '..\..\sources\MVCFramework.SQLGenerators.MySQL.pas', MVCFramework.SQLGenerators.Firebird in '..\..\sources\MVCFramework.SQLGenerators.Firebird.pas', - MVCFramework.RQL.AST2MySQL in '..\..\sources\MVCFramework.RQL.AST2MySQL.pas'; + MVCFramework.RQL.AST2MySQL in '..\..\sources\MVCFramework.RQL.AST2MySQL.pas', + MVCFramework.RQL.AST2InterbaseSQL in '..\..\sources\MVCFramework.RQL.AST2InterbaseSQL.pas', + MVCFramework.RQL.AST2PostgreSQL in '..\..\sources\MVCFramework.RQL.AST2PostgreSQL.pas'; {$R *.res} diff --git a/samples/activerecord_showcase/activerecord_showcase.dproj b/samples/activerecord_showcase/activerecord_showcase.dproj index 95d9cb05..05e1289d 100644 --- a/samples/activerecord_showcase/activerecord_showcase.dproj +++ b/samples/activerecord_showcase/activerecord_showcase.dproj @@ -113,6 +113,8 @@ + + Cfg_2 Base diff --git a/samples/apachemodule/images/block_nine.jpg b/samples/apachemodule/images/block_nine.jpg new file mode 100644 index 00000000..34710e11 Binary files /dev/null and b/samples/apachemodule/images/block_nine.jpg differ diff --git a/samples/apachemodule/images/bodega_lurton.jpg b/samples/apachemodule/images/bodega_lurton.jpg new file mode 100644 index 00000000..731f3ac2 Binary files /dev/null and b/samples/apachemodule/images/bodega_lurton.jpg differ diff --git a/samples/apachemodule/images/bouscat.jpg b/samples/apachemodule/images/bouscat.jpg new file mode 100644 index 00000000..c24c7ce8 Binary files /dev/null and b/samples/apachemodule/images/bouscat.jpg differ diff --git a/samples/apachemodule/images/domaine_serene.jpg b/samples/apachemodule/images/domaine_serene.jpg new file mode 100644 index 00000000..492ee947 Binary files /dev/null and b/samples/apachemodule/images/domaine_serene.jpg differ diff --git a/samples/apachemodule/images/ex_umbris.jpg b/samples/apachemodule/images/ex_umbris.jpg new file mode 100644 index 00000000..45691973 Binary files /dev/null and b/samples/apachemodule/images/ex_umbris.jpg differ diff --git a/samples/apachemodule/images/generic.jpg b/samples/apachemodule/images/generic.jpg new file mode 100644 index 00000000..33f2c223 Binary files /dev/null and b/samples/apachemodule/images/generic.jpg differ diff --git a/samples/apachemodule/images/lan_rioja.jpg b/samples/apachemodule/images/lan_rioja.jpg new file mode 100644 index 00000000..c2bce57f Binary files /dev/null and b/samples/apachemodule/images/lan_rioja.jpg differ diff --git a/samples/apachemodule/images/le_doyenne.jpg b/samples/apachemodule/images/le_doyenne.jpg new file mode 100644 index 00000000..da5b5b3a Binary files /dev/null and b/samples/apachemodule/images/le_doyenne.jpg differ diff --git a/samples/apachemodule/images/lurton-pinot-gris.jpg b/samples/apachemodule/images/lurton-pinot-gris.jpg new file mode 100644 index 00000000..dc4f638e Binary files /dev/null and b/samples/apachemodule/images/lurton-pinot-gris.jpg differ diff --git a/samples/apachemodule/images/margerum.jpg b/samples/apachemodule/images/margerum.jpg new file mode 100644 index 00000000..4f59af8e Binary files /dev/null and b/samples/apachemodule/images/margerum.jpg differ diff --git a/samples/apachemodule/images/morizottes.jpg b/samples/apachemodule/images/morizottes.jpg new file mode 100644 index 00000000..9b081962 Binary files /dev/null and b/samples/apachemodule/images/morizottes.jpg differ diff --git a/samples/apachemodule/images/rex_hill.jpg b/samples/apachemodule/images/rex_hill.jpg new file mode 100644 index 00000000..cb3b4699 Binary files /dev/null and b/samples/apachemodule/images/rex_hill.jpg differ diff --git a/samples/apachemodule/images/saint_cosme.jpg b/samples/apachemodule/images/saint_cosme.jpg new file mode 100644 index 00000000..72ae561e Binary files /dev/null and b/samples/apachemodule/images/saint_cosme.jpg differ diff --git a/samples/apachemodule/images/viticcio.jpg b/samples/apachemodule/images/viticcio.jpg new file mode 100644 index 00000000..f0f405c0 Binary files /dev/null and b/samples/apachemodule/images/viticcio.jpg differ diff --git a/samples/apachemodule/staticfiles/css/milligram.css b/samples/apachemodule/staticfiles/css/milligram.css new file mode 100644 index 00000000..d253355e --- /dev/null +++ b/samples/apachemodule/staticfiles/css/milligram.css @@ -0,0 +1,602 @@ +/*! + * Milligram v1.3.0 + * https://milligram.github.io + * + * Copyright (c) 2017 CJ Patoilo + * Licensed under the MIT license + */ + +*, +*:after, +*:before { + box-sizing: inherit; +} + +html { + box-sizing: border-box; + font-size: 62.5%; +} + +body { + color: #606c76; + font-family: 'Roboto', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif; + font-size: 1.6em; + font-weight: 300; + letter-spacing: .01em; + line-height: 1.6; +} + +blockquote { + border-left: 0.3rem solid #d1d1d1; + margin-left: 0; + margin-right: 0; + padding: 1rem 1.5rem; +} + +blockquote *:last-child { + margin-bottom: 0; +} + +.button, +button, +input[type='button'], +input[type='reset'], +input[type='submit'] { + background-color: #9b4dca; + border: 0.1rem solid #9b4dca; + border-radius: .4rem; + color: #fff; + cursor: pointer; + display: inline-block; + font-size: 1.1rem; + font-weight: 700; + height: 3.8rem; + letter-spacing: .1rem; + line-height: 3.8rem; + padding: 0 3.0rem; + text-align: center; + text-decoration: none; + text-transform: uppercase; + white-space: nowrap; +} + +.button:focus, .button:hover, +button:focus, +button:hover, +input[type='button']:focus, +input[type='button']:hover, +input[type='reset']:focus, +input[type='reset']:hover, +input[type='submit']:focus, +input[type='submit']:hover { + background-color: #606c76; + border-color: #606c76; + color: #fff; + outline: 0; +} + +.button[disabled], +button[disabled], +input[type='button'][disabled], +input[type='reset'][disabled], +input[type='submit'][disabled] { + cursor: default; + opacity: .5; +} + +.button[disabled]:focus, .button[disabled]:hover, +button[disabled]:focus, +button[disabled]:hover, +input[type='button'][disabled]:focus, +input[type='button'][disabled]:hover, +input[type='reset'][disabled]:focus, +input[type='reset'][disabled]:hover, +input[type='submit'][disabled]:focus, +input[type='submit'][disabled]:hover { + background-color: #9b4dca; + border-color: #9b4dca; +} + +.button.button-outline, +button.button-outline, +input[type='button'].button-outline, +input[type='reset'].button-outline, +input[type='submit'].button-outline { + background-color: transparent; + color: #9b4dca; +} + +.button.button-outline:focus, .button.button-outline:hover, +button.button-outline:focus, +button.button-outline:hover, +input[type='button'].button-outline:focus, +input[type='button'].button-outline:hover, +input[type='reset'].button-outline:focus, +input[type='reset'].button-outline:hover, +input[type='submit'].button-outline:focus, +input[type='submit'].button-outline:hover { + background-color: transparent; + border-color: #606c76; + color: #606c76; +} + +.button.button-outline[disabled]:focus, .button.button-outline[disabled]:hover, +button.button-outline[disabled]:focus, +button.button-outline[disabled]:hover, +input[type='button'].button-outline[disabled]:focus, +input[type='button'].button-outline[disabled]:hover, +input[type='reset'].button-outline[disabled]:focus, +input[type='reset'].button-outline[disabled]:hover, +input[type='submit'].button-outline[disabled]:focus, +input[type='submit'].button-outline[disabled]:hover { + border-color: inherit; + color: #9b4dca; +} + +.button.button-clear, +button.button-clear, +input[type='button'].button-clear, +input[type='reset'].button-clear, +input[type='submit'].button-clear { + background-color: transparent; + border-color: transparent; + color: #9b4dca; +} + +.button.button-clear:focus, .button.button-clear:hover, +button.button-clear:focus, +button.button-clear:hover, +input[type='button'].button-clear:focus, +input[type='button'].button-clear:hover, +input[type='reset'].button-clear:focus, +input[type='reset'].button-clear:hover, +input[type='submit'].button-clear:focus, +input[type='submit'].button-clear:hover { + background-color: transparent; + border-color: transparent; + color: #606c76; +} + +.button.button-clear[disabled]:focus, .button.button-clear[disabled]:hover, +button.button-clear[disabled]:focus, +button.button-clear[disabled]:hover, +input[type='button'].button-clear[disabled]:focus, +input[type='button'].button-clear[disabled]:hover, +input[type='reset'].button-clear[disabled]:focus, +input[type='reset'].button-clear[disabled]:hover, +input[type='submit'].button-clear[disabled]:focus, +input[type='submit'].button-clear[disabled]:hover { + color: #9b4dca; +} + +code { + background: #f4f5f6; + border-radius: .4rem; + font-size: 86%; + margin: 0 .2rem; + padding: .2rem .5rem; + white-space: nowrap; +} + +pre { + background: #f4f5f6; + border-left: 0.3rem solid #9b4dca; + overflow-y: hidden; +} + +pre > code { + border-radius: 0; + display: block; + padding: 1rem 1.5rem; + white-space: pre; +} + +hr { + border: 0; + border-top: 0.1rem solid #f4f5f6; + margin: 3.0rem 0; +} + +input[type='email'], +input[type='number'], +input[type='password'], +input[type='search'], +input[type='tel'], +input[type='text'], +input[type='url'], +textarea, +select { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: transparent; + border: 0.1rem solid #d1d1d1; + border-radius: .4rem; + box-shadow: none; + box-sizing: inherit; + height: 3.8rem; + padding: .6rem 1.0rem; + width: 100%; +} + +input[type='email']:focus, +input[type='number']:focus, +input[type='password']:focus, +input[type='search']:focus, +input[type='tel']:focus, +input[type='text']:focus, +input[type='url']:focus, +textarea:focus, +select:focus { + border-color: #9b4dca; + outline: 0; +} + +select { + background: url('data:image/svg+xml;utf8,') center right no-repeat; + padding-right: 3.0rem; +} + +select:focus { + background-image: url('data:image/svg+xml;utf8,'); +} + +textarea { + min-height: 6.5rem; +} + +label, +legend { + display: block; + font-size: 1.6rem; + font-weight: 700; + margin-bottom: .5rem; +} + +fieldset { + border-width: 0; + padding: 0; +} + +input[type='checkbox'], +input[type='radio'] { + display: inline; +} + +.label-inline { + display: inline-block; + font-weight: normal; + margin-left: .5rem; +} + +.container { + margin: 0 auto; + max-width: 112.0rem; + padding: 0 2.0rem; + position: relative; + width: 100%; +} + +.row { + display: flex; + flex-direction: column; + padding: 0; + width: 100%; +} + +.row.row-no-padding { + padding: 0; +} + +.row.row-no-padding > .column { + padding: 0; +} + +.row.row-wrap { + flex-wrap: wrap; +} + +.row.row-top { + align-items: flex-start; +} + +.row.row-bottom { + align-items: flex-end; +} + +.row.row-center { + align-items: center; +} + +.row.row-stretch { + align-items: stretch; +} + +.row.row-baseline { + align-items: baseline; +} + +.row .column { + display: block; + flex: 1 1 auto; + margin-left: 0; + max-width: 100%; + width: 100%; +} + +.row .column.column-offset-10 { + margin-left: 10%; +} + +.row .column.column-offset-20 { + margin-left: 20%; +} + +.row .column.column-offset-25 { + margin-left: 25%; +} + +.row .column.column-offset-33, .row .column.column-offset-34 { + margin-left: 33.3333%; +} + +.row .column.column-offset-50 { + margin-left: 50%; +} + +.row .column.column-offset-66, .row .column.column-offset-67 { + margin-left: 66.6666%; +} + +.row .column.column-offset-75 { + margin-left: 75%; +} + +.row .column.column-offset-80 { + margin-left: 80%; +} + +.row .column.column-offset-90 { + margin-left: 90%; +} + +.row .column.column-10 { + flex: 0 0 10%; + max-width: 10%; +} + +.row .column.column-20 { + flex: 0 0 20%; + max-width: 20%; +} + +.row .column.column-25 { + flex: 0 0 25%; + max-width: 25%; +} + +.row .column.column-33, .row .column.column-34 { + flex: 0 0 33.3333%; + max-width: 33.3333%; +} + +.row .column.column-40 { + flex: 0 0 40%; + max-width: 40%; +} + +.row .column.column-50 { + flex: 0 0 50%; + max-width: 50%; +} + +.row .column.column-60 { + flex: 0 0 60%; + max-width: 60%; +} + +.row .column.column-66, .row .column.column-67 { + flex: 0 0 66.6666%; + max-width: 66.6666%; +} + +.row .column.column-75 { + flex: 0 0 75%; + max-width: 75%; +} + +.row .column.column-80 { + flex: 0 0 80%; + max-width: 80%; +} + +.row .column.column-90 { + flex: 0 0 90%; + max-width: 90%; +} + +.row .column .column-top { + align-self: flex-start; +} + +.row .column .column-bottom { + align-self: flex-end; +} + +.row .column .column-center { + -ms-grid-row-align: center; + align-self: center; +} + +@media (min-width: 40rem) { + .row { + flex-direction: row; + margin-left: -1.0rem; + width: calc(100% + 2.0rem); + } + .row .column { + margin-bottom: inherit; + padding: 0 1.0rem; + } +} + +a { + color: #9b4dca; + text-decoration: none; +} + +a:focus, a:hover { + color: #606c76; +} + +dl, +ol, +ul { + list-style: none; + margin-top: 0; + padding-left: 0; +} + +dl dl, +dl ol, +dl ul, +ol dl, +ol ol, +ol ul, +ul dl, +ul ol, +ul ul { + font-size: 90%; + margin: 1.5rem 0 1.5rem 3.0rem; +} + +ol { + list-style: decimal inside; +} + +ul { + list-style: circle inside; +} + +.button, +button, +dd, +dt, +li { + margin-bottom: 1.0rem; +} + +fieldset, +input, +select, +textarea { + margin-bottom: 1.5rem; +} + +blockquote, +dl, +figure, +form, +ol, +p, +pre, +table, +ul { + margin-bottom: 2.5rem; +} + +table { + border-spacing: 0; + width: 100%; +} + +td, +th { + border-bottom: 0.1rem solid #e1e1e1; + padding: 1.2rem 1.5rem; + text-align: left; +} + +td:first-child, +th:first-child { + padding-left: 0; +} + +td:last-child, +th:last-child { + padding-right: 0; +} + +b, +strong { + font-weight: bold; +} + +p { + margin-top: 0; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + font-weight: 300; + letter-spacing: -.1rem; + margin-bottom: 2.0rem; + margin-top: 0; +} + +h1 { + font-size: 4.6rem; + line-height: 1.2; +} + +h2 { + font-size: 3.6rem; + line-height: 1.25; +} + +h3 { + font-size: 2.8rem; + line-height: 1.3; +} + +h4 { + font-size: 2.2rem; + letter-spacing: -.08rem; + line-height: 1.35; +} + +h5 { + font-size: 1.8rem; + letter-spacing: -.05rem; + line-height: 1.5; +} + +h6 { + font-size: 1.6rem; + letter-spacing: 0; + line-height: 1.4; +} + +img { + max-width: 100%; +} + +.clearfix:after { + clear: both; + content: ' '; + display: table; +} + +.float-left { + float: left; +} + +.float-right { + float: right; +} + +/*# sourceMappingURL=milligram.css.map */ \ No newline at end of file diff --git a/samples/apachemodule/staticfiles/css/styles.css b/samples/apachemodule/staticfiles/css/styles.css new file mode 100644 index 00000000..4c56f8b3 --- /dev/null +++ b/samples/apachemodule/staticfiles/css/styles.css @@ -0,0 +1,107 @@ + +.header { + padding-top: 5px; +} + +#wineList { + overflow-y: scroll; + height: 80%; +} + +.leftArea { + position: absolute; + left: 10px; + top: 70px; + bottom: 20px; + width: 260px; + border:solid 1px #CCCCCC; + /*overflow-y: scroll;*/ +} + +.mainArea { + position: absolute; + top: 70px; + bottom: 20px; + left:300px; + /*overflow-y: scroll;*/ + width:300px; +} + +.rightArea { + position: absolute; + top: 70px; + bottom: 20px; + left:650px; + /*overflow-y: scroll;*/ + width:280px; +} + +ul { + list-style-type: none; + padding-left: 0px; + margin-top: 0px; +} + +li a { + text-decoration:none; + display: block; + color: #000000; + border-bottom:solid 1px #CCCCCC; + padding: 8px; +} + +li a:hover { + background-color: #4B0A1E; + color: #BA8A92; +} + +input, textarea { + border:1px solid #ccc; + min-height:30px; + outline: none; +} + +.mainArea input { + margin-bottom:15px; + margin-top:5px; + width:280px; +} + +textarea { + margin-bottom:15px; + margin-top:5px; + height: 200px; + width:250px; +} + +label { + display:block; +} + +button { + padding:6px; +} + + +#searchKey { + width:160px; +} + + +.footer { + position:fixed; + left:0px; + bottom:0px; + height:30px; + width:100%; + background:#999; +} + +.textcenter { + text-align: center; +} + +.padding02 { + padding: 2px; +} + diff --git a/samples/apachemodule/staticfiles/index.html b/samples/apachemodule/staticfiles/index.html new file mode 100644 index 00000000..2d908d26 --- /dev/null +++ b/samples/apachemodule/staticfiles/index.html @@ -0,0 +1,70 @@ + + + + + Cellar + + + + + + +
+ + + +
+ + +
+
    +
    + +
    + +
    + + + + + + + + + + + + + + + + + + +
    + + +
    +
    + +
    + + + + + +
    + +
    + + + + + + + + \ No newline at end of file diff --git a/samples/apachemodule/staticfiles/js/jquery-1.7.1.min.js b/samples/apachemodule/staticfiles/js/jquery-1.7.1.min.js new file mode 100644 index 00000000..198b3ff0 --- /dev/null +++ b/samples/apachemodule/staticfiles/js/jquery-1.7.1.min.js @@ -0,0 +1,4 @@ +/*! jQuery v1.7.1 jquery.com | jquery.org/license */ +(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cv(a){if(!ck[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){cl||(cl=c.createElement("iframe"),cl.frameBorder=cl.width=cl.height=0),b.appendChild(cl);if(!cm||!cl.createElement)cm=(cl.contentWindow||cl.contentDocument).document,cm.write((c.compatMode==="CSS1Compat"?"":"")+""),cm.close();d=cm.createElement(a),cm.body.appendChild(d),e=f.css(d,"display"),b.removeChild(cl)}ck[a]=e}return ck[a]}function cu(a,b){var c={};f.each(cq.concat.apply([],cq.slice(0,b)),function(){c[this]=a});return c}function ct(){cr=b}function cs(){setTimeout(ct,0);return cr=f.now()}function cj(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ci(){try{return new a.XMLHttpRequest}catch(b){}}function cc(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){if(c!=="border")for(;g=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?parseFloat(d):j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.1",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c
    a",d=q.getElementsByTagName("*"),e=q.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=q.getElementsByTagName("input")[0],b={leadingWhitespace:q.firstChild.nodeType===3,tbody:!q.getElementsByTagName("tbody").length,htmlSerialize:!!q.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:q.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete q.test}catch(s){b.deleteExpando=!1}!q.addEventListener&&q.attachEvent&&q.fireEvent&&(q.attachEvent("onclick",function(){b.noCloneEvent=!1}),q.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),q.appendChild(i),k=c.createDocumentFragment(),k.appendChild(q.lastChild),b.checkClone=k.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,k.removeChild(i),k.appendChild(q),q.innerHTML="",a.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",q.style.width="2px",q.appendChild(j),b.reliableMarginRight=(parseInt((a.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0);if(q.attachEvent)for(o in{submit:1,change:1,focusin:1})n="on"+o,p=n in q,p||(q.setAttribute(n,"return;"),p=typeof q[n]=="function"),b[o+"Bubbles"]=p;k.removeChild(q),k=g=h=j=q=i=null,f(function(){var a,d,e,g,h,i,j,k,m,n,o,r=c.getElementsByTagName("body")[0];!r||(j=1,k="position:absolute;top:0;left:0;width:1px;height:1px;margin:0;",m="visibility:hidden;border:0;",n="style='"+k+"border:5px solid #000;padding:0;'",o="
    "+""+"
    ",a=c.createElement("div"),a.style.cssText=m+"width:0;height:0;position:static;top:0;margin-top:"+j+"px",r.insertBefore(a,r.firstChild),q=c.createElement("div"),a.appendChild(q),q.innerHTML="
    t
    ",l=q.getElementsByTagName("td"),p=l[0].offsetHeight===0,l[0].style.display="",l[1].style.display="none",b.reliableHiddenOffsets=p&&l[0].offsetHeight===0,q.innerHTML="",q.style.width=q.style.paddingLeft="1px",f.boxModel=b.boxModel=q.offsetWidth===2,typeof q.style.zoom!="undefined"&&(q.style.display="inline",q.style.zoom=1,b.inlineBlockNeedsLayout=q.offsetWidth===2,q.style.display="",q.innerHTML="
    ",b.shrinkWrapBlocks=q.offsetWidth!==2),q.style.cssText=k+m,q.innerHTML=o,d=q.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,i={doesNotAddBorder:e.offsetTop!==5,doesAddBorderForTableAndCells:h.offsetTop===5},e.style.position="fixed",e.style.top="20px",i.fixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",i.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,i.doesNotIncludeMarginInBodyOffset=r.offsetTop!==j,r.removeChild(a),q=a=null,f.extend(b,i))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.nodeName.toLowerCase()]||f.valHooks[g.type];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;h=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/\bhover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function(a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")}; +f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;le&&i.push({elem:this,matches:d.slice(e)});for(j=0;j0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

    ";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
    ";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h0)for(h=g;h=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/",""],legend:[1,"
    ","
    "],thead:[1,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],col:[2,"","
    "],area:[1,"",""],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div
    ","
    "]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function() +{for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1>");try{for(var c=0,d=this.length;c1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||!bc.test("<"+a.nodeName)?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!_.test(k))k=b.createTextNode(k);else{k=k.replace(Y,"<$1>");var l=(Z.exec(k)||["",""])[1].toLowerCase(),m=bg[l]||bg._default,n=m[0],o=b.createElement("div");b===c?bh.appendChild(o):U(b).appendChild(o),o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=$.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]===""&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&X.test(k)&&o.insertBefore(b.createTextNode(X.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return br.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bq,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bq.test(g)?g.replace(bq,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bz(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bA=function(a,b){var c,d,e;b=b.replace(bs,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b)));return c}),c.documentElement.currentStyle&&(bB=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f===null&&g&&(e=g[b])&&(f=e),!bt.test(f)&&bu.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f||0,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),bz=bA||bB,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bD=/%20/g,bE=/\[\]$/,bF=/\r?\n/g,bG=/#.*$/,bH=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bI=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bJ=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bK=/^(?:GET|HEAD)$/,bL=/^\/\//,bM=/\?/,bN=/)<[^<]*)*<\/script>/gi,bO=/^(?:select|textarea)/i,bP=/\s+/,bQ=/([?&])_=[^&]*/,bR=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bS=f.fn.load,bT={},bU={},bV,bW,bX=["*/"]+["*"];try{bV=e.href}catch(bY){bV=c.createElement("a"),bV.href="",bV=bV.href}bW=bR.exec(bV.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bS)return bS.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
    ").append(c.replace(bN,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bO.test(this.nodeName)||bI.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bF,"\r\n")}}):{name:b.name,value:c.replace(bF,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b_(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b_(a,b);return a},ajaxSettings:{url:bV,isLocal:bJ.test(bW[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bX},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bZ(bT),ajaxTransport:bZ(bU),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?cb(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cc(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bH.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bG,"").replace(bL,bW[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bP),d.crossDomain==null&&(r=bR.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bW[1]&&r[2]==bW[2]&&(r[3]||(r[1]==="http:"?80:443))==(bW[3]||(bW[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),b$(bT,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bK.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bM.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bQ,"$1_="+x);d.url=y+(y===d.url?(bM.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bX+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=b$(bU,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)ca(g,a[g],c,e);return d.join("&").replace(bD,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cd=f.now(),ce=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cd++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(ce.test(b.url)||e&&ce.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(ce,l),b.url===j&&(e&&(k=k.replace(ce,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cf=a.ActiveXObject?function(){for(var a in ch)ch[a](0,1)}:!1,cg=0,ch;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ci()||cj()}:ci,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cf&&delete ch[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cg,cf&&(ch||(ch={},f(a).unload(cf)),ch[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var ck={},cl,cm,cn=/^(?:toggle|show|hide)$/,co=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cp,cq=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cr;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cu("show",3),a,b,c);for(var g=0,h=this.length;g=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cy(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cy(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,d,"padding")):this[d]():null},f.fn["outer"+c]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,d,a?"margin":"border")):this[d]():null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c],h=e.document.body;return e.document.compatMode==="CSS1Compat"&&g||h&&h["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var i=f.css(e,d),j=parseFloat(i);return f.isNumeric(j)?j:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window); \ No newline at end of file diff --git a/samples/apachemodule/staticfiles/js/main.js b/samples/apachemodule/staticfiles/js/main.js new file mode 100644 index 00000000..c2d2bd54 --- /dev/null +++ b/samples/apachemodule/staticfiles/js/main.js @@ -0,0 +1,203 @@ +// The root URL for the RESTful services +var rootURL = "/dmvc/api/wines"; + +var currentWine; + +$(document).ready(function(){ + //pupulate years combo + for (i = new Date().getFullYear(); i > 1940; i--) + { + $('#year').append($('
    + + + \ No newline at end of file diff --git a/samples/serversideviews_mustache/bin/templates/header.mustache b/samples/serversideviews_mustache/bin/templates/header.mustache new file mode 100644 index 00000000..fcb1dfd0 --- /dev/null +++ b/samples/serversideviews_mustache/bin/templates/header.mustache @@ -0,0 +1,15 @@ + + +
    + + +
    + + + +
    +

    Server Side Views Primer DMVCFramework

    \ No newline at end of file diff --git a/samples/serversideviews_mustache/bin/templates/people_header.csv.mustache b/samples/serversideviews_mustache/bin/templates/people_header.csv.mustache new file mode 100644 index 00000000..285ec9e0 --- /dev/null +++ b/samples/serversideviews_mustache/bin/templates/people_header.csv.mustache @@ -0,0 +1 @@ +guid;first_name;last_name;age diff --git a/samples/serversideviews_mustache/bin/templates/people_list.csv.mustache b/samples/serversideviews_mustache/bin/templates/people_list.csv.mustache new file mode 100644 index 00000000..44f9e271 --- /dev/null +++ b/samples/serversideviews_mustache/bin/templates/people_list.csv.mustache @@ -0,0 +1,2 @@ +{{#people}}{{guid}};"{{first_name}}";"{{last_name}}";{{age}} +{{/people}} \ No newline at end of file diff --git a/samples/serversideviews_mustache/bin/templates/people_list.mustache b/samples/serversideviews_mustache/bin/templates/people_list.mustache new file mode 100644 index 00000000..d0b66a87 --- /dev/null +++ b/samples/serversideviews_mustache/bin/templates/people_list.mustache @@ -0,0 +1,41 @@ +
    +
    +
    + + + + + + + + + + + {{^people}} + + + + {{/people}} {{#people}} + + + + + + + + {{/people}} + +
    #First nameLast nameAge 
    <<No People Found>>
    {{-index}}{{first_name}}{{last_name}}{{age}} + View +
    + + +
    + \ No newline at end of file diff --git a/sources/JsonDataObjects.pas b/sources/JsonDataObjects.pas index feaf91c9..cda12940 100644 --- a/sources/JsonDataObjects.pas +++ b/sources/JsonDataObjects.pas @@ -8260,3 +8260,4 @@ initialization JSONFormatSettings.DecimalSeparator := '.'; end. + diff --git a/sources/MVCFramework.ActiveRecord.pas b/sources/MVCFramework.ActiveRecord.pas index 69340324..08729efe 100644 --- a/sources/MVCFramework.ActiveRecord.pas +++ b/sources/MVCFramework.ActiveRecord.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // @@ -76,6 +76,7 @@ type end; MVCTableAttribute = class(MVCActiveRecordCustomAttribute) + public Name: string; constructor Create(aName: string); end; @@ -218,6 +219,7 @@ type function GetPK: TValue; class function GetByPK(const aValue: int64): T; overload; class function GetByPK(const aClass: TMVCActiveRecordClass; const aValue: int64): TMVCActiveRecord; overload; + class function GetScalar(const SQL: string; const Params: array of Variant): Variant; class function Select(const SQL: string; const Params: array of Variant; const Options: TMVCActiveRecordLoadOptions = []): TObjectList; overload; class function Select(const aClass: TMVCActiveRecordClass; const SQL: string; const Params: array of Variant) @@ -283,6 +285,7 @@ type TMVCConnectionsRepository = class(TInterfacedObject, IMVCActiveRecordConnections) private type TConnHolder = class + public Connection: TFDConnection; OwnsConnection: Boolean; destructor Destroy; override; @@ -905,6 +908,12 @@ begin Result := fPrimaryKey.GetValue(Self); end; +class function TMVCActiveRecord.GetScalar(const SQL: string; + const Params: array of Variant): Variant; +begin + Result := CurrentConnection.ExecSQLScalar(SQL, Params); +end; + function TMVCActiveRecord.CheckAction(const aEntityAction: TMVCEntityAction; const aRaiseException: Boolean): Boolean; begin Result := aEntityAction in fEntityAllowedActions; @@ -1437,7 +1446,7 @@ begin lAR := T.Create; try lSQL := lAR.SQLGenerator.CreateSelectSQLByRQL(RQL, lAR.GetMapping).Trim; - LogD(Format('RQL [%s] => SQL [%s]', [RQL, lSQL])); + //LogD(Format('RQL [%s] => SQL [%s]', [RQL, lSQL])); if lSQL.StartsWith('where', True) then lSQL := lSQL.Remove(0, 5).Trim; Result := Where(lSQL, []); @@ -1772,3 +1781,4 @@ gCtx.Free; gLock.Free; end. + diff --git a/sources/MVCFramework.ActiveRecordController.pas b/sources/MVCFramework.ActiveRecordController.pas index f1108a00..8f85f7df 100644 --- a/sources/MVCFramework.ActiveRecordController.pas +++ b/sources/MVCFramework.ActiveRecordController.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/sources/MVCFramework.ApplicationSession.pas b/sources/MVCFramework.ApplicationSession.pas index c0c063aa..0ff1b308 100644 --- a/sources/MVCFramework.ApplicationSession.pas +++ b/sources/MVCFramework.ApplicationSession.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/sources/MVCFramework.Cache.pas b/sources/MVCFramework.Cache.pas index 5caffdf8..b38ccace 100644 --- a/sources/MVCFramework.Cache.pas +++ b/sources/MVCFramework.Cache.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/sources/MVCFramework.Commons.pas b/sources/MVCFramework.Commons.pas index 37656a5b..4ce4ea5d 100644 --- a/sources/MVCFramework.Commons.pas +++ b/sources/MVCFramework.Commons.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // @@ -42,7 +42,6 @@ uses {$I dmvcframeworkbuildconsts.inc} - type TMVCHTTPMethodType = (httpGET, httpPOST, httpPUT, httpDELETE, httpHEAD, httpOPTIONS, httpPATCH, httpTRACE); @@ -110,7 +109,7 @@ type URL_MAPPED_PARAMS_ALLOWED_CHARS = ' àèéùòì@\[\]\{\}\(\)\=;&#\.\_\,%\w\d\x2D\x3A'; OneMiB = 1048576; OneKiB = 1024; - DEFAULT_MAX_REQUEST_SIZE = OneMiB * 5; //5 MiB + DEFAULT_MAX_REQUEST_SIZE = OneMiB * 5; // 5 MiB end; TMVCConfigKey = record @@ -130,7 +129,7 @@ type SessionType = 'session_type'; FallbackResource = 'fallback_resource'; MaxEntitiesRecordCount = 'max_entities_record_count'; - MaxRequestSize = 'max_request_size'; //bytes + MaxRequestSize = 'max_request_size'; // bytes end; // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html @@ -343,8 +342,7 @@ type end; TMVCStringDictionary = class - strict - private + strict private function GetItems(const Key: string): string; procedure SetItems(const Key, Value: string); protected @@ -354,7 +352,8 @@ type destructor Destroy; override; procedure Clear; function AddProperty(const Name, Value: string): TMVCStringDictionary; - function TryGetValue(const Name: string; out Value: string): Boolean; + function TryGetValue(const Name: string; out Value: string): Boolean; overload; + function TryGetValue(const Name: string; out Value: Integer): Boolean; overload; function Count: Integer; function GetEnumerator: TDictionary.TPairEnumerator; function ContainsKey(const Key: string): Boolean; @@ -451,7 +450,8 @@ function URLSafeB64Decode(const Value: string): string; function ByteToHex(AInByte: Byte): string; function BytesToHex(ABytes: TBytes): string; -procedure SplitContentMediaTypeAndCharset(const aContentType: string; var aContentMediaType: string; var aContentCharSet: string); +procedure SplitContentMediaTypeAndCharset(const aContentType: string; var aContentMediaType: string; + var aContentCharSet: string); function BuildContentType(const aContentMediaType: string; const aContentCharSet: string): string; const @@ -467,10 +467,11 @@ var Lock: TObject; const - RESERVED_IPS: array [1 .. 11] of array [1 .. 2] of string = (('0.0.0.0', '0.255.255.255'), ('10.0.0.0', '10.255.255.255'), - ('127.0.0.0', '127.255.255.255'), ('169.254.0.0', '169.254.255.255'), ('172.16.0.0', '172.31.255.255'), ('192.0.2.0', '192.0.2.255'), - ('192.88.99.0', '192.88.99.255'), ('192.168.0.0', '192.168.255.255'), ('198.18.0.0', '198.19.255.255'), - ('224.0.0.0', '239.255.255.255'), ('240.0.0.0', '255.255.255.255')); + RESERVED_IPS: array [1 .. 11] of array [1 .. 2] of string = (('0.0.0.0', '0.255.255.255'), + ('10.0.0.0', '10.255.255.255'), ('127.0.0.0', '127.255.255.255'), ('169.254.0.0', '169.254.255.255'), + ('172.16.0.0', '172.31.255.255'), ('192.0.2.0', '192.0.2.255'), ('192.88.99.0', '192.88.99.255'), + ('192.168.0.0', '192.168.255.255'), ('198.18.0.0', '198.19.255.255'), ('224.0.0.0', '239.255.255.255'), + ('240.0.0.0', '255.255.255.255')); implementation @@ -504,7 +505,8 @@ begin if AIP.IsEmpty then Exit(0); lPieces := AIP.Split(['.']); - Result := (StrToInt(lPieces[0]) * 16777216) + (StrToInt(lPieces[1]) * 65536) + (StrToInt(lPieces[2]) * 256) + StrToInt(lPieces[3]); + Result := (StrToInt(lPieces[0]) * 16777216) + (StrToInt(lPieces[1]) * 65536) + (StrToInt(lPieces[2]) * 256) + + StrToInt(lPieces[3]); end; // function IP2Long(const AIP: string): UInt32; @@ -559,7 +561,8 @@ begin Result := Result.ToLower.Replace(' ', '', [rfReplaceAll]); end; -procedure SplitContentMediaTypeAndCharset(const aContentType: string; var aContentMediaType: string; var aContentCharSet: string); +procedure SplitContentMediaTypeAndCharset(const aContentType: string; var aContentMediaType: string; + var aContentCharSet: string); var lContentTypeValues: TArray; begin @@ -705,7 +708,8 @@ begin end end else - raise EMVCConfigException.Create('DMVCFramework configuration file [' + AFileName + '] does not contain a valid JSONObject'); + raise EMVCConfigException.Create('DMVCFramework configuration file [' + AFileName + + '] does not contain a valid JSONObject'); end else raise EMVCConfigException.Create('Cannot load DMVCFramework configuration file [' + AFileName + ']'); @@ -798,6 +802,13 @@ begin FDict.AddOrSetValue(Key, Value); end; +function TMVCStringDictionary.TryGetValue(const Name: string; out Value: Integer): Boolean; +var + lTmp: String; +begin + Result := TryGetValue(Name, lTmp) and TryStrToInt(lTmp, Value); +end; + function TMVCStringDictionary.TryGetValue(const Name: string; out Value: string): Boolean; begin Result := FDict.TryGetValue(name, Value); @@ -857,7 +868,8 @@ type end; const - GURLSafeBase64CodeTable: string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'; { Do not Localize } + GURLSafeBase64CodeTable: string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'; + { Do not Localize } procedure TURLSafeEncode.InitComponent; begin @@ -969,5 +981,3 @@ finalization FreeAndNil(Lock); end. - - diff --git a/sources/MVCFramework.Controllers.CacheController.pas b/sources/MVCFramework.Controllers.CacheController.pas index 2b9a7fd6..f5a4797f 100644 --- a/sources/MVCFramework.Controllers.CacheController.pas +++ b/sources/MVCFramework.Controllers.CacheController.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/sources/MVCFramework.DataSet.Utils.pas b/sources/MVCFramework.DataSet.Utils.pas index 3229114e..75b7d465 100644 --- a/sources/MVCFramework.DataSet.Utils.pas +++ b/sources/MVCFramework.DataSet.Utils.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // @@ -64,7 +64,7 @@ type procedure AppendFromJSONArrayString(AJSONArrayString: string); overload; procedure AppendFromJSONArrayString(AJSONArrayString: string; AIgnoredFields: TArray; AFieldNamePolicy: TFieldNamePolicy = TFieldNamePolicy.fpLowerCase); overload; - function AsObjectList(CloseAfterScroll: boolean = false): TObjectList; + function AsObjectList(CloseAfterScroll: boolean = false; OwnsObjects: boolean = true): TObjectList; function AsObject(CloseAfterScroll: boolean = false): T; end; @@ -167,16 +167,16 @@ begin Result := nil; end; -function TDataSetHelper.AsObjectList(CloseAfterScroll: boolean): TObjectList; +function TDataSetHelper.AsObjectList(CloseAfterScroll: boolean; OwnsObjects: boolean): TObjectList; var - Objs: TObjectList; + lObjs: TObjectList; begin - Objs := TObjectList.Create(True); + lObjs := TObjectList.Create(OwnsObjects); try - TDataSetUtils.DataSetToObjectList(Self, Objs, CloseAfterScroll); - Result := Objs; + TDataSetUtils.DataSetToObjectList(Self, lObjs, CloseAfterScroll); + Result := lObjs; except - FreeAndNil(Objs); + FreeAndNil(lObjs); raise; end; end; @@ -189,8 +189,6 @@ begin try lSerializer := TMVCJsonDataObjectsSerializer.Create; lSerializer.DeserializeDataSet(AJSONArray, Self, nil, ncAsIs); - // Mapper.JSONArrayToDataSet(AJSONArray, Self, TArray.Create(), false, - // AFieldNamePolicy); finally Self.EnableControls; end; diff --git a/sources/MVCFramework.DuckTyping.pas b/sources/MVCFramework.DuckTyping.pas index 3a15f822..4a538513 100644 --- a/sources/MVCFramework.DuckTyping.pas +++ b/sources/MVCFramework.DuckTyping.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/sources/MVCFramework.FireDAC.Utils.pas b/sources/MVCFramework.FireDAC.Utils.pas index aa77101f..b6e7d23f 100644 --- a/sources/MVCFramework.FireDAC.Utils.pas +++ b/sources/MVCFramework.FireDAC.Utils.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/sources/MVCFramework.HMAC.pas b/sources/MVCFramework.HMAC.pas index dc5b04a4..f24f1456 100644 --- a/sources/MVCFramework.HMAC.pas +++ b/sources/MVCFramework.HMAC.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/sources/MVCFramework.JSONRPC.Client.pas b/sources/MVCFramework.JSONRPC.Client.pas index 81cdfe0f..aa3c4a8f 100644 --- a/sources/MVCFramework.JSONRPC.Client.pas +++ b/sources/MVCFramework.JSONRPC.Client.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // @@ -41,6 +41,11 @@ type procedure AddHTTPHeader(const aNetHeader: TNetHeader); procedure ClearHTTPHeaders; function HTTPHeadersCount: Integer; + function SetOnReceiveData(const aOnReceiveData: TReceiveDataEvent): IMVCJSONRPCExecutor; + function SetOnNeedClientCertificate(const aOnNeedClientCertificate: TNeedClientCertificateEvent) + : IMVCJSONRPCExecutor; + function SetOnValidateServerCertificate(const aOnValidateServerCertificate: TValidateCertificateEvent) + : IMVCJSONRPCExecutor; end; TMVCJSONRPCExecutor = class(TInterfacedObject, IMVCJSONRPCExecutor) @@ -52,15 +57,20 @@ type function GetHTTPRequestHeaders: TList; protected function InternalExecute(const aJSONRPCObject: IJSONRPCObject): IJSONRPCResponse; - public - constructor Create(const aURL: string; const aRaiseExceptionOnError: Boolean = True); virtual; - destructor Destroy; override; function ExecuteRequest(const aJSONRPCRequest: IJSONRPCRequest): IJSONRPCResponse; procedure ExecuteNotification(const aJSONRPCNotification: IJSONRPCNotification); // Http headers handling procedure AddHTTPHeader(const aNetHeader: TNetHeader); procedure ClearHTTPHeaders; function HTTPHeadersCount: Integer; + function SetOnReceiveData(const aOnReceiveData: TReceiveDataEvent): IMVCJSONRPCExecutor; + function SetOnNeedClientCertificate(const aOnNeedClientCertificate: TNeedClientCertificateEvent) + : IMVCJSONRPCExecutor; + function SetOnValidateServerCertificate(const aOnValidateServerCertificate: TValidateCertificateEvent) + : IMVCJSONRPCExecutor; + public + constructor Create(const aURL: string; const aRaiseExceptionOnError: Boolean = True); virtual; + destructor Destroy; override; end; implementation @@ -69,7 +79,8 @@ uses System.Classes, System.SysUtils; -procedure JSONRPCExec(const aJSONRPCURL: string; const aJSONRPCRequest: IJSONRPCRequest; out aJSONRPCResponse: IJSONRPCResponse); +procedure JSONRPCExec(const aJSONRPCURL: string; const aJSONRPCRequest: IJSONRPCRequest; + out aJSONRPCResponse: IJSONRPCResponse); var lSS: TStringStream; lHttpResp: IHTTPResponse; @@ -80,8 +91,8 @@ begin lSS.Position := 0; lHTTP := THTTPClient.Create; try - lHttpResp := lHTTP.Post('http://localhost:8080/jsonrpc', lSS, nil, [TNetHeader.Create('content-type', 'application/json'), - TNetHeader.Create('accept', 'application/json')]); + lHttpResp := lHTTP.Post('http://localhost:8080/jsonrpc', lSS, nil, + [TNetHeader.Create('content-type', 'application/json'), TNetHeader.Create('accept', 'application/json')]); if (lHttpResp.StatusCode <> 204) then begin aJSONRPCResponse := TJSONRPCResponse.Create; @@ -119,6 +130,7 @@ begin FURL := aURL; FHTTP := THTTPClient.Create; FHTTPRequestHeaders := nil; + SetOnReceiveData(nil).SetOnNeedClientCertificate(nil).SetOnValidateServerCertificate(nil); end; destructor TMVCJSONRPCExecutor.Destroy; @@ -174,11 +186,11 @@ begin end; Result := nil; - lSS := TStringStream.Create(aJSONRPCObject.AsJSONString); + lSS := TStringStream.Create(aJSONRPCObject.AsJSONString, TEncoding.UTF8); try lSS.Position := 0; - lHttpResp := FHTTP.Post(FURL, lSS, nil, [TNetHeader.Create('content-type', 'application/json'), - TNetHeader.Create('accept', 'application/json')] + lCustomHeaders); + lHttpResp := FHTTP.Post(FURL, lSS, nil, [TNetHeader.Create('content-type', 'application/json;charset=utf8'), + TNetHeader.Create('accept', 'application/json;charset=utf8')] + lCustomHeaders); if (lHttpResp.StatusCode <> HTTP_STATUS.NoContent) then begin lJSONRPCResponse := TJSONRPCResponse.Create; @@ -192,4 +204,25 @@ begin end; end; +function TMVCJSONRPCExecutor.SetOnNeedClientCertificate(const aOnNeedClientCertificate: TNeedClientCertificateEvent) + : IMVCJSONRPCExecutor; +begin + FHTTP.OnNeedClientCertificate := aOnNeedClientCertificate; + Result := Self; +end; + +function TMVCJSONRPCExecutor.SetOnReceiveData( + const aOnReceiveData: TReceiveDataEvent): IMVCJSONRPCExecutor; +begin + FHTTP.OnReceiveData := aOnReceiveData; + Result := Self; +end; + +function TMVCJSONRPCExecutor.SetOnValidateServerCertificate(const aOnValidateServerCertificate + : TValidateCertificateEvent): IMVCJSONRPCExecutor; +begin + FHTTP.OnValidateServerCertificate := aOnValidateServerCertificate; + Result := self; +end; + end. diff --git a/sources/MVCFramework.JSONRPC.pas b/sources/MVCFramework.JSONRPC.pas index 8ef561d7..15031f49 100644 --- a/sources/MVCFramework.JSONRPC.pas +++ b/sources/MVCFramework.JSONRPC.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // @@ -63,25 +63,25 @@ type TMVCJSONRPCMessage = class abstract(TInterfacedObject, IMVCJSONRPCMessage) private - fJSON: TJsonObject; + fJSON: TJDOJsonObject; protected - class procedure CheckVersion(const aJSON: TJsonObject); - class procedure CheckMethod(const aJSON: TJsonObject); - class procedure CheckID(const aJSON: TJsonObject; out aIsNotification: Boolean); + class procedure CheckVersion(const aJSON: TJDOJsonObject); + class procedure CheckMethod(const aJSON: TJDOJsonObject); + class procedure CheckID(const aJSON: TJDOJsonObject; out aIsNotification: Boolean); constructor Create; overload; - procedure Build(const aJSON: TJsonObject); virtual; abstract; + procedure Build(const aJSON: TJDOJsonObject); virtual; abstract; { IMVCJSONRPCMessage } function AsJSONRPCMessage: string; - function AsJSON: TJsonObject; virtual; + function AsJSON: TJDOJsonObject; virtual; end; IJSONRPCObject = interface ['{98E161EE-B106-4023-8722-3C2CB1B4CE87}'] procedure SetJsonString(const Value: string); function GetJSONString: string; - function GetJSON: TJsonObject; - procedure SetJSON(const Value: TJsonObject); - property AsJSON: TJsonObject read GetJSON write SetJSON; + function GetJSON: TJDOJsonObject; + procedure SetJSON(const Value: TJDOJsonObject); + property AsJSON: TJDOJsonObject read GetJSON write SetJSON; property AsJSONString: string read GetJSONString write SetJsonString; end; @@ -89,19 +89,48 @@ type protected procedure SetJsonString(const Value: string); virtual; function GetJSONString: string; virtual; - function GetJSON: TJsonObject; virtual; - procedure SetJSON(const Value: TJsonObject); virtual; - property AsJSON: TJsonObject read GetJSON write SetJSON; + function GetJSON: TJDOJsonObject; virtual; + procedure SetJSON(const Value: TJDOJsonObject); virtual; + property AsJSON: TJDOJsonObject read GetJSON write SetJSON; property AsJSONString: string read GetJSONString write SetJsonString; public constructor Create; virtual; end; - TJSONRPCRequestParams = TList; + TJSONRPCParamDataType = (pdtString, pdtInteger, pdtLongInteger, pdTJDOJsonObject, pdtJSONArray, pdtBoolean, pdtDate, + pdtTime, pdtDateTime, pdtFloat, pdtObject); + + TJSONRPCRequestParams = class + private + function GetItem(const Index: Integer): TValue; + function GetItemDataType(const Index: Integer): TJSONRPCParamDataType; + protected + FParamsValue: TList; + FParamsType: TList; + public + constructor Create; virtual; + destructor Destroy; override; + procedure Clear; + function Count: Integer; + property Items[const Index: Integer]: TValue read GetItem; default; + property ItemsType[const Index: Integer]: TJSONRPCParamDataType read GetItemDataType; + function ToArray: TArray; + procedure Add(const Value: String); overload; + procedure Add(const Value: Integer); overload; + procedure Add(const Value: TJDOJsonObject); overload; + procedure Add(const Value: TJDOJsonArray); overload; + procedure Add(const Value: Boolean); overload; + procedure Add(const Value: TDate); overload; + procedure Add(const Value: TTime); overload; + procedure Add(const Value: TDateTime); overload; + procedure Add(const Value: Double); overload; + procedure Add(const Value: TValue; const ParamType: TJSONRPCParamDataType); overload; + end; IJSONRPCNotification = interface(IJSONRPCObject) ['{FAA65A29-3305-4303-833E-825BDBD3FF7F}'] procedure SetMethod(const Value: string); + procedure FillParameters(const JSON: TJDOJsonObject; const RTTIMethod: TRTTIMethod); function GetMethod: string; function GetParams: TJSONRPCRequestParams; property Method: string read GetMethod write SetMethod; @@ -112,10 +141,11 @@ type protected FMethod: string; FParams: TJSONRPCRequestParams; + procedure FillParameters(const JSON: TJDOJsonObject; const RTTIMethod: TRTTIMethod); procedure SetMethod(const Value: string); function GetMethod: string; function GetParams: TJSONRPCRequestParams; - function GetJSON: TJsonObject; override; + function GetJSON: TJDOJsonObject; override; property Method: string read GetMethod write SetMethod; property Params: TJSONRPCRequestParams read GetParams; public @@ -143,8 +173,8 @@ type function GetRequestType: TJSONRPCRequestType; function GetID: TValue; protected - procedure SetJSON(const JSON: TJsonObject); override; - function GetJSON: TJsonObject; override; + procedure SetJSON(const JSON: TJDOJsonObject); override; + function GetJSON: TJDOJsonObject; override; procedure SetID(const Value: TValue); property RequestType: TJSONRPCRequestType read GetRequestType; property RequestID: TValue read GetID write SetID; @@ -175,8 +205,8 @@ type function GetError: TJSONRPCResponseError; procedure SetError(const Value: TJSONRPCResponseError); function IsError: Boolean; - function ResultAsJSONObject: TJsonObject; - function ResultAsJSONArray: TJsonArray; + function ResultAsJSONObject: TJDOJsonObject; + function ResultAsJSONArray: TJDOJsonArray; property Result: TValue read GetResult write SetResult; property Error: TJSONRPCResponseError read GetError write SetError; property RequestID: TValue read GetID write SetID; @@ -189,15 +219,15 @@ type FID: TValue; function GetResult: TValue; protected - function GetJSON: TJsonObject; override; - procedure SetJSON(const JSON: TJsonObject); override; + function GetJSON: TJDOJsonObject; override; + procedure SetJSON(const JSON: TJDOJsonObject); override; procedure SetID(const Value: TValue); procedure SetResult(const Value: TValue); procedure SetError(const Value: TJSONRPCResponseError); function GetError: TJSONRPCResponseError; function GetID: TValue; - function ResultAsJSONObject: TJsonObject; - function ResultAsJSONArray: TJsonArray; + function ResultAsJSONObject: TJDOJsonObject; + function ResultAsJSONArray: TJDOJsonArray; function IsError: Boolean; property Result: TValue read GetResult write SetResult; property Error: TJSONRPCResponseError read GetError write SetError; @@ -229,7 +259,7 @@ type EMVCJSONRPCInvalidRequest = class(EMVCJSONRPCErrorResponse) public - constructor Create; + constructor Create(const Message: String = ''); overload; end; EMVCJSONRPCMethodNotFound = class(EMVCJSONRPCErrorResponse) @@ -239,7 +269,7 @@ type EMVCJSONRPCInvalidParams = class(EMVCJSONRPCErrorResponse) public - constructor Create(const message: string); + constructor Create(const Message: string); end; EMVCJSONRPCInternalError = class(EMVCJSONRPCErrorResponse) @@ -250,10 +280,10 @@ type { -32000 to -32099 Server error Reserved for implementation-defined server-errors. } EMVCJSONRPCServerError = class(EMVCJSONRPCErrorResponse) public - constructor Create(const JSONRPCError: Integer; const message: string); + constructor Create(const JSONRPCError: Integer; const Message: string); end; - TMVCJSONObject = TJsonObject; + TMVCJSONObject = TJDOJsonObject; TMVCJSONArray = TJDOJsonArray; TMVCJSONRPCController = class(TMVCController) @@ -262,12 +292,12 @@ type fRPCInstance: TObject; fOwsRPCInstance: Boolean; function GetSerializer: TMVCJsonDataObjectsSerializer; - procedure InjectParams(lJSONRPCReq: TJSONRPCRequest; lRTTIMethod: TRttiMethod); + // procedure CheckInputParametersTypes(aRTTIMethod: TRTTIMethod); protected - function CreateError(const RequestID: TValue; const ErrorCode: Integer; const message: string): TJsonObject; + function CreateError(const RequestID: TValue; const ErrorCode: Integer; const Message: string): TJDOJsonObject; function CreateResponse(const RequestID: TValue; const Value: TValue): TJSONRPCResponse; - function CreateRequest(const JSON: TJsonObject): TJSONRPCRequest; - function JSONObjectAs(const JSON: TJsonObject): T; + function CreateRequest(const JSON: TJDOJsonObject): IJSONRPCRequest; + function JSONObjectAs(const JSON: TJDOJsonObject): T; public [MVCPath] [MVCHTTPMethods([httpPOST])] @@ -293,7 +323,7 @@ type constructor Create; virtual; procedure StartGeneration; virtual; procedure EndGeneration; virtual; - procedure VisitMethod(const aRTTIMethod: TRttiMethod); virtual; abstract; + procedure VisitMethod(const aRTTIMethod: TRTTIMethod); virtual; abstract; function GetCode: String; virtual; abstract; end; @@ -314,7 +344,182 @@ const var GProxyGeneratorsRegister: TDictionary; -function JSONDataValueToTValue(const JSONDataValue: TJsonDataValueHelper): TValue; +procedure AppendTValueToJsonArray(const Value: TValue; const ParamType: TJSONRPCParamDataType; + const JSONArr: TJDOJsonArray); +var + lSer: TMVCJsonDataObjectsSerializer; + lJArr: TJDOJsonArray; + LJObj: TJDOJsonObject; + lOrdinalValue: Int64; +begin + case ParamType of + pdtInteger: + begin + JSONArr.Add(Value.AsInteger); + end; + pdtFloat: + begin + JSONArr.Add(Value.AsExtended); + end; + pdtDateTime: + begin + JSONArr.Add(DateTimeToISOTimeStamp(TDateTime(Value.AsExtended))); + end; + pdtDate: + begin + JSONArr.Add(DateToISODate(TDate(Value.AsExtended))); + end; + pdtTime: + begin + JSONArr.Add(TimeToISOTime(TTime(Value.AsExtended))); + end; + pdtString: + begin + JSONArr.Add(Value.AsString); + end; + pdtLongInteger: + begin + JSONArr.Add(Value.AsInt64); + end; + pdtBoolean: + begin + if not Value.TryAsOrdinal(lOrdinalValue) then + begin + raise EMVCException.Create('Invalid ordinal parameter'); + end; + JSONArr.Add(lOrdinalValue = 1); + end; + pdTJDOJsonObject: + begin + JSONArr.Add((Value.AsObject as TJDOJsonObject).Clone as TJDOJsonObject); + end; + pdtJSONArray: + begin + JSONArr.Add((Value.AsObject as TJDOJsonArray).Clone as TJDOJsonArray); + end; + pdtObject: + begin + if Value.AsObject is TDataSet then + begin + lSer := TMVCJsonDataObjectsSerializer.Create; + try + lJArr := TJDOJsonArray.Create; + JSONArr.Add(lJArr); + lSer.DataSetToJsonArray(TDataSet(Value.AsObject), lJArr, TMVCNameCase.ncLowerCase, []); + finally + lSer.Free; + end + end + else + begin + lSer := TMVCJsonDataObjectsSerializer.Create; + try + LJObj := lSer.SerializeObjectToJSON(Value.AsObject, TMVCSerializationType.stProperties, [], nil); + JSONArr.Add(LJObj); + finally + lSer.Free; + end; + end; + end; + else + raise EMVCException.Create('Invalid type'); + end; + + // + // case Value.Kind of + // tkInteger: + // begin + // JSONArr.Add(Value.AsInteger); + // end; + // tkFloat: + // begin + // if Value.IsType(TypeInfo(System.TDateTime), False) then + // begin + // lIsDate := Frac(Value.AsExtended) = 0; + // lIsTime := Trunc(Value.AsExtended) = 0; + // if lIsDate then + // begin + // JSONArr.Add(DateToISODate(Value.AsExtended)); + // end + // else if lIsTime then + // begin + // JSONArr.Add(TimeToISOTime(Value.AsExtended)); + // end + // else + // begin + // JSONArr.Add(DateTimeToISOTimeStamp(Value.AsExtended)); + // end; + // end + // else + // begin + // JSONArr.Add(Value.AsExtended); + // end; + // end; + // tkString, tkUString, tkWChar, tkLString, tkWString: + // begin + // JSONArr.Add(Value.AsString); + // end; + // tkInt64: + // begin + // JSONArr.Add(Value.AsInt64); + // end; + // tkEnumeration: + // begin + // if not Value.TryAsOrdinal(lOrdinalValue) then + // begin + // raise EMVCException.Create('Invalid ordinal parameter'); + // end; + // if Value.IsType(TypeInfo(System.Boolean)) then + // begin + // JSONArr.Add(lOrdinalValue = 1); + // end + // else + // begin + // JSONArr.Add(lOrdinalValue); + // end; + // end; + // tkClass: + // begin + // if Value.AsObject is TJDOJsonObject then + // begin + // LJObj := TJDOJsonObject.Create; + // JSONArr.Add(LJObj); + // LJObj.Assign(TJDOJsonObject(Value.AsObject)); + // end + // else if Value.AsObject is TJDOJsonArray then + // begin + // lJArr := TJDOJsonArray.Create; + // JSONArr.Add(lJArr); + // lJArr.Assign(TJDOJsonArray(Value.AsObject)); + // end + // else if Value.AsObject is TDataSet then + // begin + // lSer := TMVCJsonDataObjectsSerializer.Create; + // try + // lJArr := TJDOJsonArray.Create; + // JSONArr.Add(lJArr); + // lSer.DataSetToJsonArray(TDataSet(Value.AsObject), lJArr, TMVCNameCase.ncLowerCase, []); + // finally + // lSer.Free; + // end + // end + // else + // begin + // lSer := TMVCJsonDataObjectsSerializer.Create; + // try + // LJObj := lSer.SerializeObjectToJSON(Value.AsObject, TMVCSerializationType.stProperties, [], nil); + // JSONArr.Add(LJObj); + // finally + // lSer.Free; + // end; + // end; + // end; + // else + // raise EMVCException.Create('Invalid type'); + // end; +end; + +function JSONDataValueToTValue(const JSONDataValue: TJsonDataValueHelper): TValue; overload; begin case JSONDataValue.Typ of jdtString: @@ -331,12 +536,11 @@ begin end; jdtArray: begin - Result := TJsonArray.Parse(JSONDataValue.ArrayValue.ToJSON) as TJsonArray; + Result := JSONDataValue.ArrayValue.Clone as TJDOJsonArray; end; jdtObject: begin - { TODO -oDanieleT -cGeneral : Can be deserialized in a PODO? } - Result := TJsonObject.Parse(JSONDataValue.ObjectValue.ToJSON) as TJsonObject; + Result := JSONDataValue.ObjectValue.Clone as TJDOJsonObject; end; jdtInt: begin @@ -355,9 +559,198 @@ begin end; end; +function BuildDeclaration(const RTTIParameter: TRttiParameter): String; +begin + Result := RTTIParameter.Name + ': ' + RTTIParameter.ParamType.Name; +end; + +procedure JSONDataValueToTValueParam(const JSONDataValue: TJsonDataValueHelper; const RTTIParameter: TRttiParameter; + const JSONRPCRequestParams: TJSONRPCRequestParams); +begin + case RTTIParameter.ParamType.TypeKind of + tkString, tkUString, tkAnsiString: + begin + if JSONDataValue.Typ <> jdtString then + begin + raise EMVCJSONRPCInvalidParams.Create('Invalid param type for [' + BuildDeclaration(RTTIParameter) + ']'); + end; + JSONRPCRequestParams.Add(JSONDataValue.Value); + end; + tkFloat: + begin + if SameText(RTTIParameter.ParamType.Name, 'TDate') then + begin + JSONRPCRequestParams.Add(ISODateToDate(JSONDataValue.Value), pdtDate); + end + else if SameText(RTTIParameter.ParamType.Name, 'TDateTime') then + begin + JSONRPCRequestParams.Add(JSONDataValue.UtcDateTimeValue, pdtDateTime); + end + else if SameText(RTTIParameter.ParamType.Name, 'TTime') then + begin + JSONRPCRequestParams.Add(ISOTimeToTime(JSONDataValue.Value), pdtTime); + end + else + begin + if JSONDataValue.Typ <> jdtFloat then + begin + raise EMVCJSONRPCInvalidRequest.Create(BuildDeclaration(RTTIParameter)); + end; + JSONRPCRequestParams.Add(JSONDataValue.FloatValue, pdtFloat); + end + end; + tkEnumeration: + begin + if JSONDataValue.Typ <> jdtBool then + begin + raise EMVCJSONRPCInvalidRequest.Create(BuildDeclaration(RTTIParameter)); + end; + JSONRPCRequestParams.Add(JSONDataValue.BoolValue, pdtBoolean); + end; + tkClass: + begin + if (SameText(RTTIParameter.ParamType.Name, TJDOJsonArray.ClassName)) then + begin + JSONRPCRequestParams.Add(JSONDataValue.ArrayValue.Clone, pdtJSONArray); + end + else if SameText(RTTIParameter.ParamType.Name, TJDOJsonObject.ClassName) then + begin + JSONRPCRequestParams.Add(JSONDataValue.ObjectValue.Clone as TJDOJsonObject, pdTJDOJsonObject); + end + else + begin + { TODO -oDanieleT -cGeneral : Automatically inject the dseserialized version of arbitrary object? } + raise EMVCJSONRPCInvalidRequest.Create(BuildDeclaration(RTTIParameter)); + end; + end; + tkInteger: + begin + if JSONDataValue.Typ <> jdtInt then + begin + raise EMVCJSONRPCInvalidRequest.Create(BuildDeclaration(RTTIParameter)); + end; + JSONRPCRequestParams.Add(JSONDataValue.IntValue, pdtInteger); + end; + tkInt64: + begin + if JSONDataValue.Typ = jdtInt then + begin + JSONRPCRequestParams.Add(JSONDataValue.IntValue, pdtInteger); + end + else if JSONDataValue.Typ = jdtULong then + begin + JSONRPCRequestParams.Add(JSONDataValue.ULongValue, pdtLongInteger); + end + else if JSONDataValue.Typ = jdtULong then + begin + JSONRPCRequestParams.Add(JSONDataValue.ULongValue, pdtLongInteger); + end + else + begin + raise EMVCJSONRPCInvalidRequest.Create(BuildDeclaration(RTTIParameter)); + end; + + end; + else + raise EMVCJSONRPCInvalidRequest.Create('Invalid parameter type for ' + BuildDeclaration(RTTIParameter)); + end; + + // case JSONDataValue.Typ of + // jdtString: + // begin + // if RTTIParameter.ParamType.TypeKind in [tkString, tkUString, tkUnicodeString, tkAnsiString] then + // begin + // JSONRPCRequestParams.Add(JSONDataValue.Value); + // end + // else if SameText(RTTIParameter.ParamType.Name, 'TDate') then + // begin + // JSONRPCRequestParams.Add(JSONDataValue.UtcDateTimeValue, pdtDate); + // end + // else if SameText(RTTIParameter.ParamType.Name, 'TDateTime') then + // begin + // JSONRPCRequestParams.Add(JSONDataValue.UtcDateTimeValue, pdtDateTime); + // end + // else if SameText(RTTIParameter.ParamType.Name, 'TTime') then + // begin + // JSONRPCRequestParams.Add(JSONDataValue.UtcDateTimeValue, pdtTime); + // end + // else + // raise EMVCJSONRPCInvalidRequest.Create(BuildDeclaration(RTTIParameter)); + // end; + // jdtFloat: + // begin + // if RTTIParameter.ParamType.TypeKind in [tkFloat] then + // begin + // JSONRPCRequestParams.Add(JSONDataValue.FloatValue, pdtFloat); + // end + // else + // raise EMVCJSONRPCInvalidRequest.Create(BuildDeclaration(RTTIParameter)); + // end; + // jdtBool: + // begin + // if (RTTIParameter.ParamType.TypeKind in [tkEnumeration]) and + // (SameText(RTTIParameter.ParamType.QualifiedName, 'System.Boolean')) then + // begin + // JSONRPCRequestParams.Add(JSONDataValue.BoolValue, pdtBoolean); + // end + // else + // raise EMVCJSONRPCInvalidRequest.Create(BuildDeclaration(RTTIParameter)); + // end; + // jdtArray: + // begin + // if (RTTIParameter.ParamType.TypeKind in [tkClass]) and + // (SameText(RTTIParameter.ParamType.Name, TJDOJsonArray.ClassName)) then + // begin + // JSONRPCRequestParams.Add(JSONDataValue.ArrayValue.Clone, pdtJSONArray); + // end + // else + // raise EMVCJSONRPCInvalidRequest.Create(BuildDeclaration(RTTIParameter)); + // end; + // jdtObject: + // begin + // if (RTTIParameter.ParamType.TypeKind in [tkClass]) and + // (SameText(RTTIParameter.ParamType.Name, TJDOJsonObject.ClassName)) then + // begin + // JSONRPCRequestParams.Add(JSONDataValue.ObjectValue.Clone as TJDOJsonObject, pdTJDOJsonObject); + // end + // else + // raise EMVCJSONRPCInvalidRequest.Create(BuildDeclaration(RTTIParameter)); + // end; + // jdtInt: + // begin + // if RTTIParameter.ParamType.TypeKind in [tkInteger] then + // begin + // JSONRPCRequestParams.Add(JSONDataValue.IntValue, pdtInteger); + // end + // else + // raise EMVCJSONRPCInvalidRequest.Create(BuildDeclaration(RTTIParameter)); + // end; + // jdtLong: + // begin + // if RTTIParameter.ParamType.TypeKind in [tkInt64] then + // begin + // JSONRPCRequestParams.Add(JSONDataValue.LongValue, pdtLongInteger); + // end + // else + // raise EMVCJSONRPCInvalidRequest.Create(BuildDeclaration(RTTIParameter)); + // end; + // jdtULong: + // begin + // if RTTIParameter.ParamType.TypeKind in [tkInt64] then + // begin + // JSONRPCRequestParams.Add(JSONDataValue.ULongValue, pdtLongInteger); + // end + // else + // raise EMVCJSONRPCInvalidRequest.Create(BuildDeclaration(RTTIParameter)); + // end; + // else + // raise EMVCJSONRPCInvalidRequest.Create('Invalid parameter type for ' + BuildDeclaration(RTTIParameter)); + // end; +end; + { TMVCJSONRPCMessage } -function TMVCJSONRPCMessage.AsJSON: TJsonObject; +function TMVCJSONRPCMessage.AsJSON: TJDOJsonObject; begin Result := TMVCJSONObject.Create; Result.S[JSONRPC_HEADER] := JSONRPC_VERSION; @@ -412,6 +805,25 @@ begin fOwsRPCInstance := Owns; end; +// procedure TMVCJSONRPCController.CheckInputParametersTypes(aRTTIMethod: TRTTIMethod); +// var +// lParam: TRttiParameter; +// begin +// for lParam in aRTTIMethod.GetParameters do +// begin +// if lParam.ParamType.TypeKind in [tkClass] then +// begin +// if not(SameText(lParam.ParamType.QualifiedName, 'JsonDataObjects.TJDOJsonObject') or +// SameText(lParam.ParamType.QualifiedClassName, 'JsonDataObjects.TJDOJsonArray')) then +// begin +// raise EMVCJSONRPCException.Create('Parameter [' + lParam.Name + ': ' + lParam.ParamType.QualifiedName + +// '] is not allowed as input parameter'); +// end; +// end; +// end; +// +// end; + constructor TMVCJSONRPCController.Create; begin inherited Create; @@ -419,8 +831,8 @@ begin fOwsRPCInstance := False; end; -function TMVCJSONRPCController.CreateError(const RequestID: TValue; const ErrorCode: Integer; const message: string) - : TJsonObject; +function TMVCJSONRPCController.CreateError(const RequestID: TValue; const ErrorCode: Integer; const Message: string) + : TJDOJsonObject; var lErrResp: TJSONRPCResponse; begin @@ -436,43 +848,39 @@ begin end; end; -function TMVCJSONRPCController.CreateRequest(const JSON: TJsonObject): TJSONRPCRequest; +function TMVCJSONRPCController.CreateRequest(const JSON: TJDOJsonObject): IJSONRPCRequest; var - I: Integer; - lParams: TJsonArray; lReqID: TValue; lMethodName: String; begin - try - if JSON.Types[JSONRPC_ID] = jdtString then - lReqID := JSON.S[JSONRPC_ID] - else if JSON.Types[JSONRPC_ID] = jdtInt then - lReqID := JSON.I[JSONRPC_ID] - else if JSON.Types[JSONRPC_ID] = jdtLong then - lReqID := JSON.L[JSONRPC_ID] - else if JSON.Types[JSONRPC_ID] = jdtULong then - lReqID := JSON.U[JSONRPC_ID] - else - lReqID := TValue.Empty; - - lMethodName := JSON.S[JSONRPC_METHOD]; - Result := TJSONRPCRequest.Create(lReqID, lMethodName); + if JSON.Types[JSONRPC_ID] = jdtString then + lReqID := JSON.S[JSONRPC_ID] + else if JSON.Types[JSONRPC_ID] = jdtInt then + lReqID := JSON.I[JSONRPC_ID] + else if JSON.Types[JSONRPC_ID] = jdtLong then + lReqID := JSON.L[JSONRPC_ID] + else if JSON.Types[JSONRPC_ID] = jdtULong then + lReqID := JSON.U[JSONRPC_ID] + else + lReqID := TValue.Empty; + lMethodName := JSON.S[JSONRPC_METHOD]; + Result := TJSONRPCRequest.Create(lReqID, lMethodName); + { if JSON.Types[JSONRPC_PARAMS] = jdtArray then begin - lParams := JSON.A[JSONRPC_PARAMS]; - for I := 0 to lParams.Count - 1 do - begin - Result.Params.Add(JSONDataValueToTValue(lParams[I])); - end; + lParams := JSON.A[JSONRPC_PARAMS]; + for I := 0 to lParams.Count - 1 do + begin + Result.Params.Add(JSONDataValueToTValue(lParams[I])); + end; end else if JSON.Types[JSONRPC_PARAMS] <> jdtNone then begin - raise EMVCJSONRPCException.Create('Params must be a JSON array or null'); + raise EMVCJSONRPCException.Create('Params must be a JSON array or null'); end; - finally - JSON.Free; - end; + + } end; function TMVCJSONRPCController.CreateResponse(const RequestID: TValue; const Value: TValue): TJSONRPCResponse; @@ -492,24 +900,6 @@ begin inherited; end; -procedure TMVCJSONRPCController.InjectParams(lJSONRPCReq: TJSONRPCRequest; lRTTIMethod: TRttiMethod); -var - lRTTIMethodParams: TArray; - lRTTIMethodParam: TRttiParameter; -begin - lRTTIMethodParams := lRTTIMethod.GetParameters; - if (Length(lRTTIMethodParams) <> lJSONRPCReq.Params.Count) then - raise EMVCJSONRPCInvalidParams.CreateFmt('Wrong parameters count. Expected %d got %d.', - [Length(lRTTIMethodParams), lJSONRPCReq.Params.Count]); - for lRTTIMethodParam in lRTTIMethodParams do - begin - if lRTTIMethodParam.Flags * [pfVar, pfOut, pfArray, pfReference] <> [] then - raise EMVCJSONRPCInvalidParams.CreateFmt - ('Parameter modifier not supported for formal parameter [%s]. Only const and value modifiers are allowed.', - [lRTTIMethodParam.Name]); - end; -end; - procedure TMVCJSONRPCController.GetProxyCode; var lLanguage: string; @@ -517,7 +907,7 @@ var lGenerator: TJSONRPCProxyGenerator; lRTTI: TRTTIContext; lRTTIType: TRttiType; - lMethod: TRttiMethod; + lMethod: TRTTIMethod; begin if not Context.Request.QueryStringParamExists('language') then begin @@ -567,25 +957,25 @@ end; procedure TMVCJSONRPCController.Index; var - lJSONRPCReq: TJSONRPCRequest; + lJSONRPCReq: IJSONRPCRequest; lMethod: string; lRTTI: TRTTIContext; lRTTIType: TRttiType; - lRTTIMethod: TRttiMethod; + lRTTIMethod: TRTTIMethod; lRes: TValue; - lJSONRPCResponse: TJSONRPCResponse; + lJSONRPCResponse: IJSONRPCResponse; lParamsToInject: TArray; lReqID: TValue; - lJSON: TJsonObject; + lJSON: TJDOJsonObject; begin lReqID := TValue.Empty; SetLength(lParamsToInject, 0); try lJSON := StringToJSON(Context.Request.Body); - if not Assigned(lJSON) then - raise EMVCJSONRPCParseError.Create; - lJSONRPCReq := CreateRequest(lJSON); try + if not Assigned(lJSON) then + raise EMVCJSONRPCParseError.Create; + lJSONRPCReq := CreateRequest(lJSON); lMethod := lJSONRPCReq.Method; if lJSONRPCReq.RequestType = TJSONRPCRequestType.Request then @@ -621,9 +1011,10 @@ begin ('Cannot call a function using a JSON-RPC notification. [HINT] Use requests for functions and notifications for procedures'); end; - InjectParams(lJSONRPCReq, lRTTIMethod); + lJSONRPCReq.FillParameters(lJSON, lRTTIMethod); try LogD('[JSON-RPC][CALL][' + CALL_TYPE[lRTTIMethod.MethodKind] + '] ' + lRTTIMethod.Name); + // CheckInputParametersTypes(lRTTIMethod); lRes := lRTTIMethod.Invoke(fRPCInstance, lJSONRPCReq.Params.ToArray); except on E: EInvalidCast do @@ -635,19 +1026,15 @@ begin case lJSONRPCReq.RequestType of TJSONRPCRequestType.Notification: begin - if lRes.IsObject then - lRes.AsObject.Free; + // if lRes.IsObject then + // lRes.AsObject.Free; ResponseStatus(HTTP_STATUS.NoContent); end; TJSONRPCRequestType.Request: begin lJSONRPCResponse := CreateResponse(lJSONRPCReq.RequestID, lRes); - try - ResponseStatus(200); - Render(lJSONRPCResponse.AsJSON); - finally - lJSONRPCResponse.Free; - end; + ResponseStatus(200); + Render(lJSONRPCResponse.AsJSON); end; else raise EMVCJSONRPCException.Create('Invalid RequestType'); @@ -663,7 +1050,7 @@ begin lRTTI.Free; end; finally - lJSONRPCReq.Free; + lJSON.Free; end; except on E: EMVCJSONRPCErrorResponse do @@ -692,18 +1079,18 @@ begin -32099 .. -32000: ResponseStatus(500); end; - Render(CreateError(lReqID, E.JSONRPCErrorCode, E.message), True); - LogE(Format('[JSON-RPC][CLS %s][ERR %d][MSG "%s"]', [E.ClassName, E.JSONRPCErrorCode, E.message])); + Render(CreateError(lReqID, E.JSONRPCErrorCode, E.Message), True); + LogE(Format('[JSON-RPC][CLS %s][ERR %d][MSG "%s"]', [E.ClassName, E.JSONRPCErrorCode, E.Message])); end; on E: Exception do begin - Render(CreateError(lReqID, 0, E.message), True); - LogE(Format('[JSON-RPC][CLS %s][MSG "%s"]', [E.ClassName, E.message])); + Render(CreateError(lReqID, 0, E.Message), True); + LogE(Format('[JSON-RPC][CLS %s][MSG "%s"]', [E.ClassName, E.Message])); end; end; end; -function TMVCJSONRPCController.JSONObjectAs(const JSON: TJsonObject): T; +function TMVCJSONRPCController.JSONObjectAs(const JSON: TJDOJsonObject): T; begin Result := T.Create; try @@ -725,9 +1112,16 @@ end; { EMVCJSONRPCInvalidRequest } -constructor EMVCJSONRPCInvalidRequest.Create; +constructor EMVCJSONRPCInvalidRequest.Create(const Message: String); +var + lMsg: string; begin - inherited Create('Invalid Request. The JSON sent is not a valid Request object.'); + lMsg := 'Invalid Request. The JSON sent is not a valid Request object.'; + if not Message.IsEmpty then + begin + lMsg := lMsg + ' [HINT] ' + Message; + end; + inherited Create(lMsg); FJSONRPCErrorCode := -32600; end; @@ -741,7 +1135,7 @@ end; { EMVCJSONRPCInvalidParams } -constructor EMVCJSONRPCInvalidParams.Create(const message: string); +constructor EMVCJSONRPCInvalidParams.Create(const Message: string); begin inherited Create('Invalid params. [hint: ' + message + ']'); FJSONRPCErrorCode := -32602; @@ -757,11 +1151,10 @@ end; { EMVCJSONRPCServerError } -constructor EMVCJSONRPCServerError.Create(const JSONRPCError: Integer; const message: string); +constructor EMVCJSONRPCServerError.Create(const JSONRPCError: Integer; const Message: string); begin inherited Create(message); FJSONRPCErrorCode := JSONRPCError; - end; { TJSONRPCRequest } @@ -796,10 +1189,7 @@ begin Result := TJSONRPCRequestType.Request; end; -procedure TJSONRPCRequest.SetJSON(const JSON: TJsonObject); -var - I: Integer; - lParams: TJsonArray; +procedure TJSONRPCRequest.SetJSON(const JSON: TJDOJsonObject); begin if JSON.Types[JSONRPC_ID] = jdtString then RequestID := JSON.S[JSONRPC_ID] @@ -811,21 +1201,21 @@ begin RequestID := JSON.U[JSONRPC_ID] else RequestID := TValue.Empty; - Method := JSON.S[JSONRPC_METHOD]; Params.Clear; - if JSON.Types[JSONRPC_PARAMS] = jdtArray then - begin - lParams := JSON.A[JSONRPC_PARAMS]; - for I := 0 to lParams.Count - 1 do - begin - Params.Add(JSONDataValueToTValue(lParams[I])); - end; - end - else if JSON.Types[JSONRPC_PARAMS] <> jdtNone then - begin - raise EMVCJSONRPCException.Create('Params must be a JSON array or null'); - end; + // if JSON.Types[JSONRPC_PARAMS] = jdtArray then + // begin + // lParams := JSON.A[JSONRPC_PARAMS]; + // for I := 0 to lParams.Count - 1 do + // begin + // { TODO -oDanieleT -cGeneral : Qui devo sapere cosa si aspetta la classe, altrimenti non posso castare al tipo corretto } + // AddParam(Params,lParams[I]); + // end; + // end + // else if JSON.Types[JSONRPC_PARAMS] <> jdtNone then + // begin + // raise EMVCJSONRPCException.Create('Params must be a JSON array or null'); + // end; end; constructor TJSONRPCNotification.Create(const aMethod: String); @@ -835,19 +1225,55 @@ begin end; destructor TJSONRPCNotification.Destroy; -var - lValue: TValue; begin - for lValue in FParams do - begin - if lValue.IsObject then - lValue.AsObject.Free; - end; FParams.Free; inherited; end; -function TJSONRPCNotification.GetJSON: TJsonObject; +procedure TJSONRPCNotification.FillParameters(const JSON: TJDOJsonObject; const RTTIMethod: TRTTIMethod); +var + lRTTIMethodParams: TArray; + lRTTIMethodParam: TRttiParameter; + lJSONParams: TJDOJsonArray; + I: Integer; +begin + lJSONParams := nil; + Params.Clear; + if JSON.Types[JSONRPC_PARAMS] = jdtArray then + begin + lJSONParams := JSON.A[JSONRPC_PARAMS]; + end + else if JSON.Types[JSONRPC_PARAMS] <> jdtNone then + begin + raise EMVCJSONRPCException.Create('Params must be a JSON array or null'); + end; + + lRTTIMethodParams := RTTIMethod.GetParameters; + if (Length(lRTTIMethodParams) > 0) and (not Assigned(lJSONParams)) then + raise EMVCJSONRPCInvalidParams.CreateFmt('Wrong parameters count. Expected %d got %d.', + [Length(lRTTIMethodParams), 0]); + if Assigned(lJSONParams) and (Length(lRTTIMethodParams) <> lJSONParams.Count) then + raise EMVCJSONRPCInvalidParams.CreateFmt('Wrong parameters count. Expected %d got %d.', + [Length(lRTTIMethodParams), lJSONParams.Count]); + for lRTTIMethodParam in lRTTIMethodParams do + begin + if lRTTIMethodParam.Flags * [pfVar, pfOut, pfArray, pfReference] <> [] then + raise EMVCJSONRPCInvalidParams.CreateFmt + ('Parameter modifier not supported for formal parameter [%s]. Only const and value modifiers are allowed.', + [lRTTIMethodParam.Name]); + end; + + // scroll json params and rttimethod params and find the best match + if Assigned(lJSONParams) then + begin + for I := 0 to lJSONParams.Count - 1 do + begin + JSONDataValueToTValueParam(lJSONParams[I], lRTTIMethodParams[I], Params); + end; + end; +end; + +function TJSONRPCNotification.GetJSON: TJDOJsonObject; var I: Integer; begin @@ -859,7 +1285,7 @@ begin begin for I := 0 to FParams.Count - 1 do begin - AppendTValueToJsonArray(FParams[I], Result.A[JSONRPC_PARAMS]); + AppendTValueToJsonArray(FParams.FParamsValue[I], FParams.FParamsType[I], Result.A[JSONRPC_PARAMS]); end; end; end; @@ -905,7 +1331,7 @@ begin Result := FID; end; -function TJSONRPCResponse.GetJSON: TJsonObject; +function TJSONRPCResponse.GetJSON: TJDOJsonObject; begin Result := inherited; // Must generate something like the following: @@ -956,14 +1382,14 @@ begin Result := Assigned(FError); end; -function TJSONRPCResponse.ResultAsJSONArray: TJsonArray; +function TJSONRPCResponse.ResultAsJSONArray: TJDOJsonArray; begin - Result := Self.Result.AsObject as TJsonArray; + Result := Self.Result.AsObject as TJDOJsonArray; end; -function TJSONRPCResponse.ResultAsJSONObject: TJsonObject; +function TJSONRPCResponse.ResultAsJSONObject: TJDOJsonObject; begin - Result := Self.Result.AsObject as TJsonObject; + Result := Self.Result.AsObject as TJDOJsonObject; end; procedure TJSONRPCResponse.SetError(const Value: TJSONRPCResponseError); @@ -976,7 +1402,7 @@ begin FID := Value; end; -procedure TJSONRPCResponse.SetJSON(const JSON: TJsonObject); +procedure TJSONRPCResponse.SetJSON(const JSON: TJDOJsonObject); begin if JSON.Types[JSONRPC_ID] = jdtString then RequestID := JSON.S[JSONRPC_ID] @@ -1026,15 +1452,15 @@ begin inherited; end; -function TJSONRPCObject.GetJSON: TJsonObject; +function TJSONRPCObject.GetJSON: TJDOJsonObject; begin - Result := TJsonObject.Create; + Result := TJDOJsonObject.Create; Result.S[JSONRPC_HEADER] := JSONRPC_VERSION; end; function TJSONRPCObject.GetJSONString: string; var - lJSON: TJsonObject; + lJSON: TJDOJsonObject; begin lJSON := GetJSON; try @@ -1049,7 +1475,7 @@ begin FID := Value; end; -procedure TJSONRPCObject.SetJSON(const Value: TJsonObject); +procedure TJSONRPCObject.SetJSON(const Value: TJDOJsonObject); begin // not implemented raise Exception.Create('This method must be overwritten by child'); @@ -1057,10 +1483,10 @@ end; procedure TJSONRPCObject.SetJsonString(const Value: string); var - lJSON: TJsonObject; + lJSON: TJDOJsonObject; begin try - lJSON := TJsonObject.Parse(Value) as TJsonObject; + lJSON := TJDOJsonObject.Parse(Value) as TJDOJsonObject; except raise EMVCJSONRPCParseError.Create; end; @@ -1090,7 +1516,7 @@ begin Result := FID; end; -function TJSONRPCRequest.GetJSON: TJsonObject; +function TJSONRPCRequest.GetJSON: TJDOJsonObject; begin Result := inherited GetJSON; if not FID.IsEmpty then @@ -1139,6 +1565,106 @@ begin // do nothing end; +{ TJSONRPCRequestParams } + +procedure TJSONRPCRequestParams.Add(const Value: TJDOJsonArray); +begin + Add(Value, pdtJSONArray); +end; + +procedure TJSONRPCRequestParams.Add(const Value: TJDOJsonObject); +begin + Add(Value, pdTJDOJsonObject); +end; + +procedure TJSONRPCRequestParams.Add(const Value: Integer); +begin + Add(Value, pdtInteger); +end; + +procedure TJSONRPCRequestParams.Add(const Value: String); +begin + Add(Value, pdtString); +end; + +procedure TJSONRPCRequestParams.Add(const Value: Boolean); +begin + Add(Value, pdtBoolean); +end; + +procedure TJSONRPCRequestParams.Add(const Value: Double); +begin + Add(Value, pdtFloat); +end; + +procedure TJSONRPCRequestParams.Add(const Value: TDateTime); +begin + Add(Value, pdtDateTime); +end; + +procedure TJSONRPCRequestParams.Add(const Value: TTime); +begin + Add(Value, pdtTime); +end; + +procedure TJSONRPCRequestParams.Add(const Value: TDate); +begin + Add(Value, pdtDate); +end; + +procedure TJSONRPCRequestParams.Clear; +begin + FParamsValue.Clear; + FParamsType.Clear; +end; + +function TJSONRPCRequestParams.Count: Integer; +begin + Result := FParamsValue.Count; +end; + +constructor TJSONRPCRequestParams.Create; +begin + inherited Create; + FParamsValue := TList.Create; + FParamsType := TList.Create; +end; + +destructor TJSONRPCRequestParams.Destroy; +var + lValue: TValue; +begin + for lValue in FParamsValue do + begin + if lValue.IsObject then + lValue.AsObject.Free; + end; + FParamsValue.Free; + FParamsType.Free; + inherited; +end; + +function TJSONRPCRequestParams.GetItem(const Index: Integer): TValue; +begin + Result := FParamsValue[Index]; +end; + +function TJSONRPCRequestParams.GetItemDataType(const Index: Integer): TJSONRPCParamDataType; +begin + Result := FParamsType[Index]; +end; + +function TJSONRPCRequestParams.ToArray: TArray; +begin + Result := FParamsValue.ToArray; +end; + +procedure TJSONRPCRequestParams.Add(const Value: TValue; const ParamType: TJSONRPCParamDataType); +begin + FParamsValue.Add(Value); + FParamsType.Add(ParamType); +end; + initialization finalization diff --git a/sources/MVCFramework.JWT.pas b/sources/MVCFramework.JWT.pas index 2806e139..e264dea0 100644 --- a/sources/MVCFramework.JWT.pas +++ b/sources/MVCFramework.JWT.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/sources/MVCFramework.Logger.pas b/sources/MVCFramework.Logger.pas index f121b352..391643f3 100644 --- a/sources/MVCFramework.Logger.pas +++ b/sources/MVCFramework.Logger.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/sources/MVCFramework.Middleware.Analytics.pas b/sources/MVCFramework.Middleware.Analytics.pas index c38b0757..4d1bb199 100644 --- a/sources/MVCFramework.Middleware.Analytics.pas +++ b/sources/MVCFramework.Middleware.Analytics.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/sources/MVCFramework.Middleware.Authentication.RoleBasedAuthHandler.pas b/sources/MVCFramework.Middleware.Authentication.RoleBasedAuthHandler.pas index 0b9ef630..d0a0925c 100644 --- a/sources/MVCFramework.Middleware.Authentication.RoleBasedAuthHandler.pas +++ b/sources/MVCFramework.Middleware.Authentication.RoleBasedAuthHandler.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // Contributor of this file: Janidan - https://github.com/janidan // diff --git a/sources/MVCFramework.Middleware.Authentication.pas b/sources/MVCFramework.Middleware.Authentication.pas index 8a810d7e..b0d624a5 100644 --- a/sources/MVCFramework.Middleware.Authentication.pas +++ b/sources/MVCFramework.Middleware.Authentication.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/sources/MVCFramework.Middleware.CORS.pas b/sources/MVCFramework.Middleware.CORS.pas index 1d14afdd..2b65863a 100644 --- a/sources/MVCFramework.Middleware.CORS.pas +++ b/sources/MVCFramework.Middleware.CORS.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // @@ -34,11 +34,39 @@ uses MVCFramework.Commons; type + TMVCCORSDefaults = class sealed + public + const + ALLOWS_ORIGIN_URL = '*'; + ALLOWS_CREDENTIALS = True; + /// + /// The Access-Control-Expose-Headers response header indicates which headers can be exposed as part of the response by listing their names. + /// By default, only the 6 simple response headers are exposed: Cache-Control, Content-Language, Content-Type, Expires, Last-Modified, Pragma + /// If you want clients to be able to access other headers, you have to list them using the Access-Control-Expose-Headers header. + /// Source: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers + /// + EXPOSE_HEADERS = ''; + /// + /// The Access-Control-Allow-Headers response header is used in response to a preflight request which includes the Access-Control-Request-Headers to indicate which HTTP headers can be used during the actual request. + /// The simple headers, Accept, Accept-Language, Content-Language, Content-Type (but only with a MIME type of its parsed value (ignoring parameters) of either application/x-www-form-urlencoded, multipart/form-data, or text/plain), are always available and don't need to be listed by this header. + /// This header is required if the request has an Access-Control-Request-Headers header. + /// Source: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers + /// + ALLOWS_HEADERS = 'Content-Type, Accept, jwtusername, jwtpassword, authentication, authorization'; + /// + /// The Access-Control-Allow-Methods response header specifies the method or methods allowed when accessing the resource in response to a preflight request. + /// Source: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods + /// + ALLOWS_METHODS = 'POST,GET,OPTIONS,PUT,DELETE'; + end; TMVCCORSMiddleware = class(TInterfacedObject, IMVCMiddleware) private FAllowedOriginURL: string; FAllowsCredentials: string; + FAllowsMethods: string; + FExposeHeaders: string; + FAllowsHeaders: string; protected procedure OnBeforeRouting( AContext: TWebContext; @@ -58,7 +86,13 @@ type const AHandled: Boolean ); public - constructor Create(const AAllowedOriginURL: string = '*'; const AAllowsCredentials: Boolean = True); virtual; + constructor Create( + const AAllowedOriginURL: string = TMVCCORSDefaults.ALLOWS_ORIGIN_URL; + const AAllowsCredentials: Boolean = TMVCCORSDefaults.ALLOWS_CREDENTIALS; + const AExposeHeaders: String = TMVCCORSDefaults.EXPOSE_HEADERS; + const AAllowsHeaders: String = TMVCCORSDefaults.ALLOWS_HEADERS; + const AAllowsMethods: string = TMVCCORSDefaults.ALLOWS_METHODS + ); virtual; end; TCORSMiddleware = TMVCCORSMiddleware; @@ -67,11 +101,20 @@ implementation { TMVCCORSMiddleware } -constructor TMVCCORSMiddleware.Create(const AAllowedOriginURL: string; const AAllowsCredentials: Boolean); +constructor TMVCCORSMiddleware.Create( + const AAllowedOriginURL: string; + const AAllowsCredentials: Boolean; + const AExposeHeaders: String; + const AAllowsHeaders: String; + const AAllowsMethods: string + ); begin inherited Create; FAllowedOriginURL := AAllowedOriginURL; FAllowsCredentials := IfThen(AAllowsCredentials, 'true', 'false'); + FExposeHeaders := AExposeHeaders; + FAllowsHeaders := AAllowsHeaders; + FAllowsMethods := AAllowsMethods; end; procedure TMVCCORSMiddleware.OnAfterControllerAction(AContext: TWebContext; @@ -90,10 +133,12 @@ end; procedure TMVCCORSMiddleware.OnBeforeRouting(AContext: TWebContext; var AHandled: Boolean); begin AContext.Response.RawWebResponse.CustomHeaders.Values['Access-Control-Allow-Origin'] := FAllowedOriginURL; - AContext.Response.RawWebResponse.CustomHeaders.Values['Access-Control-Allow-Methods'] := 'POST, GET, OPTIONS, PUT, DELETE'; - AContext.Response.RawWebResponse.CustomHeaders.Values['Access-Control-Allow-Headers'] := 'Content-Type, Accept, jwtusername, jwtpassword, authentication, authorization'; + AContext.Response.RawWebResponse.CustomHeaders.Values['Access-Control-Allow-Methods'] := FAllowsMethods; + AContext.Response.RawWebResponse.CustomHeaders.Values['Access-Control-Allow-Headers'] := FAllowsHeaders; AContext.Response.RawWebResponse.CustomHeaders.Values['Access-Control-Allow-Credentials'] := FAllowsCredentials; - AContext.Response.RawWebResponse.CustomHeaders.Values['Access-Control-Expose-Headers'] := 'authentication, authorization'; + AContext.Response.RawWebResponse.CustomHeaders.Values['Access-Control-Expose-Headers'] := FExposeHeaders; + + // allows preflight requests if (AContext.Request.HTTPMethod = httpOPTIONS) then begin AContext.Response.StatusCode := HTTP_STATUS.OK; diff --git a/sources/MVCFramework.Middleware.Compression.pas b/sources/MVCFramework.Middleware.Compression.pas index e6e0807b..50f65d07 100644 --- a/sources/MVCFramework.Middleware.Compression.pas +++ b/sources/MVCFramework.Middleware.Compression.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/sources/MVCFramework.Middleware.JWT.pas b/sources/MVCFramework.Middleware.JWT.pas index d15a9cfd..56b87f20 100644 --- a/sources/MVCFramework.Middleware.JWT.pas +++ b/sources/MVCFramework.Middleware.JWT.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/sources/MVCFramework.Middleware.SecurityHeaders.pas b/sources/MVCFramework.Middleware.SecurityHeaders.pas index b7085ca3..1f9bf42d 100644 --- a/sources/MVCFramework.Middleware.SecurityHeaders.pas +++ b/sources/MVCFramework.Middleware.SecurityHeaders.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/sources/MVCFramework.MultiMap.pas b/sources/MVCFramework.MultiMap.pas index 7f5c6beb..c6798f16 100644 --- a/sources/MVCFramework.MultiMap.pas +++ b/sources/MVCFramework.MultiMap.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/sources/MVCFramework.Patches.pas b/sources/MVCFramework.Patches.pas index 4482b25a..44b4d256 100644 --- a/sources/MVCFramework.Patches.pas +++ b/sources/MVCFramework.Patches.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/sources/MVCFramework.RESTAdapter.pas b/sources/MVCFramework.RESTAdapter.pas index e2b60792..d90664eb 100644 --- a/sources/MVCFramework.RESTAdapter.pas +++ b/sources/MVCFramework.RESTAdapter.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/sources/MVCFramework.RESTClient.pas b/sources/MVCFramework.RESTClient.pas index 032ccbba..ee7ba0fb 100644 --- a/sources/MVCFramework.RESTClient.pas +++ b/sources/MVCFramework.RESTClient.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/sources/MVCFramework.RQL.AST2FirebirdSQL.pas b/sources/MVCFramework.RQL.AST2FirebirdSQL.pas index 1ca0008a..170ac0d5 100644 --- a/sources/MVCFramework.RQL.AST2FirebirdSQL.pas +++ b/sources/MVCFramework.RQL.AST2FirebirdSQL.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/sources/MVCFramework.RQL.AST2InterbaseSQL.pas b/sources/MVCFramework.RQL.AST2InterbaseSQL.pas index 30baa3e7..766d9964 100644 --- a/sources/MVCFramework.RQL.AST2InterbaseSQL.pas +++ b/sources/MVCFramework.RQL.AST2InterbaseSQL.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/sources/MVCFramework.RQL.AST2MySQL.pas b/sources/MVCFramework.RQL.AST2MySQL.pas index c1621335..2b52f2d2 100644 --- a/sources/MVCFramework.RQL.AST2MySQL.pas +++ b/sources/MVCFramework.RQL.AST2MySQL.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/sources/MVCFramework.RQL.AST2PostgreSQL.pas b/sources/MVCFramework.RQL.AST2PostgreSQL.pas index 5f82a6a0..ae119f0d 100644 --- a/sources/MVCFramework.RQL.AST2PostgreSQL.pas +++ b/sources/MVCFramework.RQL.AST2PostgreSQL.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/sources/MVCFramework.RQL.Parser.pas b/sources/MVCFramework.RQL.Parser.pas index f1a405f5..e955558d 100644 --- a/sources/MVCFramework.RQL.Parser.pas +++ b/sources/MVCFramework.RQL.Parser.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/sources/MVCFramework.Router.pas b/sources/MVCFramework.Router.pas index 5fb6a1ae..e5d79826 100644 --- a/sources/MVCFramework.Router.pas +++ b/sources/MVCFramework.Router.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // @@ -502,3 +502,4 @@ begin end; end. + diff --git a/sources/MVCFramework.Rtti.Utils.pas b/sources/MVCFramework.Rtti.Utils.pas index bd3e34c7..ebd35ab1 100644 --- a/sources/MVCFramework.Rtti.Utils.pas +++ b/sources/MVCFramework.Rtti.Utils.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // @@ -53,12 +53,13 @@ type class function GetPropertyType(AObject: TObject; APropertyName: string): string; class function GetProperty(AObject: TObject; const APropertyName: string): TValue; -// class function GetPropertyAsString(AObject: TObject; const APropertyName: string): string; overload; -// class function GetPropertyAsString(AObject: TObject; AProperty: TRttiProperty): string; overload; + // class function GetPropertyAsString(AObject: TObject; const APropertyName: string): string; overload; + // class function GetPropertyAsString(AObject: TObject; AProperty: TRttiProperty): string; overload; class function ExistsProperty(AObject: TObject; const APropertyName: string; out AProperty: TRttiProperty): Boolean; class procedure SetProperty(AObject: TObject; const APropertyName: string; const AValue: TValue); overload; static; - class function MethodCall(AObject: TObject; AMethodName: string; AParameters: array of TValue; ARaiseExceptionIfNotFound: Boolean = true): TValue; + class function MethodCall(AObject: TObject; AMethodName: string; AParameters: array of TValue; + ARaiseExceptionIfNotFound: Boolean = true): TValue; class procedure ObjectToDataSet(AObject: TObject; AField: TField; var AValue: Variant); class procedure DatasetToObject(ADataset: TDataset; AObject: TObject); @@ -67,8 +68,9 @@ type class procedure CopyObject(ASourceObject, ATargetObject: TObject); static; class procedure CopyObjectAS(ASourceObject, ATargetObject: TObject); static; - class function CreateObject(ARttiType: TRttiType): TObject; overload; static; - class function CreateObject(AQualifiedClassName: string): TObject; overload; static; + class function CreateObject(ARttiType: TRttiType; const AParams: TArray = nil): TObject; overload; static; + class function CreateObject(AQualifiedClassName: string; const AParams: TArray = nil): TObject; + overload; static; class function GetAttribute(const AObject: TRttiObject): T; overload; class function GetAttribute(const AObject: TRttiType): T; overload; @@ -83,7 +85,7 @@ type class function EqualValues(ASource, ADestination: TValue): Boolean; class function FindByProperty(AList: TObjectList; APropertyName: string; APropertyValue: TValue): T; class procedure ForEachProperty(AClazz: TClass; AProc: TProc); -// class function HasStringValueAttribute(ARttiMember: TRttiMember; out AValue: string): Boolean; + // class function HasStringValueAttribute(ARttiMember: TRttiMember; out AValue: string): Boolean; class function BuildClass(AQualifiedName: string; AParams: array of TValue): TObject; class function FindType(AQualifiedName: string): TRttiType; class function GetGUID: TGUID; @@ -193,33 +195,33 @@ begin raise Exception.CreateFmt('Property is not readable [%s.%s]', [ARttiType.ToString, APropertyName]); end; -//class function TRttiUtils.GetPropertyAsString(AObject: TObject; AProperty: TRttiProperty): string; -//var -// P: TValue; -// FT: string; -// CustomFormat: string; -//begin -// if AProperty.IsReadable then -// begin -// P := AProperty.GetValue(AObject); -// FT := GetFieldType(AProperty); -// HasStringValueAttribute(AProperty, CustomFormat); -// Result := TValueAsString(P, FT, CustomFormat); -// end -// else -// Result := ''; -//end; +// class function TRttiUtils.GetPropertyAsString(AObject: TObject; AProperty: TRttiProperty): string; +// var +// P: TValue; +// FT: string; +// CustomFormat: string; +// begin +// if AProperty.IsReadable then +// begin +// P := AProperty.GetValue(AObject); +// FT := GetFieldType(AProperty); +// HasStringValueAttribute(AProperty, CustomFormat); +// Result := TValueAsString(P, FT, CustomFormat); +// end +// else +// Result := ''; +// end; // -//class function TRttiUtils.GetPropertyAsString(AObject: TObject; const APropertyName: string): string; -//var -// Prop: TRttiProperty; -//begin -// Prop := GlContext.GetType(AObject.ClassType).GetProperty(APropertyName); -// if Assigned(Prop) then -// Result := GetPropertyAsString(AObject, Prop) -// else -// Result := ''; -//end; +// class function TRttiUtils.GetPropertyAsString(AObject: TObject; const APropertyName: string): string; +// var +// Prop: TRttiProperty; +// begin +// Prop := GlContext.GetType(AObject.ClassType).GetProperty(APropertyName); +// if Assigned(Prop) then +// Result := GetPropertyAsString(AObject, Prop) +// else +// Result := ''; +// end; class function TRttiUtils.GetPropertyType(AObject: TObject; APropertyName: string): string; begin @@ -270,16 +272,16 @@ begin Result := Assigned(AAttribute); end; -//class function TRttiUtils.HasStringValueAttribute(ARttiMember: TRttiMember; out AValue: string): Boolean; -//var -// Attr: T; // StringValueAttribute; -//begin -// Result := HasAttribute(ARTTIMember, Attr); -// if Result then -// AValue := StringValueAttribute(Attr).Value -// else -// AValue := ''; -//end; +// class function TRttiUtils.HasStringValueAttribute(ARttiMember: TRttiMember; out AValue: string): Boolean; +// var +// Attr: T; // StringValueAttribute; +// begin +// Result := HasAttribute(ARTTIMember, Attr); +// if Result then +// AValue := StringValueAttribute(Attr).Value +// else +// AValue := ''; +// end; class procedure TRttiUtils.SetField(AObject: TObject; const APropertyName: string; const AValue: TValue); var @@ -633,7 +635,6 @@ end; {$IF CompilerVersion >= 24.0} - class procedure TRttiUtils.CopyObjectAS(ASourceObject, ATargetObject: TObject); var _ARttiType: TRttiType; @@ -726,41 +727,63 @@ end; {$ENDIF} - class constructor TRttiUtils.Create; begin GlContext := TRttiContext.Create; end; -class function TRttiUtils.CreateObject(AQualifiedClassName: string): TObject; +class function TRttiUtils.CreateObject(AQualifiedClassName: string; const AParams: TArray = nil): TObject; var rttitype: TRttiType; begin rttitype := GlContext.FindType(AQualifiedClassName); if Assigned(rttitype) then - Result := CreateObject(rttitype) + Result := CreateObject(rttitype, AParams) else raise Exception.Create('Cannot find RTTI for ' + AQualifiedClassName + '. Hint: Is the specified classtype linked in the module?'); end; -class function TRttiUtils.CreateObject(ARttiType: TRttiType): TObject; +class function TRttiUtils.CreateObject(ARttiType: TRttiType; const AParams: TArray = nil): TObject; var Method: TRttiMethod; metaClass: TClass; + lParamsCount: Integer; begin + if AParams = nil then + begin + lParamsCount := 0; + end + else + begin + lParamsCount := Length(AParams); + end; + { First solution, clear and slow } metaClass := nil; Method := nil; for Method in ARttiType.GetMethods do + begin if Method.HasExtendedInfo and Method.IsConstructor then - if Length(Method.GetParameters) = 0 then + begin + if Length(Method.GetParameters) = lParamsCount then begin metaClass := ARttiType.AsInstance.MetaclassType; Break; end; + end; + end; if Assigned(metaClass) then - Result := Method.Invoke(metaClass, []).AsObject + begin + if AParams = nil then + begin + Result := Method.Invoke(metaClass, []).AsObject; + end + else + begin + Result := Method.Invoke(metaClass, AParams).AsObject; + end; + end else raise Exception.Create('Cannot find a propert constructor for ' + ARttiType.ToString); diff --git a/sources/MVCFramework.SQLGenerators.Firebird.pas b/sources/MVCFramework.SQLGenerators.Firebird.pas index 98c137a0..d3ba0b12 100644 --- a/sources/MVCFramework.SQLGenerators.Firebird.pas +++ b/sources/MVCFramework.SQLGenerators.Firebird.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/sources/MVCFramework.SQLGenerators.MySQL.pas b/sources/MVCFramework.SQLGenerators.MySQL.pas index c6a60086..f85bdfa6 100644 --- a/sources/MVCFramework.SQLGenerators.MySQL.pas +++ b/sources/MVCFramework.SQLGenerators.MySQL.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/sources/MVCFramework.Serializer.Abstract.pas b/sources/MVCFramework.Serializer.Abstract.pas index 4ff932a8..fb00f593 100644 --- a/sources/MVCFramework.Serializer.Abstract.pas +++ b/sources/MVCFramework.Serializer.Abstract.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/sources/MVCFramework.Serializer.Commons.pas b/sources/MVCFramework.Serializer.Commons.pas index e48dd670..dae4b229 100644 --- a/sources/MVCFramework.Serializer.Commons.pas +++ b/sources/MVCFramework.Serializer.Commons.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/sources/MVCFramework.Serializer.Intf.pas b/sources/MVCFramework.Serializer.Intf.pas index 71db9544..1ba9d099 100644 --- a/sources/MVCFramework.Serializer.Intf.pas +++ b/sources/MVCFramework.Serializer.Intf.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/sources/MVCFramework.Serializer.JsonDataObjects.CustomTypes.pas b/sources/MVCFramework.Serializer.JsonDataObjects.CustomTypes.pas index f15ec361..9ad45e79 100644 --- a/sources/MVCFramework.Serializer.JsonDataObjects.CustomTypes.pas +++ b/sources/MVCFramework.Serializer.JsonDataObjects.CustomTypes.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // @@ -163,7 +163,7 @@ begin Stream := AElementValue.AsObject as TStream; if Assigned(Stream) then begin - SS := TStringStream.Create('', TEncoding.Unicode); + SS := TStringStream.Create('', TEncoding.Default); try Stream.Position := 0; if TMVCSerializerHelper.AttributeExists(AAttributes) then diff --git a/sources/MVCFramework.Serializer.JsonDataObjects.pas b/sources/MVCFramework.Serializer.JsonDataObjects.pas index 424dcf90..c82a7365 100644 --- a/sources/MVCFramework.Serializer.JsonDataObjects.pas +++ b/sources/MVCFramework.Serializer.JsonDataObjects.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // @@ -49,27 +49,27 @@ uses type TMVCJsonDataObjectsSerializer = class(TMVCAbstractSerializer, IMVCSerializer) public - procedure ObjectToJsonObject(const AObject: TObject; const AJsonObject: TJsonObject; + procedure ObjectToJsonObject(const AObject: TObject; const AJsonObject: TJDOJsonObject; const AType: TMVCSerializationType; const AIgnoredAttributes: TMVCIgnoredList); - procedure ListToJsonArray(const AList: IMVCList; const AJsonArray: TJsonArray; const AType: TMVCSerializationType; - const AIgnoredAttributes: TMVCIgnoredList); - procedure AttributeToJsonDataValue(const AJsonObject: TJsonObject; const AName: string; const AValue: TValue; + procedure ListToJsonArray(const AList: IMVCList; const AJsonArray: TJDOJsonArray; + const AType: TMVCSerializationType; const AIgnoredAttributes: TMVCIgnoredList); + procedure AttributeToJsonDataValue(const AJsonObject: TJDOJsonObject; const AName: string; const AValue: TValue; const AType: TMVCSerializationType; const AIgnored: TMVCIgnoredList; const ACustomAttributes: TArray); - procedure JsonObjectToObject(const AJsonObject: TJsonObject; const AObject: TObject; + procedure JsonObjectToObject(const AJsonObject: TJDOJsonObject; const AObject: TObject; const AType: TMVCSerializationType; const AIgnoredAttributes: TMVCIgnoredList); - procedure JsonDataValueToAttribute(const AJsonObject: TJsonObject; const AName: string; var AValue: TValue; + procedure JsonDataValueToAttribute(const AJsonObject: TJDOJsonObject; const AName: string; var AValue: TValue; const AType: TMVCSerializationType; const AIgnored: TMVCIgnoredList; const ACustomAttributes: TArray); - procedure JsonArrayToList(const AJsonArray: TJsonArray; const AList: IMVCList; const AClazz: TClass; + procedure JsonArrayToList(const AJsonArray: TJDOJsonArray; const AList: IMVCList; const AClazz: TClass; const AType: TMVCSerializationType; const AIgnoredAttributes: TMVCIgnoredList); - procedure DataSetToJsonObject(const ADataSet: TDataSet; const AJsonObject: TJsonObject; + procedure DataSetToJsonObject(const ADataSet: TDataSet; const AJsonObject: TJDOJsonObject; const ANameCase: TMVCNameCase; const AIgnoredFields: TMVCIgnoredList); - procedure DataSetToJsonArray(const ADataSet: TDataSet; const AJsonArray: TJsonArray; const ANameCase: TMVCNameCase; - const AIgnoredFields: TMVCIgnoredList); - procedure JsonObjectToDataSet(const AJsonObject: TJsonObject; const ADataSet: TDataSet; + procedure DataSetToJsonArray(const ADataSet: TDataSet; const AJsonArray: TJDOJsonArray; + const ANameCase: TMVCNameCase; const AIgnoredFields: TMVCIgnoredList); + procedure JsonObjectToDataSet(const AJsonObject: TJDOJsonObject; const ADataSet: TDataSet; const AIgnoredFields: TMVCIgnoredList; const ANameCase: TMVCNameCase); - procedure JsonArrayToDataSet(const AJsonArray: TJsonArray; const ADataSet: TDataSet; + procedure JsonArrayToDataSet(const AJsonArray: TJDOJsonArray; const ADataSet: TDataSet; const AIgnoredFields: TMVCIgnoredList; const ANameCase: TMVCNameCase); { IMVCSerializer } function SerializeObject(const AObject: TObject; const AType: TMVCSerializationType = stDefault; @@ -77,7 +77,7 @@ type const ASerializationAction: TMVCSerializationAction = nil): string; function SerializeObjectToJSON(const AObject: TObject; const AType: TMVCSerializationType; - const AIgnoredAttributes: TMVCIgnoredList; const ASerializationAction: TMVCSerializationAction): TJsonObject; + const AIgnoredAttributes: TMVCIgnoredList; const ASerializationAction: TMVCSerializationAction): TJDOJsonObject; function SerializeCollection(const AList: TObject; const AType: TMVCSerializationType = stDefault; const AIgnoredAttributes: TMVCIgnoredList = []): string; @@ -99,17 +99,17 @@ type procedure DeserializeDataSetRecord(const ASerializedDataSetRecord: string; const ADataSet: TDataSet; const AIgnoredFields: TMVCIgnoredList = []; const ANameCase: TMVCNameCase = ncAsIs); - class function ParseObject(const AString: string): TJsonObject; - class function ParseArray(const AString: string): TJsonArray; + class function ParseObject(const AString: string): TJDOJsonObject; + class function ParseArray(const AString: string): TJDOJsonArray; class function Parse(const AString: string): T; public procedure AfterConstruction; override; end; -procedure TValueToJsonElement(const Value: TValue; const JSON: TJsonObject; const KeyName: string); -procedure AppendTValueToJsonArray(const Value: TValue; const JSONArr: TJsonArray); -function StringToJSON(const aValue: string): TJsonObject; -procedure JsonObjectToObject(const AJsonObject: TJsonObject; const AObject: TObject; const AType: TMVCSerializationType; const AIgnoredAttributes: TMVCIgnoredList); +procedure TValueToJsonElement(const Value: TValue; const JSON: TJDOJsonObject; const KeyName: string); +function StringToJSON(const AValue: string): TJDOJsonObject; +procedure JsonObjectToObject(const AJsonObject: TJDOJsonObject; const AObject: TObject; + const AType: TMVCSerializationType; const AIgnoredAttributes: TMVCIgnoredList); implementation @@ -130,12 +130,12 @@ begin GetTypeSerializers.Add(TypeInfo(TMVCStringDictionary), TMVCStringDictionarySerializer.Create); end; -procedure TMVCJsonDataObjectsSerializer.AttributeToJsonDataValue(const AJsonObject: TJsonObject; const AName: string; +procedure TMVCJsonDataObjectsSerializer.AttributeToJsonDataValue(const AJsonObject: TJDOJsonObject; const AName: string; const AValue: TValue; const AType: TMVCSerializationType; const AIgnored: TMVCIgnoredList; const ACustomAttributes: TArray); var - ChildJsonObject: TJsonObject; - ChildJsonArray: TJsonArray; + ChildJsonObject: TJDOJsonObject; + ChildJsonArray: TJDOJsonArray; ChildValue: TValue; ChildObject, Obj: TObject; ChildList: IMVCList; @@ -285,10 +285,10 @@ begin end; end; -procedure TMVCJsonDataObjectsSerializer.DataSetToJsonArray(const ADataSet: TDataSet; const AJsonArray: TJsonArray; +procedure TMVCJsonDataObjectsSerializer.DataSetToJsonArray(const ADataSet: TDataSet; const AJsonArray: TJDOJsonArray; const ANameCase: TMVCNameCase; const AIgnoredFields: TMVCIgnoredList); var - LJObj: TJsonObject; + LJObj: TJDOJsonObject; begin while not ADataSet.Eof do begin @@ -298,7 +298,7 @@ begin end; end; -procedure TMVCJsonDataObjectsSerializer.DataSetToJsonObject(const ADataSet: TDataSet; const AJsonObject: TJsonObject; +procedure TMVCJsonDataObjectsSerializer.DataSetToJsonObject(const ADataSet: TDataSet; const AJsonObject: TJDOJsonObject; const ANameCase: TMVCNameCase; const AIgnoredFields: TMVCIgnoredList); var I: Integer; @@ -306,8 +306,8 @@ var MS: TMemoryStream; SS: TStringStream; NestedDataSet: TDataSet; - ChildJsonArray: TJsonArray; - ChildJsonObject: TJsonObject; + ChildJsonArray: TJDOJsonArray; + ChildJsonObject: TJDOJsonObject; begin for I := 0 to ADataSet.FieldCount - 1 do begin @@ -418,7 +418,7 @@ end; procedure TMVCJsonDataObjectsSerializer.DeserializeCollection(const ASerializedList: string; const AList: TObject; const AClazz: TClass; const AType: TMVCSerializationType; const AIgnoredAttributes: TMVCIgnoredList); var - JsonArray: TJsonArray; + JsonArray: TJDOJsonArray; ObjList: IMVCList; begin if (ASerializedList = EmptyStr) then @@ -430,7 +430,7 @@ begin ObjList := TDuckTypedList.Wrap(AList); if Assigned(ObjList) then begin - JsonArray := TJsonArray.Parse(ASerializedList) as TJsonArray; + JsonArray := TJDOJsonArray.Parse(ASerializedList) as TJDOJsonArray; try JsonArrayToList(JsonArray, ObjList, AClazz, AType, AIgnoredAttributes); finally @@ -442,7 +442,7 @@ end; procedure TMVCJsonDataObjectsSerializer.DeserializeDataSet(const ASerializedDataSet: string; const ADataSet: TDataSet; const AIgnoredFields: TMVCIgnoredList; const ANameCase: TMVCNameCase); var - lJsonArray: TJsonArray; + lJsonArray: TJDOJsonArray; begin if (ASerializedDataSet = EmptyStr) then raise EMVCException.Create(http_status.BadRequest, 'Invalid body'); @@ -451,7 +451,7 @@ begin Exit; try - lJsonArray := TJsonArray.Parse(ASerializedDataSet) as TJsonArray; + lJsonArray := TJDOJsonArray.Parse(ASerializedDataSet) as TJDOJsonArray; except on E: EJsonParserException do begin @@ -468,13 +468,13 @@ end; procedure TMVCJsonDataObjectsSerializer.DeserializeDataSetRecord(const ASerializedDataSetRecord: string; const ADataSet: TDataSet; const AIgnoredFields: TMVCIgnoredList; const ANameCase: TMVCNameCase); var - lJsonObject: TJsonObject; + lJsonObject: TJDOJsonObject; begin if (ASerializedDataSetRecord = EmptyStr) or (not Assigned(ADataSet)) then Exit; try - lJsonObject := TJsonObject.Parse(ASerializedDataSetRecord) as TJsonObject; + lJsonObject := TJDOJsonObject.Parse(ASerializedDataSetRecord) as TJDOJsonObject; except on E: EJsonParserException do begin @@ -490,7 +490,7 @@ begin end; end; -procedure TMVCJsonDataObjectsSerializer.JsonArrayToDataSet(const AJsonArray: TJsonArray; const ADataSet: TDataSet; +procedure TMVCJsonDataObjectsSerializer.JsonArrayToDataSet(const AJsonArray: TJDOJsonArray; const ADataSet: TDataSet; const AIgnoredFields: TMVCIgnoredList; const ANameCase: TMVCNameCase); var I: Integer; @@ -503,7 +503,7 @@ begin end; end; -procedure TMVCJsonDataObjectsSerializer.JsonArrayToList(const AJsonArray: TJsonArray; const AList: IMVCList; +procedure TMVCJsonDataObjectsSerializer.JsonArrayToList(const AJsonArray: TJDOJsonArray; const AList: IMVCList; const AClazz: TClass; const AType: TMVCSerializationType; const AIgnoredAttributes: TMVCIgnoredList); var I: Integer; @@ -517,7 +517,7 @@ begin end; end; -procedure TMVCJsonDataObjectsSerializer.JsonDataValueToAttribute(const AJsonObject: TJsonObject; const AName: string; +procedure TMVCJsonDataObjectsSerializer.JsonDataValueToAttribute(const AJsonObject: TJDOJsonObject; const AName: string; var AValue: TValue; const AType: TMVCSerializationType; const AIgnored: TMVCIgnoredList; const ACustomAttributes: TArray); var @@ -617,7 +617,7 @@ begin end; end; -procedure TMVCJsonDataObjectsSerializer.JsonObjectToDataSet(const AJsonObject: TJsonObject; const ADataSet: TDataSet; +procedure TMVCJsonDataObjectsSerializer.JsonObjectToDataSet(const AJsonObject: TJDOJsonObject; const ADataSet: TDataSet; const AIgnoredFields: TMVCIgnoredList; const ANameCase: TMVCNameCase); var Field: TField; @@ -731,7 +731,7 @@ begin end; end; -procedure TMVCJsonDataObjectsSerializer.JsonObjectToObject(const AJsonObject: TJsonObject; const AObject: TObject; +procedure TMVCJsonDataObjectsSerializer.JsonObjectToObject(const AJsonObject: TJDOJsonObject; const AObject: TObject; const AType: TMVCSerializationType; const AIgnoredAttributes: TMVCIgnoredList); var ObjType: TRttiType; @@ -796,7 +796,7 @@ begin end; end; -procedure TMVCJsonDataObjectsSerializer.ListToJsonArray(const AList: IMVCList; const AJsonArray: TJsonArray; +procedure TMVCJsonDataObjectsSerializer.ListToJsonArray(const AList: IMVCList; const AJsonArray: TJDOJsonArray; const AType: TMVCSerializationType; const AIgnoredAttributes: TMVCIgnoredList); var I: Integer; @@ -809,7 +809,7 @@ begin end; end; -procedure TMVCJsonDataObjectsSerializer.ObjectToJsonObject(const AObject: TObject; const AJsonObject: TJsonObject; +procedure TMVCJsonDataObjectsSerializer.ObjectToJsonObject(const AObject: TObject; const AJsonObject: TJDOJsonObject; const AType: TMVCSerializationType; const AIgnoredAttributes: TMVCIgnoredList); var ObjType: TRttiType; @@ -847,25 +847,25 @@ end; class function TMVCJsonDataObjectsSerializer.Parse(const AString: string): T; begin - Result := TJsonObject.Parse(AString) as T; + Result := TJDOJsonObject.Parse(AString) as T; if not Assigned(Result) then raise EMVCDeserializationException.Create('Cannot parse string as ' + T.ClassName); end; -class function TMVCJsonDataObjectsSerializer.ParseArray(const AString: string): TJsonArray; +class function TMVCJsonDataObjectsSerializer.ParseArray(const AString: string): TJDOJsonArray; begin - Result := Parse(AString); + Result := Parse(AString); end; -class function TMVCJsonDataObjectsSerializer.ParseObject(const AString: string): TJsonObject; +class function TMVCJsonDataObjectsSerializer.ParseObject(const AString: string): TJDOJsonObject; begin - Result := Parse(AString); + Result := Parse(AString); end; function TMVCJsonDataObjectsSerializer.SerializeCollection(const AList: TObject; const AType: TMVCSerializationType; const AIgnoredAttributes: TMVCIgnoredList): string; var - JsonArray: TJsonArray; + JsonArray: TJDOJsonArray; ObjList: IMVCList; Obj: TObject; begin @@ -880,7 +880,7 @@ begin ObjList := TDuckTypedList.Wrap(AList); if Assigned(ObjList) then begin - JsonArray := TJsonArray.Create; + JsonArray := TJDOJsonArray.Create; try for Obj in ObjList do begin @@ -899,7 +899,7 @@ end; function TMVCJsonDataObjectsSerializer.SerializeDataSet(const ADataSet: TDataSet; const AIgnoredFields: TMVCIgnoredList; const ANameCase: TMVCNameCase): string; var - JsonArray: TJsonArray; + JsonArray: TJDOJsonArray; BookMark: TBookmark; lNameCase: TMVCNameCase; begin @@ -908,7 +908,7 @@ begin if (not Assigned(ADataSet)) or (ADataSet.IsEmpty) then Exit; - JsonArray := TJsonArray.Create; + JsonArray := TJDOJsonArray.Create; try BookMark := ADataSet.BookMark; lNameCase := GetNameCase(ADataSet, ANameCase); @@ -930,14 +930,14 @@ end; function TMVCJsonDataObjectsSerializer.SerializeDataSetRecord(const ADataSet: TDataSet; const AIgnoredFields: TMVCIgnoredList; const ANameCase: TMVCNameCase): string; var - JsonObject: TJsonObject; + JsonObject: TJDOJsonObject; begin Result := EmptyStr; if (not Assigned(ADataSet)) or (ADataSet.IsEmpty) then Exit; - JsonObject := TJsonObject.Create; + JsonObject := TJDOJsonObject.Create; try DataSetToJsonObject(ADataSet, JsonObject, GetNameCase(ADataSet, ANameCase), AIgnoredFields); Result := JsonObject.ToJSON(True); @@ -949,7 +949,7 @@ end; function TMVCJsonDataObjectsSerializer.SerializeObject(const AObject: TObject; const AType: TMVCSerializationType; const AIgnoredAttributes: TMVCIgnoredList; const ASerializationAction: TMVCSerializationAction): string; var - JsonObject: TJsonObject; + JsonObject: TJDOJsonObject; ObjType: TRttiType; begin Result := EmptyStr; @@ -963,8 +963,8 @@ begin if AObject is TDataSet then Exit(self.SerializeDataSet(TDataSet(AObject))); - if AObject is TJsonValue then - Exit(TJsonValue(AObject).ToJSON); + if AObject is System.JSON.TJsonValue then + Exit(System.JSON.TJsonValue(AObject).ToJSON); ObjType := GetRttiContext.GetType(AObject.ClassType); @@ -979,7 +979,7 @@ begin Exit; end; - JsonObject := TJsonObject.Create; + JsonObject := TJDOJsonObject.Create; try ObjectToJsonObject(AObject, JsonObject, GetSerializationType(AObject, AType), AIgnoredAttributes); Result := JsonObject.ToJSON(True); @@ -989,25 +989,25 @@ begin end; function TMVCJsonDataObjectsSerializer.SerializeObjectToJSON(const AObject: TObject; const AType: TMVCSerializationType; - const AIgnoredAttributes: TMVCIgnoredList; const ASerializationAction: TMVCSerializationAction): TJsonObject; + const AIgnoredAttributes: TMVCIgnoredList; const ASerializationAction: TMVCSerializationAction): TJDOJsonObject; var - JsonObject: TJsonObject; + JsonObject: TJDOJsonObject; ObjType: TRttiType; begin if not Assigned(AObject) then - Exit(TJsonObject.Create); + Exit(TJDOJsonObject.Create); if AObject is TJsonBaseObject then - Exit(TJsonBaseObject(AObject).Clone as TJsonObject); + Exit(TJsonBaseObject(AObject).Clone as TJDOJsonObject); if AObject is TDataSet then begin raise Exception.Create('Not supported yet'); - //Exit(self.SerializeDataSet(TDataSet(AObject))); + // Exit(self.SerializeDataSet(TDataSet(AObject))); end; if AObject is TJsonValue then - Exit(TJSONObject.Parse(TJsonValue(AObject).ToJSON) as TJsonObject); + Exit(TJDOJsonObject.Parse(TJsonValue(AObject).ToJSON) as TJDOJsonObject); ObjType := GetRttiContext.GetType(AObject.ClassType); @@ -1023,7 +1023,7 @@ begin Exit; end; - Result := TJsonObject.Create; + Result := TJDOJsonObject.Create; try ObjectToJsonObject(AObject, Result, GetSerializationType(AObject, AType), AIgnoredAttributes); except @@ -1035,7 +1035,7 @@ end; procedure TMVCJsonDataObjectsSerializer.DeserializeObject(const ASerializedObject: string; const AObject: TObject; const AType: TMVCSerializationType; const AIgnoredAttributes: TMVCIgnoredList); var - JsonObject: TJsonObject; + JsonObject: TJDOJsonObject; begin if (ASerializedObject = EmptyStr) then raise EMVCException.Create(http_status.BadRequest, 'Invalid body'); @@ -1044,7 +1044,7 @@ begin Exit; try - JsonObject := TJsonObject.Parse(ASerializedObject) as TJsonObject; + JsonObject := TJDOJsonObject.Parse(ASerializedObject) as TJDOJsonObject; except on E: EJsonParserException do begin @@ -1065,10 +1065,13 @@ begin end; end; -procedure TValueToJsonElement(const Value: TValue; const JSON: TJsonObject; const KeyName: string); +procedure TValueToJsonElement(const Value: TValue; const JSON: TJDOJsonObject; const KeyName: string); var lSer: TMVCJsonDataObjectsSerializer; lMVCList: IMVCList; + lOrdinalValue: Int64; + lValueAsObj: TObject; + lValueAsObjQualifClassName: string; begin case Value.Kind of tkInteger: @@ -1085,7 +1088,7 @@ begin begin JSON.F[KeyName] := Value.AsExtended; end; - end; + end; tkString, tkUString, tkWChar, tkLString, tkWString: begin JSON.S[KeyName] := Value.AsString; @@ -1094,33 +1097,50 @@ begin begin JSON.I[KeyName] := Value.AsInt64; end; + tkEnumeration: + begin + Value.TryAsOrdinal(lOrdinalValue); + JSON.I[KeyName] := lOrdinalValue; + end; tkClass: begin - if Value.AsObject is TJsonObject then + lValueAsObj := Value.AsObject; + lValueAsObjQualifClassName := lValueAsObj.QualifiedClassName.ToLower; + if (lValueAsObj is TJDOJsonObject) or (lValueAsObj is TJsonObject) +{$IFDEF RIOORBETTER} or + { this is for a bug in delphi103rio } (lValueAsObjQualifClassName = 'jsondataobjects.tjsonobject') or + { this is for a bug in delphi103rio } (lValueAsObj.QualifiedClassName = 'jsondataobjects.tjdojsonobject') +{$ENDIF} + then begin - JSON.O[KeyName] := TJsonObject.Create; - JSON.O[KeyName].Assign(TJsonObject(Value.AsObject)); + JSON.O[KeyName] := TJDOJsonObject.Create; + JSON.O[KeyName].Assign(TJDOJsonObject(Value.AsObject)); end - else if Value.AsObject is TJsonArray then + else if (lValueAsObj is TJDOJsonArray) or (lValueAsObj is TJsonArray) +{$IFDEF RIOORBETTER} or + { this is for a bug in delphi103rio } (lValueAsObj.QualifiedClassName = 'jsondataobjects.tjsonarray') or + { this is for a bug in delphi103rio } (lValueAsObj.QualifiedClassName = 'jsondataobjects.tjdojsonarray') +{$ENDIF} + then begin - JSON.A[KeyName] := TJsonArray.Create; - JSON.A[KeyName].Assign(TJsonArray(Value.AsObject)); + JSON.A[KeyName] := TJDOJsonArray.Create; + JSON.A[KeyName].Assign(TJDOJsonArray(Value.AsObject)); end - else if Value.AsObject is TDataSet then + else if lValueAsObj is TDataSet then begin lSer := TMVCJsonDataObjectsSerializer.Create; try - JSON.A[KeyName] := TJsonArray.Create; - lSer.DataSetToJsonArray(TDataSet(Value.AsObject), JSON.A[KeyName], TMVCNameCase.ncLowerCase, []); + JSON.A[KeyName] := TJDOJsonArray.Create; + lSer.DataSetToJsonArray(TDataSet(lValueAsObj), JSON.A[KeyName], TMVCNameCase.ncLowerCase, []); finally lSer.Free; end; end - else if TDuckTypedList.CanBeWrappedAsList(Value.AsObject, lMVCList) then + else if TDuckTypedList.CanBeWrappedAsList(lValueAsObj, lMVCList) then begin lSer := TMVCJsonDataObjectsSerializer.Create; try - JSON.A[KeyName] := TJsonArray.Create; + JSON.A[KeyName] := TJDOJsonArray.Create; lSer.ListToJsonArray(lMVCList, JSON.A[KeyName], TMVCSerializationType.stDefault, nil); finally lSer.Free; @@ -1130,7 +1150,7 @@ begin begin lSer := TMVCJsonDataObjectsSerializer.Create; try - JSON.O[KeyName] := lSer.SerializeObjectToJSON(Value.AsObject, TMVCSerializationType.stProperties, [], nil); + JSON.O[KeyName] := lSer.SerializeObjectToJSON(lValueAsObj, TMVCSerializationType.stProperties, [], nil); finally lSer.Free; end; @@ -1141,77 +1161,13 @@ begin end; end; -procedure AppendTValueToJsonArray(const Value: TValue; const JSONArr: TJsonArray); +function StringToJSON(const AValue: string): TJDOJsonObject; var - lSer: TMVCJsonDataObjectsSerializer; - lJArr: TJsonArray; - lJObj: TJsonObject; -begin - case Value.Kind of - tkInteger: - begin - JSONArr.Add(Value.AsInteger); - end; - tkFloat: - begin - JSONArr.Add(Value.AsExtended); - end; - tkString, tkUString, tkWChar, tkLString, tkWString: - begin - JSONArr.Add(Value.AsString); - end; - tkInt64: - begin - JSONArr.Add(Value.AsInt64); - end; - tkClass: - begin - if Value.AsObject is TJsonObject then - begin - lJObj := TJsonObject.Create; - JSONArr.Add(lJObj); - lJObj.Assign(TJsonObject(Value.AsObject)); - end - else if Value.AsObject is TJsonArray then - begin - lJArr := TJsonArray.Create; - JSONArr.Add(lJArr); - lJArr.Assign(TJsonArray(Value.AsObject)); - end - else if Value.AsObject is TDataSet then - begin - lSer := TMVCJsonDataObjectsSerializer.Create; - try - lJArr := TJsonArray.Create; - JSONArr.Add(lJArr); - lSer.DataSetToJsonArray(TDataSet(Value.AsObject), lJArr, TMVCNameCase.ncLowerCase, []); - finally - lSer.Free; - end - end - else - begin - lSer := TMVCJsonDataObjectsSerializer.Create; - try - lJObj := lSer.SerializeObjectToJSON(Value.AsObject, TMVCSerializationType.stProperties, [], nil); - JSONArr.Add(lJObj); - finally - lSer.Free; - end; - end; - end; - else - raise EMVCException.Create('Invalid type'); - end; -end; - -function StringToJSON(const aValue: string): TJsonObject; -var - lJSON: TJsonObject; + lJSON: TJDOJsonObject; begin lJSON := nil; try - lJSON := TJsonObject.Parse(aValue) as TJsonObject; + lJSON := TJDOJsonObject.Parse(AValue) as TJDOJsonObject; Result := lJSON; except on E: Exception do @@ -1222,11 +1178,12 @@ begin end; end; -procedure JsonObjectToObject(const AJsonObject: TJsonObject; const AObject: TObject; const AType: TMVCSerializationType; const AIgnoredAttributes: TMVCIgnoredList); +procedure JsonObjectToObject(const AJsonObject: TJDOJsonObject; const AObject: TObject; + const AType: TMVCSerializationType; const AIgnoredAttributes: TMVCIgnoredList); var lSer: TMVCJsonDataObjectsSerializer; begin - lSer := TMVCJsonDataObjectsSerializer.Create; + lSer := TMVCJsonDataObjectsSerializer.Create; try lSer.JsonObjectToObject(AJsonObject, AObject, AType, AIgnoredAttributes); finally @@ -1234,5 +1191,5 @@ begin end; end; - end. + diff --git a/sources/MVCFramework.Server.Impl.pas b/sources/MVCFramework.Server.Impl.pas index 66948ec5..b82167af 100644 --- a/sources/MVCFramework.Server.Impl.pas +++ b/sources/MVCFramework.Server.Impl.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/sources/MVCFramework.Server.pas b/sources/MVCFramework.Server.pas index 205c6de0..4af41907 100644 --- a/sources/MVCFramework.Server.pas +++ b/sources/MVCFramework.Server.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/sources/MVCFramework.Session.pas b/sources/MVCFramework.Session.pas index 0988e9c0..86af5e2e 100644 --- a/sources/MVCFramework.Session.pas +++ b/sources/MVCFramework.Session.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/sources/MVCFramework.SysControllers.pas b/sources/MVCFramework.SysControllers.pas index 0cb6de1a..ebd63062 100644 --- a/sources/MVCFramework.SysControllers.pas +++ b/sources/MVCFramework.SysControllers.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/sources/MVCFramework.View.Cache.pas b/sources/MVCFramework.View.Cache.pas index f3cf48e9..c03c744c 100644 --- a/sources/MVCFramework.View.Cache.pas +++ b/sources/MVCFramework.View.Cache.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/sources/MVCFramework.View.Renderers.Mustache.pas b/sources/MVCFramework.View.Renderers.Mustache.pas index 08f86e6c..d84542fa 100644 --- a/sources/MVCFramework.View.Renderers.Mustache.pas +++ b/sources/MVCFramework.View.Renderers.Mustache.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/sources/MVCFramework.pas b/sources/MVCFramework.pas index 864071f2..b2f567f4 100644 --- a/sources/MVCFramework.pas +++ b/sources/MVCFramework.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/sources/dmvcframework.inc b/sources/dmvcframework.inc index b42ab5f6..293fc26d 100644 --- a/sources/dmvcframework.inc +++ b/sources/dmvcframework.inc @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // @@ -64,7 +64,7 @@ DelphiMVCFramework is compatible with Delphi version XE6 or better {$DEFINE TOKYOORBETTER} {$ENDIF} -{$IF CompilerVersion >= 33} // 10.2 Rio +{$IF CompilerVersion >= 33} // 10.3 Rio {$DEFINE RIOORBETTER} {$ENDIF} diff --git a/sources/dmvcframeworkbuildconsts.inc b/sources/dmvcframeworkbuildconsts.inc index 63695af1..cf360456 100644 --- a/sources/dmvcframeworkbuildconsts.inc +++ b/sources/dmvcframeworkbuildconsts.inc @@ -1,2 +1,2 @@ const - DMVCFRAMEWORK_VERSION = '3.1.0 lithium (beta)'; \ No newline at end of file + DMVCFRAMEWORK_VERSION = '3.1.0 (lithium)'; \ No newline at end of file diff --git a/tasks.py b/tasks.py new file mode 100644 index 00000000..83522365 --- /dev/null +++ b/tasks.py @@ -0,0 +1,233 @@ +from invoke import task, context +import os +import subprocess +from colorama import * +import glob +from shutil import copy2, rmtree +from datetime import datetime + +# import markdown +# from markdown.extensions.tables import TableExtension +# from markdown.extensions.toc import TocExtension +# from markdown.extensions.fenced_code import FencedCodeExtension +# from markdown.extensions.codehilite import CodeHiliteExtension + +from pathlib import Path + +init() + +DEFAULT_DELPHI_VERSION = "10.3" + +g_releases_path = "releases" +g_output = "bin" +g_output_folder = "" # defined at runtime +g_version = 'DEV' + + +def get_delphi_projects_to_build(which=''): + projects = [] + if not which or which == 'ide': + projects += glob.glob(r"ideexpert\*.dproj") + if not which or which == 'tests': + projects += glob.glob(r"unittests\**\*.dproj") + if not which or which == 'samples': + projects += glob.glob(r"samples\**\*.dproj") + return projects + + +def build_delphi_project(ctx: context.Context, project_filename, config='DEBUG', delphi_version=DEFAULT_DELPHI_VERSION): + delphi_versions = { + "10.2": "19.0", + "10.3": "20.0" + } + assert delphi_version in delphi_versions, "Invalid Delphi version: " + delphi_version + version_path = delphi_versions[delphi_version] + p = project_filename.replace('.dproj', '.cfg') + if os.path.isfile(p): + if os.path.isfile(p + '.unused'): + os.remove(p + '.unused') + os.rename(p, p + '.unused') + rsvars_path = f'C:\\Program Files (x86)\\Embarcadero\\Studio\\{version_path}\\bin\\rsvars.bat' + if not os.path.isfile(rsvars_path): + rsvars_path = f'D:\\Program Files (x86)\\Embarcadero\\Studio\\{version_path}\\bin\\rsvars.bat' + if not os.path.isfile(rsvars_path): + raise Exception("Cannot find rsvars.bat") + cmdline = '"' + rsvars_path + '"' + " & msbuild /t:Build /p:Config=" + config + " /p:Platform=Win32 \"" + project_filename + "\"" + return ctx.run(cmdline, hide=True, warn=True) + + +def zip_samples(version): + global g_output_folder + cmdline = "7z a " + g_output_folder + f"\\..\\dmvcframework_{version}_samples.zip -r -i@7ziplistfile.txt" + return subprocess.call(cmdline, shell=True) == 0 + +def create_zip(ctx, version): + global g_output_folder + print("CREATING ZIP") + archive_name = r"..\dmvcframework_" + version + ".zip" + switches = "" + files_name = "*" + cmdline = f"..\\..\\7z.exe a {switches} {archive_name} *" + print(cmdline) + with ctx.cd(g_output_folder): + ctx.run(cmdline, hide=False) + #return subprocess.call(cmdline, shell=True) == 0 + + +def copy_sources(): + global g_output_folder + os.makedirs(g_output_folder + "\\sources", exist_ok=True) + os.makedirs(g_output_folder + "\\ideexpert", exist_ok=True) + os.makedirs(g_output_folder + "\\packages", exist_ok=True) + # copying main sources + print("Copying DMVCFramework Sources...") + src = glob.glob("sources\\*.pas") + glob.glob("sources\\*.inc") + for file in src: + print("Copying " + file + " to " + g_output_folder + "\\sources") + copy2(file, g_output_folder + "\\sources\\") + + # copying ideexperts + print("Copying DMVCFramework IDEExpert...") + src = glob.glob("ideexpert\\*.pas") + \ + glob.glob("ideexpert\\*.dfm") + \ + glob.glob("ideexpert\\*.ico") + \ + glob.glob("ideexpert\\*.bmp") + \ + glob.glob("ideexpert\\*.dpk") + \ + glob.glob("ideexpert\\*.dproj") + for file in src: + print("Copying " + file + " to " + g_output_folder + "\\ideexpert") + copy2(file, g_output_folder + "\\ideexpert\\") + + # copying packages + print("Copying DMVCFramework Delphi 10.2 Tokyo packages...") + os.makedirs(g_output_folder + "\\packages\\d102", exist_ok=True) + copy2(r"packages\d102\dmvcframeworkRT.dpk", g_output_folder + "\\packages\\d102") + copy2(r"packages\d102\dmvcframeworkRT.dproj", g_output_folder + "\\packages\\d102") + + print("Copying DMVCFramework Delphi 10.3 Rio packages...") + os.makedirs(g_output_folder + "\\packages\\d103", exist_ok=True) + copy2(r"packages\d103\dmvcframeworkRT.dpk", g_output_folder + "\\packages\\d103") + copy2(r"packages\d103\dmvcframeworkRT.dproj", g_output_folder + "\\packages\\d103") + + + + + +def copy_libs(): + global g_output_folder + + # loggerpro + print("Copying libraries: LoggerPro...") + curr_folder = g_output_folder + "\\lib\\loggerpro" + os.makedirs(curr_folder, exist_ok=True) + src = glob.glob("lib\\loggerpro\\*.pas") + for file in src: + print("Copying " + file + " to " + curr_folder) + copy2(file, curr_folder) + copy2("lib\\loggerpro\\LICENSE", curr_folder) + copy2("lib\\loggerpro\\VERSION.TXT", curr_folder) + + # dmustache + print("Copying libraries: dmustache...") + curr_folder = g_output_folder + "\\lib\\dmustache" + os.makedirs(curr_folder, exist_ok=True) + src = glob.glob("lib\\dmustache\\*.pas") + \ + glob.glob("lib\\dmustache\\*.inc") + for file in src: + print("Copying " + file + " to " + curr_folder) + copy2(file, curr_folder) + copy2("lib\\dmustache\\README.md", curr_folder) + + +def init_build(version): + """Required by all tasks""" + global g_version + global g_output_folder + global g_releases_path + g_version = version + g_output_folder = g_releases_path + "\\" + g_version + print('Output path: ' + g_output_folder) + print("BUILD VERSION: " + g_version) + rmtree(g_output_folder, True) + os.makedirs(g_output_folder, exist_ok=True) + f = open(g_output_folder + "\\version.txt", "w") + f.write("VERSION " + g_version + "\n") + f.write("BUILD DATETIME " + datetime.now().isoformat() + "\n") + f.close() + copy2("README.md", g_output_folder) + copy2("3_0_0_breaking_changes.md", g_output_folder) + copy2("3_1_0_breaking_changes.md", g_output_folder) + copy2("License.txt", g_output_folder) + + +def build_delphi_project_list(ctx, projects, config="DEBUG", filter='', delphi_version=DEFAULT_DELPHI_VERSION): + ret = True + for delphi_project in projects: + if filter and (not filter in delphi_project): + print(f"Skipped {os.path.basename(delphi_project)}") + continue + msg = f"Building: {os.path.basename(delphi_project)} ({config})" + print(Fore.RESET + msg.ljust(70, '.'), end="") + res = build_delphi_project(ctx, delphi_project, 'DEBUG') + if res.ok: + print(Fore.GREEN + 'OK' + Fore.RESET) + else: + ret = False + print(Fore.RED + "\n\nBUILD ERROR") + print(Fore.RESET + res.stdout) + print("\n") + + return ret + + +@task +def tests(ctx, delphi_version=DEFAULT_DELPHI_VERSION): + import os + apppath = os.path.dirname(os.path.realpath(__file__)) + res = True + tests = [ + r"unittests\serializer\jsondataobjects\TestSerializerJsonDataObjects.dproj" + ] + testsexe = [ + r"unittests\serializer\jsondataobjects\Win32\CI\TestSerializerJsonDataObjects.exe" + ] + i = 0 + for test_project in tests: + res = build_delphi_project(ctx, test_project, 'CI', delphi_version) and res + if res: + exename = apppath + "\\" + testsexe[i] + print("Running: " + exename) + res = ctx.run(exename, hide=False) + if not res: + print("UnitTest execution failed!") + return False + i = i + 1 + return res + + +@task(pre=[tests]) +def build(ctx, version="DEBUG", delphi_version=DEFAULT_DELPHI_VERSION, skip_build = False): + """Builds all the binaries and execute integration tests""" + init_build(version) + if not skip_build: + delphi_projects = get_delphi_projects_to_build() + build_delphi_project_list(ctx, delphi_projects, "DEBUG", '', delphi_version) + copy_sources() + copy_libs() + zip_samples(version) + create_zip(ctx, version) + +@task +def build_samples(ctx, version="DEBUG", filter="", delphi_version=DEFAULT_DELPHI_VERSION): + """Builds samples""" + init_build(version) + delphi_projects = get_delphi_projects_to_build('samples') + build_delphi_project_list(ctx, delphi_projects, "DEBUG", filter, delphi_version) + + +@task +def build_ide(ctx, version="DEBUG", delphi_version=DEFAULT_DELPHI_VERSION): + """Builds IDE extensions""" + init_build(version) + delphi_projects = get_delphi_projects_to_build('ide') + build_delphi_project_list(ctx, delphi_projects, "DEBUG", '', delphi_version) diff --git a/tools/entitygenerator/MainFormU.pas b/tools/entitygenerator/MainFormU.pas index cf86b7fb..041b4bff 100644 --- a/tools/entitygenerator/MainFormU.pas +++ b/tools/entitygenerator/MainFormU.pas @@ -241,7 +241,7 @@ begin fIntfBuff.WriteString('//' + sLineBreak); fIntfBuff.WriteString('// Delphi MVC Framework' + sLineBreak); fIntfBuff.WriteString('//' + sLineBreak); - fIntfBuff.WriteString('// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team' + sLineBreak); + fIntfBuff.WriteString('// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team' + sLineBreak); fIntfBuff.WriteString('//' + sLineBreak); fIntfBuff.WriteString('// https://github.com/danieleteti/delphimvcframework' + sLineBreak); fIntfBuff.WriteString('//' + sLineBreak); diff --git a/unittests/common/MVCFramework.Tests.Serializer.Entities.pas b/unittests/common/MVCFramework.Tests.Serializer.Entities.pas index 20482412..2bcb3cd7 100644 --- a/unittests/common/MVCFramework.Tests.Serializer.Entities.pas +++ b/unittests/common/MVCFramework.Tests.Serializer.Entities.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/unittests/common/MVCFramework.Tests.Serializer.EntitiesModule.pas b/unittests/common/MVCFramework.Tests.Serializer.EntitiesModule.pas index 54552123..f00b837b 100644 --- a/unittests/common/MVCFramework.Tests.Serializer.EntitiesModule.pas +++ b/unittests/common/MVCFramework.Tests.Serializer.EntitiesModule.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/unittests/common/MVCFramework.Tests.Serializer.Intf.pas b/unittests/common/MVCFramework.Tests.Serializer.Intf.pas index 2514f28a..63bae9ef 100644 --- a/unittests/common/MVCFramework.Tests.Serializer.Intf.pas +++ b/unittests/common/MVCFramework.Tests.Serializer.Intf.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/unittests/general/Several/DMVCFrameworkTests.dproj b/unittests/general/Several/DMVCFrameworkTests.dproj index a4b15e0a..5361d2aa 100644 --- a/unittests/general/Several/DMVCFrameworkTests.dproj +++ b/unittests/general/Several/DMVCFrameworkTests.dproj @@ -104,6 +104,7 @@ true $(BDS)\bin\default_app.manifest 1033 + 3
    CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= diff --git a/unittests/general/Several/FrameworkTestsU.pas b/unittests/general/Several/FrameworkTestsU.pas index 10730068..64ae6eb8 100644 --- a/unittests/general/Several/FrameworkTestsU.pas +++ b/unittests/general/Several/FrameworkTestsU.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/unittests/general/Several/JSONRPCTestsU.pas b/unittests/general/Several/JSONRPCTestsU.pas index bc99c6f8..f8b06e73 100644 --- a/unittests/general/Several/JSONRPCTestsU.pas +++ b/unittests/general/Several/JSONRPCTestsU.pas @@ -52,38 +52,28 @@ uses MVCFramework.JSONRPC; procedure TTestJSONRPC.TestNotificationWithNoParameters; var - lReq: TJSONRPCRequest; + lReq: IJSONRPCRequest; begin lReq := TJSONRPCRequest.Create; // FromString('{"jsonrpc": "2.0", "method": "subtract"}'); - try - lReq.Method := 'subtract'; - Assert.AreEqual(TJSONRPCRequestType.Notification, lReq.RequestType); - Assert.AreEqual(0, lReq.Params.Count); - Assert.AreEqual('subtract', lReq.Method); - finally - lReq.Free; - end; + lReq.Method := 'subtract'; + Assert.AreEqual(TJSONRPCRequestType.Notification, lReq.RequestType); + Assert.AreEqual(0, lReq.Params.Count); + Assert.AreEqual('subtract', lReq.Method); end; procedure TTestJSONRPC.TestRequestWithArrayParameters; var - lReq: TJSONRPCRequest; + lReq: IJSONRPCRequest; begin lReq := TJSONRPCRequest.Create; - // FromString('{"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1}'); - try - lReq.Method := 'subtract'; - lReq.Params.Add(42); - lReq.Params.Add(23); - lReq.RequestID := 1; - Assert.AreEqual(1, lReq.RequestID.AsInteger); - Assert.AreEqual(42, lReq.Params[0].AsInteger); - Assert.AreEqual(23, lReq.Params[1].AsInteger); - Assert.AreEqual('subtract', lReq.Method); - // Assert.AreNotEqual(tjsonrpc lReq.IsNotification); - finally - lReq.Free; - end; + lReq.Method := 'subtract'; + lReq.Params.Add(42); + lReq.Params.Add(23); + lReq.RequestID := 1; + Assert.AreEqual(1, lReq.RequestID.AsInteger); + Assert.AreEqual(42, lReq.Params[0].AsInteger); + Assert.AreEqual(23, lReq.Params[1].AsInteger); + Assert.AreEqual('subtract', lReq.Method); end; procedure TTestJSONRPC.TestRequestWithMalformedJSON; @@ -91,31 +81,23 @@ begin Assert.WillRaise( procedure var - lReq: TJSONRPCRequest; + lReq: IJSONRPCRequest; begin lReq := TJSONRPCRequest.Create; - try - lReq.AsJSONString := '{"jsonrpc": "2.0", this is wrong}'; - finally - lReq.Free; - end; + lReq.AsJSONString := '{"jsonrpc": "2.0", this is wrong}'; end, EMVCJSONRPCParseError); end; procedure TTestJSONRPC.TestRequestWithNoParameters; var - lReq: TJSONRPCRequest; + lReq: IJSONRPCRequest; begin lReq := TJSONRPCRequest.Create; - try - lReq.AsJSONString := '{"jsonrpc": "2.0", "method": "subtract", "id": 1}'; - Assert.AreEqual(1, lReq.RequestID.AsInteger); - Assert.AreEqual(0, lReq.Params.Count); - Assert.AreEqual('subtract', lReq.Method); - Assert.AreEqual(TJSONRPCRequestType.Request, lReq.RequestType); - finally - lReq.Free; - end; + lReq.AsJSONString := '{"jsonrpc": "2.0", "method": "subtract", "id": 1}'; + Assert.AreEqual(1, lReq.RequestID.AsInteger); + Assert.AreEqual(0, lReq.Params.Count); + Assert.AreEqual('subtract', lReq.Method); + Assert.AreEqual(TJSONRPCRequestType.Request, lReq.RequestType); end; initialization diff --git a/unittests/general/Several/LiveServerTestU.pas b/unittests/general/Several/LiveServerTestU.pas index 2281cfd4..58b60d4c 100644 --- a/unittests/general/Several/LiveServerTestU.pas +++ b/unittests/general/Several/LiveServerTestU.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // @@ -385,7 +385,8 @@ begin procedure(E: Exception) begin end).doPUT('/echo', ['1', '2', '3'], - TSystemJSON.JSONValueToString(System.JSON.TJSONObject.Create(System.JSON.TJSONPair.Create('from client', 'hello world')))); + TSystemJSON.JSONValueToString(System.JSON.TJSONObject.Create(System.JSON.TJSONPair.Create('from client', + 'hello world')))); // wait for thred finish repeat @@ -1288,154 +1289,95 @@ end; procedure TJSONRPCServerTest.TestRequestToNotFoundMethod; var - lReq: TJSONRPCRequest; - lResp: TJSONRPCResponse; + lReq: IJSONRPCRequest; + lResp: IJSONRPCResponse; begin lReq := TJSONRPCRequest.Create; - try - lReq.Method := 'nonexist'; - lReq.RequestID := 1234; - - lResp := FExecutor.ExecuteRequest(lReq); - try - Assert.IsNotNull(lResp.Error); - Assert.areEqual(-32601, lResp.Error.Code); - Assert.isTrue(lResp.Error.ErrMessage.StartsWith('Method "nonexist" not found.')); - finally - lResp.Free; - end; - finally - lReq.Free; - end; + lReq.Method := 'nonexist'; + lReq.RequestID := 1234; + lResp := FExecutor.ExecuteRequest(lReq); + Assert.IsNotNull(lResp.Error); + Assert.areEqual(-32601, lResp.Error.Code); + Assert.isTrue(lResp.Error.ErrMessage.StartsWith('Method "nonexist" not found.')); end; procedure TJSONRPCServerTest.TestRequestWithoutParams; var - lReq: TJSONRPCNotification; + lReq: IJSONRPCNotification; begin lReq := TJSONRPCNotification.Create; - try - lReq.Method := 'mynotify'; - FExecutor.ExecuteNotification(lReq); - Assert.Pass(); - finally - lReq.Free; - end; + lReq.Method := 'mynotify'; + FExecutor.ExecuteNotification(lReq); + Assert.Pass(); end; procedure TJSONRPCServerTest.TestRequestWithParams_I_I_ret_I; var - lReq: TJSONRPCRequest; - lResp: TJSONRPCResponse; + lReq: IJSONRPCRequest; + lResp: IJSONRPCResponse; begin lReq := TJSONRPCRequest.Create; - try - lReq.RequestID := 1234; - lReq.Method := 'subtract'; - lReq.Params.Add(18); - lReq.Params.Add(8); - lResp := FExecutor.ExecuteRequest(lReq); - try - Assert.areEqual(10, lResp.Result.AsInteger); - Assert.areEqual(1234, lResp.RequestID.AsInteger); - finally - lResp.Free; - end; - finally - lReq.Free; - end; + lReq.RequestID := 1234; + lReq.Method := 'subtract'; + lReq.Params.Add(18); + lReq.Params.Add(8); + lResp := FExecutor.ExecuteRequest(lReq); + Assert.areEqual(10, lResp.Result.AsInteger); + Assert.areEqual(1234, lResp.RequestID.AsInteger); end; procedure TJSONRPCServerTest.TestRequestWithParams_I_I_ret_A; var - lReq: TJSONRPCRequest; - lRPCResp: TJSONRPCResponse; + lReq: IJSONRPCRequest; + lRPCResp: IJSONRPCResponse; lArr: TJDOJSONArray; I: Integer; x: Integer; begin lReq := TJSONRPCRequest.Create; - try - lReq.Method := 'GetListFromTo'; - lReq.Params.Add(1); - lReq.Params.Add(5); - lReq.RequestID := 1234; - lRPCResp := FExecutor.ExecuteRequest(lReq); - try - lArr := TJDOJSONArray(lRPCResp.Result.AsObject); - x := 1; - for I := 0 to lArr.Count - 1 do - begin - Assert.areEqual(x, lArr[I].IntValue); - Inc(x); - end; - finally - lRPCResp.Free; - end; - finally - lReq.Free; + lReq.Method := 'GetListFromTo'; + lReq.Params.Add(1); + lReq.Params.Add(5); + lReq.RequestID := 1234; + lRPCResp := FExecutor.ExecuteRequest(lReq); + lArr := TJDOJSONArray(lRPCResp.Result.AsObject); + x := 1; + for I := 0 to lArr.Count - 1 do + begin + Assert.areEqual(x, lArr[I].IntValue); + Inc(x); end; end; procedure TJSONRPCServerTest.TestRequestWithParams_I_I_I_ret_O; var - lReq: TJSONRPCRequest; - lRPCResp: TJSONRPCResponse; + lReq: IJSONRPCRequest; + lRPCResp: IJSONRPCResponse; lS: string; begin lReq := TJSONRPCRequest.Create; - try - // lReq.AsJSONString := '{"jsonrpc": "2.0", "method": "add", "params": [3, 4, 5], "id": 1234}'; - lReq.Method := 'add'; - lReq.Params.Add(3); - lReq.Params.Add(4); - lReq.Params.Add(5); - lReq.RequestID := 1234; - // lResp := RESTClient.ContentType(TMVCMediaType.APPLICATION_JSON).Accept(TMVCMediaType.APPLICATION_JSON) - // .doPOST('/jsonrpc', [], lReq.AsJSONString); - lRPCResp := FExecutor.ExecuteRequest(lReq); - // Assert.IsNotEmpty(lResp.BodyAsString); - // Assert.areEqual(HTTP_STATUS.OK, Integer(lResp.ResponseCode)); - // lRPCResp := TJSONRPCResponse.Create; - try - // lRPCResp.AsJSONString := lResp.BodyAsString; - lS := (lRPCResp.Result.AsObject as TJDOJsonObject).ToJSON(); - Assert.areEqual(12, TJDOJsonObject(lRPCResp.Result.AsObject).I['res']); - finally - lRPCResp.Free; - end; - finally - lReq.Free; - end; + lReq.Method := 'add'; + lReq.Params.Add(3); + lReq.Params.Add(4); + lReq.Params.Add(5); + lReq.RequestID := 1234; + lRPCResp := FExecutor.ExecuteRequest(lReq); + lS := (lRPCResp.Result.AsObject as TJDOJsonObject).ToJSON(); + Assert.areEqual(12, TJDOJsonObject(lRPCResp.Result.AsObject).I['res']); end; procedure TJSONRPCServerTest.TestRequest_S_I_ret_S; var - lReq: TJSONRPCRequest; - lRPCResp: TJSONRPCResponse; + lReq: IJSONRPCRequest; + lRPCResp: IJSONRPCResponse; begin lReq := TJSONRPCRequest.Create; - try - lReq.Method := 'MultiplyString'; - lReq.Params.Add('Daniele'); - lReq.Params.Add(4); - lReq.RequestID := 1234; - lRPCResp := FExecutor.ExecuteRequest(lReq); - // lReq.AsJSONString := '{"jsonrpc": "2.0", "method": "MultiplyString", "params": ["Daniele", 4], "id": 1234}'; - // lResp := RESTClient.ContentType(TMVCMediaType.APPLICATION_JSON).Accept(TMVCMediaType.APPLICATION_JSON) - // .doPOST('/jsonrpc', [], lReq.AsJSONString); - // Assert.IsNotEmpty(lResp.BodyAsString); - // Assert.areEqual(HTTP_STATUS.OK, Integer(lResp.ResponseCode)); - // lRPCResp := TJSONRPCResponse.Create; - try - // lRPCResp.AsJSONString := lResp.BodyAsString; - Assert.areEqual('DanieleDanieleDanieleDaniele', lRPCResp.Result.AsString); - finally - lRPCResp.Free; - end; - finally - lReq.Free; - end; + lReq.Method := 'MultiplyString'; + lReq.Params.Add('Daniele'); + lReq.Params.Add(4); + lReq.RequestID := 1234; + lRPCResp := FExecutor.ExecuteRequest(lReq); + Assert.areEqual('DanieleDanieleDanieleDaniele', lRPCResp.Result.AsString); end; initialization diff --git a/unittests/general/TestServer/TestServerControllerU.pas b/unittests/general/TestServer/TestServerControllerU.pas index d1f794d6..46f4a1d7 100644 --- a/unittests/general/TestServer/TestServerControllerU.pas +++ b/unittests/general/TestServer/TestServerControllerU.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/unittests/serializer/jsondataobjects/MVCFramework.Tests.Serializer.JsonDataObjects.pas b/unittests/serializer/jsondataobjects/MVCFramework.Tests.Serializer.JsonDataObjects.pas index 248e1b0d..f1a51d23 100644 --- a/unittests/serializer/jsondataobjects/MVCFramework.Tests.Serializer.JsonDataObjects.pas +++ b/unittests/serializer/jsondataobjects/MVCFramework.Tests.Serializer.JsonDataObjects.pas @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/unittests/serializer/jsondataobjects/TestSerializerJsonDataObjects.dpr b/unittests/serializer/jsondataobjects/TestSerializerJsonDataObjects.dpr index 60678be9..ec7b78cc 100644 --- a/unittests/serializer/jsondataobjects/TestSerializerJsonDataObjects.dpr +++ b/unittests/serializer/jsondataobjects/TestSerializerJsonDataObjects.dpr @@ -2,7 +2,7 @@ // // Delphi MVC Framework // -// Copyright (c) 2010-2018 Daniele Teti and the DMVCFramework Team +// Copyright (c) 2010-2019 Daniele Teti and the DMVCFramework Team // // https://github.com/danieleteti/delphimvcframework // diff --git a/unittests/serializer/jsondataobjects/TestSerializerJsonDataObjects.dproj b/unittests/serializer/jsondataobjects/TestSerializerJsonDataObjects.dproj index 4c355832..0ed6d3f9 100644 --- a/unittests/serializer/jsondataobjects/TestSerializerJsonDataObjects.dproj +++ b/unittests/serializer/jsondataobjects/TestSerializerJsonDataObjects.dproj @@ -4,7 +4,7 @@ 18.5 None True - Debug + CI Win32 1 Console @@ -34,28 +34,21 @@ true true - - true - Cfg_1 - true - true - - - true - Cfg_3 - true - true - true - - - true + + true Base true + + true + Cfg_4 + true + true + TestSerializerJsonDataObjects System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace) - _CONSOLE_TESTRUNNER;TESTINSIGHT;$(DCC_Define) + _CONSOLE_TESTRUNNER;$(DCC_Define) 1046 .\$(Platform)\$(Config)\dcp $(BDS)\Source\DUnit\src;..\..\..\sources;..\..\..\lib\loggerpro;..\..\..\lib\dmustache;..\..\..\lib\delphistompclient;..\..\..\lib\jsondataobjects\Source;$(DCC_UnitSearchPath) @@ -73,7 +66,7 @@ 1033 Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) DBXSqliteDriver;dxSkinBlueprintRS24;dxPSDBTeeChartRS24;dxPSdxGaugeControlLnkRS24;vclactnband;dxSpreadSheetRS24;vclFireDAC;dxDockingRS24;fs24;tethering;dxSkinVisualStudio2013BlueRS24;dxPScxTLLnkRS24;dxBarExtItemsRS24;FireDACADSDriver;dxFireDACServerModeRS24;dxPSTeeChartRS24;dxSkinOffice2007BlackRS24;FireDACMSSQLDriver;vcltouch;ACBr_NFe;ACBr_NFeDanfeFR;vcldb;Intraweb;svn;dxSkinXmas2008BlueRS24;dxSkinscxSchedulerPainterRS24;Spring.Data;ACBr_NFeDanfeESCPOS;dxSkinsdxBarPainterRS24;dxSkinOffice2010BlackRS24;dxADOServerModeRS24;dxGDIPlusRS24;dxPSdxDBTVLnkRS24;frx24;vclib;dxSkinLilianRS24;FireDACDBXDriver;ACBr_NFSeDanfseFR;dxNavBarRS24;vclx;cxTreeListRS24;dxSkinDevExpressDarkStyleRS24;dxtrmdRS24;ACBr_SATExtratoRL;RESTBackendComponents;dxRibbonRS24;VCLRESTComponents;cxExportRS24;cxPivotGridChartRS24;cxTreeListdxBarPopupMenuRS24;dxSkinOffice2013LightGrayRS24;dxTabbedMDIRS24;vclie;dxSkinVisualStudio2013LightRS24;bindengine;CloudService;remotedb;ACBr_PAF;FireDACMySQLDriver;cxPivotGridOLAPRS24;ACBr_SATECFVirtual;dxSkinSharpRS24;dxSkinBlackRS24;DataSnapClient;dxPSLnksRS24;bindcompdbx;ACBr_CTeDacteRL;dxSkinCoffeeRS24;ACBr_TCP;IndyIPServer;ACBr_CTe;dxSkinsdxRibbonPainterRS24;dxCoreRS24;IndySystem;dxSkinOffice2013DarkGrayRS24;dsnapcon;ACBr_synapse;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;ACBr_SPEDImportar;ACBr_SPED;ACBr_MDFe;emshosting;dxBarDBNavRS24;dxSkinDarkSideRS24;ACBr_BoletoRL;ACBr_LFD;dxSkinOffice2013WhiteRS24;FireDACTDataDriver;dxPSdxLCLnkRS24;FMXTee;dxPScxExtCommonRS24;dxPScxPivotGridLnkRS24;soaprtl;DbxCommonDriver;ACBr_NFSeDanfseRL;xmlrtl;soapmidas;fmxobj;dxSkinMcSkinRS24;rtl;dxLayoutControlRS24;DbxClientDriver;cxGridRS24;dxSkinBlueRS24;dxSpellCheckerRS24;cxLibraryRS24;dxSkinStardustRS24;dxSkinCaramelRS24;ACBr_Convenio115;appanalytics;dxSkinsCoreRS24;dxDBXServerModeRS24;dxMapControlRS24;IndyIPClient;dxSkinHighContrastRS24;bindcompvcl;dxSkinTheAsphaltWorldRS24;frxe24;TeeUI;cxPageControlRS24;cxEditorsRS24;dxPsPrVwAdvRS24;dxSkinSevenClassicRS24;VclSmp;cxSchedulerRibbonStyleEventEditorRS24;FireDACODBCDriver;dxSkinPumpkinRS24;aurelius;dxSkinscxPCPainterRS24;dxPSPrVwRibbonRS24;ACBr_Boleto;ACBr_SEF2;FireDACMongoDBDriver;dxSkinSevenRS24;ACBr_MDFeDamdfeFR;ACBr_NFSe;dxdborRS24;dxmdsRS24;cxSchedulerGridRS24;RESTComponents;dxHttpIndyRequestRS24;cxPivotGridRS24;DBXInterBaseDriver;ACBr_TEFD;emsclientfiredac;DataSnapFireDAC;svnui;dxdbtrRS24;dxSkinMetropolisRS24;dxSkinMoneyTwinsRS24;dxPScxPCProdRS24;ACBr_MDFeDamdfeRL;dxWizardControlRS24;bindcompfmx;dxPSdxOCLnkRS24;dxBarExtDBItemsRS24;dxPSdxFCLnkRS24;inetdb;cxSchedulerTreeBrowserRS24;dxSkinOffice2016ColorfulRS24;ACBr_Diversos;ACBr_TXTComum;ACBr_GNREGuiaFR;FmxTeeUI;emsedge;FireDACIBDriver;fmx;fmxdae;dxSkinSpringTimeRS24;dxSkinValentineRS24;dxSkinLondonLiquidSkyRS24;ACBr_CTeDacteFR;dxSkinWhiteprintRS24;ACBr_Ponto;dbexpress;IndyCore;dxSkiniMaginaryRS24;dxTileControlRS24;dxSkinOffice2016DarkRS24;dsnap;emsclient;DataSnapCommon;cxDataRS24;FireDACCommon;dxSkinOffice2007PinkRS24;dxPSdxSpreadSheetLnkRS24;ACBr_PCNComum;ACBR_DeSTDA;dxSkinDevExpressStyleRS24;soapserver;ACBr_SAT;dxBarRS24;dxSkinMetropolisDarkRS24;FireDACOracleDriver;DBXMySQLDriver;dxPSRichEditControlLnkRS24;dxPScxCommonRS24;ACBr_Sintegra;FireDACCommonODBC;FireDACCommonDriver;ACBr_GNRE;inet;IndyIPCommon;dxSkinVS2010RS24;vcl;ACBr_NFeDanfeRL;dxSkinSharpPlusRS24;ACBr_SATExtratoESCPOS;dxPSdxDBOCLnkRS24;FireDACDb2Driver;dxThemeRS24;dxSkinOffice2007GreenRS24;TeeDB;FireDAC;dxPScxGridLnkRS24;dxPScxVGridLnkRS24;ACBr_Comum;FireDACSqliteDriver;FireDACPgDriver;ibmonitor;FireDACASADriver;dxSkinOffice2010BlueRS24;ACBr_GNREGuiaRL;dxServerModeRS24;ibxpress;Tee;ibxbindings;cxSchedulerRS24;vclwinx;FireDACDSDriver;dxSkinsdxDLPainterRS24;dxPSCoreRS24;dxSkinOffice2007BlueRS24;ACBr_OpenSSL;frxTee24;CustomIPTransport;vcldsnap;dxSkinGlassOceansRS24;dxRibbonCustomizationFormRS24;dxPScxSchedulerLnkRS24;ACBr_DFeComum;dxSkinSummer2008RS24;dxSkinDarkRoomRS24;bindcomp;dxSkinFoggyRS24;ACBr_Serial;dxorgcRS24;dxSkinOffice2010SilverRS24;frce;ACBr_BlocoX;dxRichEditControlRS24;dxSkinsdxNavBarPainterRS24;dbxcds;ACBr_NFCeECFVirtual;adortl;dxSkinSilverRS24;ACBr_BoletoFR;dxSkinVisualStudio2013DarkRS24;dxComnRS24;cxVerticalGridRS24;dxFlowChartRS24;frxDB24;dsnapxml;dbrtl;IndyProtocols;inetdbxpress;dxGaugeControlRS24;dxSkinOffice2007SilverRS24;dxSkinLiquidSkyRS24;fmxase;$(DCC_UsePackage) - CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + (None) Debug @@ -88,19 +81,15 @@ true - (None) 1033 false + TEST_INSIGHT;$(DCC_Define) - - CONSOLE_TESTRUNNER;$(DCC_Define) + 1033 - - - false - RELEASE;$(DCC_Define) - 0 - 0 + false + CONSOLE_TESTRUNNER;CI;$(DCC_Define) + 3 @@ -118,14 +107,10 @@ Base - - Cfg_2 + + Cfg_4 Base - - Cfg_3 - Cfg_1 - Cfg_1 Base @@ -137,10 +122,11 @@ - Embarcadero C++Builder Office 2000 Servers Package - Embarcadero C++Builder Office XP Servers Package - Microsoft Office 2000 Sample Automation Server Wrapper Components - Microsoft Office XP Sample Automation Server Wrapper Components + TurboPack LockBox Delphi FMX designtime package + DBExpress Enterprise Data Explorer Integration + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + FastReport 6.0 Tee Components TestSerializerJsonDataObjects.dpr