mirror of
https://github.com/danieleteti/delphimvcframework.git
synced 2024-11-15 07:45:54 +01:00
Added htmx_template_pro sample
This commit is contained in:
parent
3429f4a825
commit
a452ecd433
311
samples/htmx_templatepro/DAL.pas
Normal file
311
samples/htmx_templatepro/DAL.pas
Normal file
@ -0,0 +1,311 @@
|
||||
unit DAL;
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
System.JSON,
|
||||
MVCFramework.SystemJSONUtils,
|
||||
System.Generics.Collections,
|
||||
MVCFramework.Serializer.Commons;
|
||||
|
||||
type
|
||||
|
||||
[MVCNameCase(ncLowerCase)]
|
||||
TPerson = class
|
||||
private
|
||||
FFirstName: string;
|
||||
FLastName: string;
|
||||
FAge: Integer;
|
||||
FItems: string;
|
||||
FGUID: string;
|
||||
procedure SetFirstName(const Value: string);
|
||||
procedure SetLastName(const Value: string);
|
||||
procedure SetAge(const Value: Integer);
|
||||
procedure SetGUID(const Value: string);
|
||||
procedure SetItems(const Value: string);
|
||||
public
|
||||
[MVCNameAs('first_name')]
|
||||
property FirstName: string read FFirstName write SetFirstName;
|
||||
[MVCNameAs('last_name')]
|
||||
property LastName: string read FLastName write SetLastName;
|
||||
property Age: Integer read FAge write SetAge;
|
||||
property Items: string read FItems write SetItems;
|
||||
property GUID: string read FGUID write SetGUID;
|
||||
end;
|
||||
|
||||
TPeople = class(TObjectList<TPerson>)
|
||||
end;
|
||||
|
||||
{$M+}
|
||||
TMyObj = class
|
||||
private
|
||||
FRawHTML: String;
|
||||
procedure SetRawHTML(const Value: String);
|
||||
published
|
||||
property RawHTML: String read FRawHTML write SetRawHTML;
|
||||
end;
|
||||
{$M-}
|
||||
|
||||
TDevice = class
|
||||
private
|
||||
fDeviceName: string;
|
||||
fSelected: Boolean;
|
||||
public
|
||||
property DeviceName: string read fDeviceName write fDeviceName;
|
||||
property Selected: Boolean read fSelected write fSelected;
|
||||
constructor Create(aDeviceName: string; aSelected: Boolean);
|
||||
end;
|
||||
|
||||
TDeviceList = class(TObjectList<TDevice>)
|
||||
public
|
||||
function Contains(const aDeviceName: string): Boolean;
|
||||
function IndexOf(const aDeviceName: string): Integer;
|
||||
end;
|
||||
|
||||
IPeopleDAL = interface
|
||||
['{3E534A3E-EAEB-44ED-B74E-EFBBAAAE11B4}']
|
||||
function GetPeople(const SearchText: String = ''): TPeople;
|
||||
procedure AddPerson(FirstName, LastName: string; Age: Integer;
|
||||
Items: TArray<string>);
|
||||
procedure DeleteByGUID(GUID: string);
|
||||
function GetPersonByGUID(GUID: string): TPerson;
|
||||
function GetDevicesList: TDeviceList;
|
||||
end;
|
||||
|
||||
TPeopleDAL = class(TInterfacedObject, IPeopleDAL)
|
||||
private const
|
||||
DATAFILE: string = 'people.data';
|
||||
public
|
||||
function GetPeople(const SearchText: String = ''): TPeople;
|
||||
procedure AddPerson(FirstName, LastName: string; Age: Integer;
|
||||
Items: TArray<string>);
|
||||
procedure DeleteByGUID(GUID: string);
|
||||
function GetPersonByGUID(GUID: string): TPerson;
|
||||
function GetDevicesList: TDeviceList;
|
||||
end;
|
||||
|
||||
TServicesFactory = class sealed
|
||||
class function GetPeopleDAL: IPeopleDAL;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
System.SyncObjs,
|
||||
System.IOUtils,
|
||||
MVCFramework.Serializer.Defaults,
|
||||
System.SysUtils;
|
||||
|
||||
var
|
||||
// Hey! The storage is a simple json file, so some synchronization is needed
|
||||
_CS: TCriticalSection = nil;
|
||||
|
||||
{ TSimpleDAL }
|
||||
|
||||
procedure TPeopleDAL.AddPerson(FirstName, LastName: string; Age: Integer;
|
||||
Items: TArray<string>);
|
||||
var
|
||||
lPeople: TPeople;
|
||||
lPerson: TPerson;
|
||||
begin
|
||||
_CS.Enter;
|
||||
try
|
||||
lPeople := GetPeople();
|
||||
try
|
||||
lPerson := TPerson.Create;
|
||||
lPeople.Add(lPerson);
|
||||
lPerson.FirstName := FirstName;
|
||||
lPerson.LastName := LastName;
|
||||
lPerson.Age := Age;
|
||||
lPerson.Items := string.Join(',', Items);
|
||||
lPerson.GUID := TGuid.NewGuid.ToString.Replace('{', '').Replace('}', '')
|
||||
.Replace('-', '');
|
||||
TFile.WriteAllText(DATAFILE, GetDefaultSerializer.SerializeCollection
|
||||
(lPeople));
|
||||
finally
|
||||
lPeople.Free;
|
||||
end;
|
||||
finally
|
||||
_CS.Leave;
|
||||
end;
|
||||
end;
|
||||
|
||||
class function TServicesFactory.GetPeopleDAL: IPeopleDAL;
|
||||
begin
|
||||
Result := TPeopleDAL.Create;
|
||||
end;
|
||||
|
||||
procedure TPeopleDAL.DeleteByGUID(GUID: string);
|
||||
var
|
||||
LJPeople: TPeople;
|
||||
I: Integer;
|
||||
begin
|
||||
_CS.Enter;
|
||||
try
|
||||
LJPeople := GetPeople;
|
||||
try
|
||||
for I := 0 to LJPeople.Count - 1 do
|
||||
begin
|
||||
if LJPeople[I].GUID = GUID then
|
||||
begin
|
||||
LJPeople.Delete(I);
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
TFile.WriteAllText(DATAFILE, GetDefaultSerializer.SerializeCollection
|
||||
(LJPeople));
|
||||
finally
|
||||
LJPeople.Free;
|
||||
end;
|
||||
finally
|
||||
_CS.Leave;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TPeopleDAL.GetDevicesList: TDeviceList;
|
||||
begin
|
||||
Result := TDeviceList.Create(true);
|
||||
Result.Add(TDevice.Create('smartphone', false));
|
||||
Result.Add(TDevice.Create('dumbphone', false));
|
||||
Result.Add(TDevice.Create('laptop', false));
|
||||
Result.Add(TDevice.Create('desktop', false));
|
||||
end;
|
||||
|
||||
function TPeopleDAL.GetPeople(const SearchText: String): TPeople;
|
||||
var
|
||||
LData: string;
|
||||
lSearch: String;
|
||||
begin
|
||||
lSearch := SearchText.ToLower;
|
||||
_CS.Enter;
|
||||
try
|
||||
Result := TPeople.Create;
|
||||
if TFile.Exists(DATAFILE) then
|
||||
LData := TFile.ReadAllText(DATAFILE).Trim;
|
||||
if not LData.IsEmpty then
|
||||
begin
|
||||
GetDefaultSerializer.DeserializeCollection(LData, Result, TPerson);
|
||||
end;
|
||||
if not SearchText.IsEmpty then
|
||||
begin
|
||||
var lToDelete := TPeople.Create(False);
|
||||
try
|
||||
for var I := 0 to Result.Count-1 do
|
||||
begin
|
||||
if not (
|
||||
Result[i].FirstName.ToLower.Contains(lSearch) or
|
||||
Result[i].LastName.ToLower.Contains(lSearch)) then
|
||||
begin
|
||||
lToDelete.Add(Result[i]);
|
||||
end;
|
||||
end;
|
||||
|
||||
for var I := 0 to lToDelete.Count-1 do
|
||||
begin
|
||||
Result.Remove(lToDelete[I]);
|
||||
end;
|
||||
finally
|
||||
lToDelete.Free;
|
||||
end;
|
||||
end;
|
||||
finally
|
||||
_CS.Leave;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TPeopleDAL.GetPersonByGUID(GUID: string): TPerson;
|
||||
var
|
||||
lPeople: TPeople;
|
||||
lPerson: TPerson;
|
||||
begin
|
||||
Result := nil;
|
||||
lPeople := GetPeople;
|
||||
try
|
||||
for lPerson in lPeople do
|
||||
begin
|
||||
if lPerson.GUID = GUID then
|
||||
begin
|
||||
Result := lPeople.Extract(lPerson);
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
if not Assigned(Result) then
|
||||
begin
|
||||
raise Exception.Create('Person not found');
|
||||
end;
|
||||
finally
|
||||
lPeople.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ TPerson }
|
||||
|
||||
procedure TPerson.SetAge(const Value: Integer);
|
||||
begin
|
||||
FAge := Value;
|
||||
end;
|
||||
|
||||
procedure TPerson.SetFirstName(const Value: string);
|
||||
begin
|
||||
FFirstName := Value;
|
||||
end;
|
||||
|
||||
procedure TPerson.SetGUID(const Value: string);
|
||||
begin
|
||||
FGUID := Value;
|
||||
end;
|
||||
|
||||
procedure TPerson.SetItems(const Value: string);
|
||||
begin
|
||||
FItems := Value;
|
||||
end;
|
||||
|
||||
procedure TPerson.SetLastName(const Value: string);
|
||||
begin
|
||||
FLastName := Value;
|
||||
end;
|
||||
|
||||
{ TDevice }
|
||||
|
||||
constructor TDevice.Create(aDeviceName: string; aSelected: Boolean);
|
||||
begin
|
||||
inherited Create;
|
||||
fDeviceName := aDeviceName;
|
||||
fSelected := aSelected;
|
||||
end;
|
||||
|
||||
{ TDeviceList }
|
||||
|
||||
function TDeviceList.Contains(const aDeviceName: string): Boolean;
|
||||
begin
|
||||
Result := IndexOf(aDeviceName) > -1;
|
||||
end;
|
||||
|
||||
function TDeviceList.IndexOf(const aDeviceName: string): Integer;
|
||||
var
|
||||
I: Integer;
|
||||
begin
|
||||
Result := -1;
|
||||
for I := 0 to Self.Count - 1 do
|
||||
begin
|
||||
if SameText(Self[I].DeviceName, aDeviceName) then
|
||||
Exit(I);
|
||||
end;
|
||||
end;
|
||||
|
||||
{ TRawObj }
|
||||
|
||||
procedure TMyObj.SetRawHTML(const Value: String);
|
||||
begin
|
||||
FRawHTML := Value;
|
||||
end;
|
||||
|
||||
initialization
|
||||
|
||||
_CS := TCriticalSection.Create;
|
||||
|
||||
finalization
|
||||
|
||||
_CS.Free;
|
||||
|
||||
end.
|
@ -1,5 +1,4 @@
|
||||
object WebModule1: TWebModule1
|
||||
OldCreateOrder = False
|
||||
OnCreate = WebModuleCreate
|
||||
OnDestroy = WebModuleDestroy
|
||||
Actions = <
|
77
samples/htmx_templatepro/WebModuleU.pas
Normal file
77
samples/htmx_templatepro/WebModuleU.pas
Normal file
@ -0,0 +1,77 @@
|
||||
unit WebModuleU;
|
||||
|
||||
interface
|
||||
|
||||
uses System.SysUtils, System.Classes, Web.HTTPApp, MVCFramework;
|
||||
|
||||
type
|
||||
TWebModule1 = class(TWebModule)
|
||||
procedure WebModuleCreate(Sender: TObject);
|
||||
procedure WebModuleDestroy(Sender: TObject);
|
||||
private
|
||||
FMVCEngine: TMVCEngine;
|
||||
{ Private declarations }
|
||||
public
|
||||
{ Public declarations }
|
||||
end;
|
||||
|
||||
var
|
||||
WebModuleClass: TComponentClass = TWebModule1;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
MVCFramework.View.Renderers.TemplatePro,
|
||||
WebSiteControllerU,
|
||||
System.IOUtils,
|
||||
MVCFramework.Commons,
|
||||
MVCFramework.Middleware.Redirect,
|
||||
MVCFramework.Middleware.StaticFiles;
|
||||
|
||||
{ %CLASSGROUP 'Vcl.Controls.TControl' }
|
||||
|
||||
{$R *.dfm}
|
||||
|
||||
|
||||
procedure TWebModule1.WebModuleCreate(Sender: TObject);
|
||||
begin
|
||||
FMVCEngine := TMVCEngine.Create(Self,
|
||||
procedure(Config: TMVCConfig)
|
||||
begin
|
||||
Config.dotEnv := dotEnv;
|
||||
// session timeout (0 means session cookie)
|
||||
Config[TMVCConfigKey.SessionTimeout] := dotEnv.Env('dmvc.session_timeout', '0');
|
||||
//default content-type
|
||||
Config[TMVCConfigKey.DefaultContentType] := dotEnv.Env('dmvc.default.content_type', TMVCMediaType.TEXT_HTML);
|
||||
//default content charset
|
||||
Config[TMVCConfigKey.DefaultContentCharset] := dotEnv.Env('dmvc.default.content_charset', TMVCConstants.DEFAULT_CONTENT_CHARSET);
|
||||
//unhandled actions are permitted?
|
||||
Config[TMVCConfigKey.AllowUnhandledAction] := dotEnv.Env('dmvc.allow_unhandled_actions', 'false');
|
||||
//enables or not system controllers loading (available only from localhost requests)
|
||||
Config[TMVCConfigKey.LoadSystemControllers] := dotEnv.Env('dmvc.load_system_controllers', 'true');
|
||||
//default view file extension
|
||||
Config[TMVCConfigKey.DefaultViewFileExtension] := dotEnv.Env('dmvc.default.view_file_extension', 'html');
|
||||
//view path
|
||||
Config[TMVCConfigKey.ViewPath] := dotEnv.Env('dmvc.view_path', 'templates');
|
||||
//use cache for server side views (use "false" in debug and "true" in production for faster performances
|
||||
Config[TMVCConfigKey.ViewCache] := dotEnv.Env('dmvc.view_cache', 'false');
|
||||
//Max Record Count for automatic Entities CRUD
|
||||
Config[TMVCConfigKey.MaxEntitiesRecordCount] := dotEnv.Env('dmvc.max_entities_record_count', IntToStr(TMVCConstants.MAX_RECORD_COUNT));
|
||||
//Enable Server Signature in response
|
||||
Config[TMVCConfigKey.ExposeServerSignature] := dotEnv.Env('dmvc.expose_server_signature', 'false');
|
||||
//Enable X-Powered-By Header in response
|
||||
Config[TMVCConfigKey.ExposeXPoweredBy] := dotEnv.Env('dmvc.expose_x_powered_by', 'true');
|
||||
// Max request size in bytes
|
||||
Config[TMVCConfigKey.MaxRequestSize] := dotEnv.Env('dmvc.max_request_size', IntToStr(TMVCConstants.DEFAULT_MAX_REQUEST_SIZE));
|
||||
end)
|
||||
.AddController(TWebSiteController)
|
||||
.AddMiddleware(TMVCRedirectMiddleware.Create(['/'],'/people'))
|
||||
.SetViewEngine(TMVCTemplateProViewEngine)
|
||||
end;
|
||||
|
||||
procedure TWebModule1.WebModuleDestroy(Sender: TObject);
|
||||
begin
|
||||
FMVCEngine.Free;
|
||||
end;
|
||||
|
||||
end.
|
209
samples/htmx_templatepro/WebSiteControllerU.pas
Normal file
209
samples/htmx_templatepro/WebSiteControllerU.pas
Normal file
@ -0,0 +1,209 @@
|
||||
unit WebSiteControllerU;
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
MVCFramework, System.Diagnostics, JsonDataObjects, MVCFramework.Commons, MVCFramework.HTMX;
|
||||
|
||||
type
|
||||
|
||||
[MVCPath('/people')]
|
||||
TWebSiteController = class(TMVCController)
|
||||
protected
|
||||
function GeneratePeopleListAsCSV: String;
|
||||
public
|
||||
[MVCPath]
|
||||
[MVCHTTPMethods([httpGET])]
|
||||
function PeopleSearch(const [MVCFromQueryString('q', '')] SearchText: String): String;
|
||||
|
||||
[MVCPath('/exports/csv')]
|
||||
[MVCHTTPMethods([httpGET])]
|
||||
function ExportPeopleListAsCSV_API: String;
|
||||
|
||||
[MVCPath]
|
||||
[MVCHTTPMethods([httpPOST])]
|
||||
[MVCConsumes(TMVCMediaType.APPLICATION_FORM_URLENCODED)]
|
||||
procedure SavePerson(
|
||||
const [MVCFromContentField('first_name')] FirstName: String;
|
||||
const [MVCFromContentField('last_name')] LastName: String;
|
||||
const [MVCFromContentField('age', 0)] Age: Integer;
|
||||
const [MVCFromContentField('items')] Devices: TArray<String>
|
||||
);
|
||||
|
||||
[MVCPath('/delete/($guid)')]
|
||||
[MVCHTTPMethods([httpDELETE])]
|
||||
procedure DeletePerson(const guid: string);
|
||||
|
||||
[MVCPath('/new')]
|
||||
[MVCHTTPMethods([httpGET])]
|
||||
[MVCProduces(TMVCMediaType.TEXT_HTML)]
|
||||
function NewPerson: String;
|
||||
|
||||
[MVCPath('/modal/fordelete/($guid)')]
|
||||
[MVCHTTPMethods([httpGET])]
|
||||
[MVCProduces(TMVCMediaType.TEXT_HTML)]
|
||||
function ShowModalForDelete(guid: string): String;
|
||||
|
||||
|
||||
[MVCPath('/edit/($guid)')]
|
||||
[MVCHTTPMethods([httpGET])]
|
||||
[MVCProduces(TMVCMediaType.TEXT_HTML)]
|
||||
function EditPerson(guid: string): String;
|
||||
|
||||
[MVCPath]
|
||||
[MVCHTTPMethods([httpGET])]
|
||||
[MVCProduces(TMVCMediaType.TEXT_HTML)]
|
||||
procedure Index;
|
||||
|
||||
[MVCPath('/modal')]
|
||||
[MVCHTTPMethods([httpGET])]
|
||||
[MVCProduces(TMVCMediaType.TEXT_HTML)]
|
||||
function ShowModal: String;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TWebSiteController }
|
||||
|
||||
uses DAL, System.SysUtils, Web.HTTPApp;
|
||||
|
||||
procedure TWebSiteController.DeletePerson(const guid: string);
|
||||
var
|
||||
LDAL: IPeopleDAL;
|
||||
begin
|
||||
LDAL := TServicesFactory.GetPeopleDAL;
|
||||
LDAL.DeleteByGUID(GUID);
|
||||
Context.Response.HXSetLocation('/people');
|
||||
RenderStatusMessage(HTTP_STATUS.OK);
|
||||
end;
|
||||
|
||||
function TWebSiteController.EditPerson(guid: string): String;
|
||||
var
|
||||
LDAL: IPeopleDAL;
|
||||
lPerson: TPerson;
|
||||
lDevices: TDeviceList;
|
||||
lItem: TDevice;
|
||||
begin
|
||||
LDAL := TServicesFactory.GetPeopleDAL;
|
||||
lPerson := LDAL.GetPersonByGUID(guid);
|
||||
try
|
||||
lDevices := LDAL.GetDevicesList;
|
||||
try
|
||||
ViewData['person'] := lPerson;
|
||||
for lItem in lDevices do
|
||||
begin
|
||||
lItem.Selected := lPerson.Items.Contains(lItem.DeviceName);
|
||||
end;
|
||||
ViewData['deviceslist'] := lDevices;
|
||||
Result := Page(['editperson']);
|
||||
finally
|
||||
lDevices.Free;
|
||||
end;
|
||||
finally
|
||||
lPerson.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TWebSiteController.ExportPeopleListAsCSV_API: String;
|
||||
begin
|
||||
ContentType := TMVCMediaType.TEXT_CSV;
|
||||
Context.Response.CustomHeaders.Values['Content-Disposition'] := 'attachment; filename=people.csv';
|
||||
Result := GeneratePeopleListAsCSV;
|
||||
end;
|
||||
|
||||
function TWebSiteController.GeneratePeopleListAsCSV: String;
|
||||
var
|
||||
LDAL: IPeopleDAL;
|
||||
lPeople: TPeople;
|
||||
begin
|
||||
LDAL := TServicesFactory.GetPeopleDAL;
|
||||
lPeople := LDAL.GetPeople;
|
||||
try
|
||||
ViewData['people'] := lPeople;
|
||||
Result := Page(['people_list.csv']);
|
||||
finally
|
||||
lPeople.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TWebSiteController.Index;
|
||||
begin
|
||||
Redirect('/people');
|
||||
end;
|
||||
|
||||
function TWebSiteController.NewPerson: String;
|
||||
var
|
||||
LDAL: IPeopleDAL;
|
||||
lDevices: TDeviceList;
|
||||
begin
|
||||
LDAL := TServicesFactory.GetPeopleDAL;
|
||||
lDevices := LDAL.GetDevicesList;
|
||||
try
|
||||
ViewData['deviceslist'] := lDevices;
|
||||
ViewData['ishtmx'] := Context.Request.IsHTMX;
|
||||
Result := Page(['editperson']);
|
||||
finally
|
||||
lDevices.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TWebSiteController.PeopleSearch(const SearchText: String): String;
|
||||
var
|
||||
LDAL: IPeopleDAL;
|
||||
lPeople: TPeople;
|
||||
begin
|
||||
LDAL := TServicesFactory.GetPeopleDAL;
|
||||
|
||||
lPeople := LDAL.GetPeople(SearchText);
|
||||
try
|
||||
ViewData['people'] := lPeople;
|
||||
ViewData['ishtmx'] := Context.Request.IsHTMX;
|
||||
if Context.Request.IsHTMX then
|
||||
begin
|
||||
if SearchText.IsEmpty then
|
||||
Context.Response.HXSetPushUrl('/people')
|
||||
else
|
||||
Context.Response.HXSetPushUrl('/people?q=' + SearchText);
|
||||
end;
|
||||
ViewData['q'] := SearchText;
|
||||
Result := PageFragment(['people_list']);
|
||||
finally
|
||||
lPeople.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TWebSiteController.SavePerson(
|
||||
const FirstName: String;
|
||||
const LastName: String;
|
||||
const Age: Integer;
|
||||
const Devices: TArray<String>);
|
||||
var
|
||||
LPeopleDAL: IPeopleDAL;
|
||||
begin
|
||||
if FirstName.IsEmpty or LastName.IsEmpty or (Age <= 0) then
|
||||
begin
|
||||
{ TODO -oDaniele -cGeneral : Show how to properly render an exception }
|
||||
raise EMVCException.Create('Invalid data', 'First name, last name and age are not optional', 0);
|
||||
end;
|
||||
|
||||
LPeopleDAL := TServicesFactory.GetPeopleDAL;
|
||||
LPeopleDAL.AddPerson(FirstName, LastName, Age, Devices);
|
||||
Context.Response.HXSetRedirect('/people');
|
||||
end;
|
||||
|
||||
function TWebSiteController.ShowModal: String;
|
||||
begin
|
||||
ViewData['message'] := 'Do you really want to delete row?';
|
||||
ViewData['title'] := 'Bootstrap Modal Dialog';
|
||||
Result := Page(['modal']);
|
||||
end;
|
||||
|
||||
function TWebSiteController.ShowModalForDelete(guid: string): String;
|
||||
begin
|
||||
ViewData['title'] := 'Bootstrap Modal Dialog';
|
||||
ViewData['message'] := 'Do you really want to delete row?';
|
||||
ViewData['guid'] := guid;
|
||||
Result := Page(['modal']);
|
||||
end;
|
||||
|
||||
end.
|
1
samples/htmx_templatepro/bin/people.data
Normal file
1
samples/htmx_templatepro/bin/people.data
Normal file
@ -0,0 +1 @@
|
||||
[{"first_name":"Bruce","last_name":"Banner","age":56,"items":"smartphone,dumbphone","guid":"2290EE213DFB4855894A3FC91FE52C17"},{"first_name":"Reed","last_name":"Richards","age":45,"items":"laptop,smartphone","guid":"298CE047B4C24D67B29710BF4ABE290C"},{"first_name":"Scott","last_name":"Summers","age":54,"items":"desktop","guid":"3DACB879E83749EDA68389EBA2286A13"},{"first_name":"Daniele","last_name":"Teti","age":40,"items":"dumbphone,laptop","guid":"C2C002A595694C7CBD3CA1F3123F0EEB"},{"first_name":"Bruce","last_name":"Banner","age":56,"items":"desktop","guid":"97D40FD94580407FA60B9A09EAE2DA8E"},{"first_name":"Bruce","last_name":"Banner","age":56,"items":"smartphone,dumbphone,laptop","guid":"F7EAC68F3709415985C699653950C85B"}]
|
@ -0,0 +1,8 @@
|
||||
<a
|
||||
hx-get="/people/modal/fordelete/{{:people.guid}}"
|
||||
href=""
|
||||
hx-target="#modals-here"
|
||||
hx-trigger="click"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#modals-here"
|
||||
>Delete</a>
|
78
samples/htmx_templatepro/bin/templates/editperson.html
Normal file
78
samples/htmx_templatepro/bin/templates/editperson.html
Normal file
@ -0,0 +1,78 @@
|
||||
{{if(!ishtmx)}}
|
||||
{{include("partials/header.html")}}
|
||||
{{endif}}
|
||||
<form class="form form-horizontal" id="myForm" name="myForm" hx-post="/people">
|
||||
<input type="hidden" value="{{:person.guid}}" name="guid">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
{{if(!person)}}
|
||||
<h3>New Person</h3>
|
||||
{{else}}
|
||||
<h3>Edit Person</h3>
|
||||
{{endif}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="form-group">
|
||||
<label for="first_name" class="control-label">First name</label>
|
||||
<input type="text" value="{{:person.firstname}}" class="form-control" id="first_name" placeholder="First name" name="first_name" autocomplete="first_name" >
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="form-group">
|
||||
<label for="last_name" class="control-label">Last name</label>
|
||||
<input type="text" value="{{:person.lastname}}" class="form-control" id="last_name" placeholder="Last name" name="last_name">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="form-group">
|
||||
<label for="age" class="control-label">Age</label>
|
||||
<input type="number" value="{{:person.age}}" class="form-control" id="age" placeholder="Age" name="age">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<div class="form-group">
|
||||
<label for="items" class="col-sm-10 control-label">Devices</label>
|
||||
<select id="items" name="items" multiple class="form-control">
|
||||
{{loop(deviceslist)}}
|
||||
<option value="{{:deviceslist.devicename}}" {{if(deviceslist.selected)}}selected{{endif}}>{{:deviceslist.devicename}}</option>
|
||||
{{endloop}}
|
||||
</select>
|
||||
<span style="font-size: 80%">(Ctrl+Click to select multiple devices)</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" style="padding-top:2rem">
|
||||
<div class="col">
|
||||
<button type="button" class="btn btn-secondary btn-block w-100" onclick="history.back()">Return to the list</button>
|
||||
</div>
|
||||
<div class="col offset-4">
|
||||
</div>
|
||||
<div class="col">
|
||||
<button type="submit" class="btn btn-primary btn-block w-100">Save</button>
|
||||
</div>
|
||||
<div class="col">
|
||||
{{if(person)}}
|
||||
<button
|
||||
type="button"
|
||||
hx-confirm="Are you sure you wish to delete user?"
|
||||
hx-delete="/people/delete/{{:person.guid}}"
|
||||
class="btn btn-danger btn-block w-100">
|
||||
Delete
|
||||
</button>
|
||||
{{endif}}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</form>
|
||||
{{if(!ishtmx)}}
|
||||
{{include("partials/footer.html")}}
|
||||
{{endif}}
|
14
samples/htmx_templatepro/bin/templates/modal.html
Normal file
14
samples/htmx_templatepro/bin/templates/modal.html
Normal file
@ -0,0 +1,14 @@
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">{{:title}}</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>{{:message}}</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" hx-delete="/people/delete/{{:guid}}">Yes, delete it!</button>
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">No</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
17
samples/htmx_templatepro/bin/templates/partials/footer.html
Normal file
17
samples/htmx_templatepro/bin/templates/partials/footer.html
Normal file
@ -0,0 +1,17 @@
|
||||
<div class="row_fluid">
|
||||
<div class="col-sm">
|
||||
<div style="height: 100px"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm bg-primary">
|
||||
<span style="color: white">Powered by DMVCFramework</span>
|
||||
</div>
|
||||
<div class="col-sm bg-warning" style="text-align: right">
|
||||
<span><a target="_blank" href="https://github.com/danieleteti/templatepro">Template Pro</a> project site</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
19
samples/htmx_templatepro/bin/templates/partials/header.html
Normal file
19
samples/htmx_templatepro/bin/templates/partials/header.html
Normal file
@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<header>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
|
||||
<script src="https://unpkg.com/htmx.org@2.0.0"></script>
|
||||
|
||||
<style>
|
||||
h1 a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
</header>
|
||||
|
||||
<body hx-boost="true" class="text-capitalize">
|
||||
<div id="main" class="container">
|
||||
<div class="shadow-sm p-3 mb-5 bg-body-tertiary rounded">
|
||||
<h1>✅<a href="https://htmx.org/" target="_blank">HTMX</a> Sample</h1>
|
||||
</div>
|
@ -0,0 +1,9 @@
|
||||
<div id="modals-here"
|
||||
class="modal modal-blur fade"
|
||||
style="display: none"
|
||||
aria-hidden="false"
|
||||
tabindex="-1">
|
||||
<div class="modal-dialog modal-lg modal-dialog-centered" role="document">
|
||||
<div class="modal-content"></div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1 @@
|
||||
{{first_name}}, {{last_name}}
|
@ -0,0 +1,4 @@
|
||||
guid;first_name;last_name;age
|
||||
{{loop(people)}}
|
||||
{{:people.guid}};"{{:people.firstname}}";"{{:people.lastname}}";{{:people.age}}
|
||||
{{endloop}}
|
13
samples/htmx_templatepro/bin/templates/people_list.html
Normal file
13
samples/htmx_templatepro/bin/templates/people_list.html
Normal file
@ -0,0 +1,13 @@
|
||||
{{if(!ishtmx)}}
|
||||
{{include("partials/header.html")}}
|
||||
{{include("people_list_search.html")}}
|
||||
{{endif}}
|
||||
<div class="row" id="people_list">
|
||||
<div class="col">
|
||||
{{include("people_table.html")}}
|
||||
</div>
|
||||
</div>
|
||||
{{if(!ishtmx)}}
|
||||
{{include("people_list_bottom.html")}}
|
||||
{{include("partials/footer.html")}}
|
||||
{{endif}}
|
@ -0,0 +1,10 @@
|
||||
<div class="row padding mx-auto">
|
||||
<div class="col-2 text-left">
|
||||
<a href="/people/exports/csv" class="btn btn-dark" download="people.csv" target="_blank">Export as CSV</a>
|
||||
</div>
|
||||
<div class="offset-8 col-2 text-right">
|
||||
<button class="btn btn-primary" hx-on:click="window.location.href = '/people/new'">Add New Person</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{include("partials/modal_placeholder.html")}}
|
@ -0,0 +1,13 @@
|
||||
<div class="row_fluid">
|
||||
<div class="offset-sm-6 col-sm-6 text-right">
|
||||
<form hx-get="/people" hx-target="#people_list">
|
||||
<div class="input-group mb-3">
|
||||
<input name="q" type="text" class="form-control" placeholder="Contains..." value="{{:q}}">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-outline-secondary" type="submit">Search</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
28
samples/htmx_templatepro/bin/templates/people_table.html
Normal file
28
samples/htmx_templatepro/bin/templates/people_table.html
Normal file
@ -0,0 +1,28 @@
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">First name</th>
|
||||
<th scope="col">Last name</th>
|
||||
<th scope="col">Age</th>
|
||||
<th scope="col"> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{if(!people)}}
|
||||
<tr>
|
||||
<td class="text-center" colspan="5"><<No People Found>></td>
|
||||
</tr>
|
||||
{{else}}
|
||||
{{loop(people)}}
|
||||
<tr>
|
||||
<td>{{:people.@@index}}</td>
|
||||
<td>{{:people.FirstName}}</td>
|
||||
<td>{{:people.LastName}}</td>
|
||||
<td>{{:people.Age}}</td>
|
||||
<td class="text-right">{{include("delete_person_link.html")}} | {{include("view_person_link.html")}}</td>
|
||||
</tr>
|
||||
{{endloop}}
|
||||
{{endif}}
|
||||
</tbody>
|
||||
</table>
|
85
samples/htmx_templatepro/bin/templates/showcase.html
Normal file
85
samples/htmx_templatepro/bin/templates/showcase.html
Normal file
@ -0,0 +1,85 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<header>
|
||||
<style>
|
||||
body {
|
||||
font-family: Consolas, 'Courier New';
|
||||
}
|
||||
|
||||
blockquote {
|
||||
font-style: italic;
|
||||
color: #a0a0a0;
|
||||
padding: 0.2em;
|
||||
}
|
||||
|
||||
.section {
|
||||
background-color: #3a3a3a;
|
||||
color: white;
|
||||
border-left: 0.5em red solid;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.box {
|
||||
border: thin black solid;
|
||||
margin: auto;
|
||||
width: 80%;
|
||||
padding: 2em;
|
||||
}
|
||||
</style>
|
||||
</header>
|
||||
|
||||
<body>
|
||||
<h1>Mustache Template Showcase <small><a href="/people">return to the app</a></small></h1>
|
||||
<p>
|
||||
This page is a showcase for all the mustache features usable from DMVCFramework Server Side Views using the default Mustache engine.
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<h2 class="section">List of objects</h2>
|
||||
<div>
|
||||
{{^people}}
|
||||
<div>No People Found</div>
|
||||
{{/people}}
|
||||
{{#people}}
|
||||
<div>{{-index}}. {{first_name}} {{last_name}}</div>
|
||||
{{/people}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 class="section">Handle empty list of objects</h2>
|
||||
<div>
|
||||
{{^people2}}
|
||||
<div>No People Found</div>
|
||||
{{/people2}}
|
||||
{{#people2}}
|
||||
<div>{{-index}}. {{first_name}} {{last_name}}</div>
|
||||
{{/people2}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 class="section">Avoid HTML automatic escaping using {{=<% %>=}} {{{ content }}</h2>
|
||||
<%={{ }}=%>
|
||||
<div class="box">
|
||||
{{#myobj}}
|
||||
{{{rawhtml}}
|
||||
{{/myobj}}
|
||||
<br>
|
||||
<blockquote >Check source code to know how to escape curly braces</blockquote >
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 class="section">Handling partials | <small>This partial is located in <code>partials/partial_person.mustache</code></small></h2>
|
||||
<div>
|
||||
<ul>
|
||||
{{#people}}
|
||||
<li>{{>partials/partial_person}}</li>
|
||||
{{/people}}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1 @@
|
||||
<a href="/people/edit/{{:people.guid}}">View</a>
|
75
samples/htmx_templatepro/htmx_templatepro.dpr
Normal file
75
samples/htmx_templatepro/htmx_templatepro.dpr
Normal file
@ -0,0 +1,75 @@
|
||||
program htmx_templatepro;
|
||||
|
||||
{$APPTYPE CONSOLE}
|
||||
|
||||
|
||||
uses
|
||||
System.SysUtils,
|
||||
MVCFramework,
|
||||
MVCFramework.Signal,
|
||||
MVCFramework.Logger,
|
||||
MVCFramework.Console,
|
||||
{$IFDEF MSWINDOWS}
|
||||
Winapi.ShellAPI,
|
||||
Winapi.Windows,
|
||||
{$ENDIF }
|
||||
IdHTTPWebBrokerBridge,
|
||||
Web.WebReq,
|
||||
Web.WebBroker,
|
||||
WebModuleU in 'WebModuleU.pas' {WebModule1: TWebModule},
|
||||
WebSiteControllerU in 'WebSiteControllerU.pas',
|
||||
DAL in 'DAL.pas',
|
||||
MyDataModuleU in '..\renders\MyDataModuleU.pas' {MyDataModule: TDataModule},
|
||||
MVCFramework.Commons,
|
||||
MVCFramework.DotEnv;
|
||||
|
||||
{$R *.res}
|
||||
|
||||
|
||||
procedure RunServer(APort: Integer);
|
||||
var
|
||||
LServer: TIdHTTPWebBrokerBridge;
|
||||
begin
|
||||
ReportMemoryLeaksOnShutdown := True;
|
||||
LogI('HTMX DMVCFramework Sample');
|
||||
LogI(Format('Starting HTTP Server on port %d', [APort]));
|
||||
ResetConsole;
|
||||
LServer := TIdHTTPWebBrokerBridge.Create(nil);
|
||||
try
|
||||
LServer.DefaultPort := APort;
|
||||
LServer.Active := True;
|
||||
{$IFDEF MSWINDOWS}
|
||||
//ShellExecute(0, 'open', 'http://localhost:8080', nil, nil, SW_SHOW);
|
||||
{$ENDIF}
|
||||
LogI('HTMX DMVCFramework Sample');
|
||||
LogI('Ctrl+C to stop the server');
|
||||
WaitForTerminationSignal;
|
||||
EnterInShutdownState;
|
||||
ResetConsole;
|
||||
LServer.Active := False;
|
||||
finally
|
||||
LServer.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
begin
|
||||
{ Enable ReportMemoryLeaksOnShutdown during debug }
|
||||
// ReportMemoryLeaksOnShutdown := True;
|
||||
IsMultiThread := True;
|
||||
UseConsoleLogger := True;
|
||||
// DMVCFramework Specific Configuration
|
||||
// When MVCSerializeNulls = True empty nullables and nil are serialized as json null.
|
||||
// When MVCSerializeNulls = False empty nullables and nil are not serialized at all.
|
||||
MVCSerializeNulls := True;
|
||||
|
||||
try
|
||||
if WebRequestHandler <> nil then
|
||||
WebRequestHandler.WebModuleClass := WebModuleClass;
|
||||
WebRequestHandlerProc.MaxConnections := dotEnv.Env('dmvc.handler.max_connections', 1024);
|
||||
RunServer(dotEnv.Env('dmvc.server.port', 8080));
|
||||
except
|
||||
on E: Exception do
|
||||
LogE(E.ClassName + ': ' + E.Message);
|
||||
end;
|
||||
|
||||
end.
|
@ -1,15 +1,15 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{26F03627-A96B-4F16-8E35-D0D1890F6748}</ProjectGuid>
|
||||
<ProjectGuid>{563921D8-AB80-4E14-AD4E-36870C7E2008}</ProjectGuid>
|
||||
<ProjectVersion>20.1</ProjectVersion>
|
||||
<FrameworkType>VCL</FrameworkType>
|
||||
<MainSource>ServerSideViewsCustomEngine.dpr</MainSource>
|
||||
<MainSource>htmx_templatepro.dpr</MainSource>
|
||||
<Base>True</Base>
|
||||
<Config Condition="'$(Config)'==''">Debug</Config>
|
||||
<Platform Condition="'$(Platform)'==''">Win32</Platform>
|
||||
<TargetedPlatforms>1</TargetedPlatforms>
|
||||
<AppType>Console</AppType>
|
||||
<ProjectName Condition="'$(ProjectName)'==''">ServerSideViewsCustomEngine</ProjectName>
|
||||
<ProjectName Condition="'$(ProjectName)'==''">htmx_templatepro</ProjectName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''">
|
||||
<Base>true</Base>
|
||||
@ -44,7 +44,7 @@
|
||||
<Icns_MainIcns>$(BDS)\bin\delphi_PROJECTICNS.icns</Icns_MainIcns>
|
||||
<Icon_MainIcon>$(BDS)\bin\delphi_PROJECTICON.ico</Icon_MainIcon>
|
||||
<DCC_Namespace>System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace)</DCC_Namespace>
|
||||
<SanitizedProjectName>ServerSideViewsCustomEngine</SanitizedProjectName>
|
||||
<SanitizedProjectName>htmx_templatepro</SanitizedProjectName>
|
||||
<DCC_DcuOutput>.\$(Platform)\$(Config)</DCC_DcuOutput>
|
||||
<DCC_ExeOutput>.\$(Platform)\$(Config)</DCC_ExeOutput>
|
||||
<DCC_E>false</DCC_E>
|
||||
@ -57,7 +57,7 @@
|
||||
<DCC_Namespace>Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)</DCC_Namespace>
|
||||
<VerInfo_Locale>1033</VerInfo_Locale>
|
||||
<DCC_ExeOutput>.\bin</DCC_ExeOutput>
|
||||
<VerInfo_Keys>CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName)</VerInfo_Keys>
|
||||
<VerInfo_Keys>CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(ModuleName);FileDescription=$(ModuleName);ProductName=$(ModuleName)</VerInfo_Keys>
|
||||
<Manifest_File>None</Manifest_File>
|
||||
<DCC_UsePackage>DBXSqliteDriver;RESTComponents;DataSnapServerMidas;DBXDb2Driver;DBXInterBaseDriver;vclactnband;frxe23;vclFireDAC;emsclientfiredac;DataSnapFireDAC;svnui;tethering;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;svn;Intraweb;DBXOracleDriver;ipstudiowinwordxp;inetdb;FmxTeeUI;FireDACIBDriver;fmx;fmxdae;DelphiCookbookListViewAppearance;vclib;FireDACDBXDriver;dbexpress;IndyCore;vclx;dsnap;DataSnapCommon;emsclient;FireDACCommon;bdertl;RESTBackendComponents;DataSnapConnectors;VCLRESTComponents;soapserver;frxTee23;JclDeveloperTools;vclie;CPortLibDXE;bindengine;DBXMySQLDriver;FireDACOracleDriver;CloudService;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonDriver;DataSnapClient;inet;bindcompdbx;IndyIPCommon;vcl;DBXSybaseASEDriver;IndyIPServer;IndySystem;FireDACDb2Driver;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;TeeDB;FireDAC;Jcl;FireDACSqliteDriver;FireDACPgDriver;ibmonitor;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;FMXTee;ipstudiowinclient;soaprtl;DbxCommonDriver;ibxpress;Tee;DataSnapServer;xmlrtl;soapmidas;DataSnapNativeClient;fmxobj;vclwinx;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;DTKANPRPackage;DBXSybaseASADriver;CustomIPTransport;vcldsnap;CodeSiteExpressPkg;SampleListViewMultiDetailAppearancePackage;bindcomp;appanalytics;ipstudiowin;DBXInformixDriver;officeXPrt;IndyIPClient;bindcompvcl;frxDB23;vcldbx;TeeUI;vclribbon;dbxcds;VclSmp;adortl;FireDACODBCDriver;JclVcl;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;inetdbxpress;FireDACMongoDBDriver;IndyProtocols;frx23;JclContainers;fmxase;$(DCC_UsePackage)</DCC_UsePackage>
|
||||
</PropertyGroup>
|
||||
@ -76,6 +76,7 @@
|
||||
<VerInfo_Locale>1033</VerInfo_Locale>
|
||||
<DCC_RemoteDebug>false</DCC_RemoteDebug>
|
||||
<VerInfo_Keys>CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName)</VerInfo_Keys>
|
||||
<AppDPIAwarenessMode>none</AppDPIAwarenessMode>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Cfg_2)'!=''">
|
||||
<DCC_LocalDebugSymbols>false</DCC_LocalDebugSymbols>
|
||||
@ -92,15 +93,11 @@
|
||||
<DesignClass>TWebModule</DesignClass>
|
||||
</DCCReference>
|
||||
<DCCReference Include="WebSiteControllerU.pas"/>
|
||||
<DCCReference Include="MVCFramework.View.Renderers.TemplatePro.pas"/>
|
||||
<DCCReference Include="lib\TemplateProU.pas"/>
|
||||
<DCCReference Include="DAL.pas"/>
|
||||
<DCCReference Include="..\renders\MyDataModuleU.pas">
|
||||
<Form>MyDataModule</Form>
|
||||
<FormType>dfm</FormType>
|
||||
<DesignClass>TDataModule</DesignClass>
|
||||
</DCCReference>
|
||||
<DCCReference Include="..\..\sources\MVCFramework.Cache.pas"/>
|
||||
<DCCReference Include="..\..\sources\MVCFramework.Serializer.HTML.pas"/>
|
||||
<BuildConfiguration Include="Base">
|
||||
<Key>Base</Key>
|
||||
</BuildConfiguration>
|
||||
@ -119,15 +116,23 @@
|
||||
<BorlandProject>
|
||||
<Delphi.Personality>
|
||||
<Source>
|
||||
<Source Name="MainSource">ServerSideViewsCustomEngine.dpr</Source>
|
||||
<Source Name="MainSource">htmx_templatepro.dpr</Source>
|
||||
</Source>
|
||||
<Excluded_Packages>
|
||||
<Excluded_Packages Name="$(BDSBIN)\dcloffice2k250.bpl">Microsoft Office 2000 Sample Automation Server Wrapper Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="$(BDSBIN)\dclofficexp250.bpl">Microsoft Office XP Sample Automation Server Wrapper Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="$(BDSBIN)\dcloffice2k290.bpl">Microsoft Office 2000 Sample Automation Server Wrapper Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="$(BDSBIN)\dclofficexp290.bpl">Microsoft Office XP Sample Automation Server Wrapper Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="$(BDSBIN)\bcboffice2k290.bpl">Embarcadero C++Builder Office 2000 Servers Package</Excluded_Packages>
|
||||
<Excluded_Packages Name="$(BDSBIN)\bcbofficexp290.bpl">Embarcadero C++Builder Office XP Servers Package</Excluded_Packages>
|
||||
</Excluded_Packages>
|
||||
</Delphi.Personality>
|
||||
<Deployment Version="4">
|
||||
<DeployFile LocalName="bin\ServerSideViewsCustomEngine.exe" Configuration="Debug" Class="ProjectOutput"/>
|
||||
<DeployFile LocalName="bin\ServerSideViews.exe" Configuration="Debug" Class="ProjectOutput"/>
|
||||
<DeployFile LocalName="bin\htmx_templatepro.exe" Configuration="Debug" Class="ProjectOutput">
|
||||
<Platform Name="Win32">
|
||||
<RemoteName>htmx_templatepro.exe</RemoteName>
|
||||
<Overwrite>true</Overwrite>
|
||||
</Platform>
|
||||
</DeployFile>
|
||||
<DeployClass Name="AdditionalDebugSymbols">
|
||||
<Platform Name="OSX32">
|
||||
<Operation>1</Operation>
|
@ -15,10 +15,10 @@ type
|
||||
public
|
||||
[MVCPath('/public')]
|
||||
[MVCHTTPMethod([httpGET])]
|
||||
procedure PublicSection(ctx: TWebContext);
|
||||
procedure PublicSection;
|
||||
[MVCPath('/')]
|
||||
[MVCHTTPMethod([httpGET])]
|
||||
procedure Index(ctx: TWebContext);
|
||||
procedure Index;
|
||||
end;
|
||||
|
||||
[MVCPath('/admin')]
|
||||
@ -30,7 +30,7 @@ type
|
||||
[MVCPath('/role1')]
|
||||
[MVCProduces('text/html')]
|
||||
[MVCHTTPMethod([httpGET])]
|
||||
procedure OnlyRole1(ctx: TWebContext);
|
||||
procedure OnlyRole1;
|
||||
[MVCPath('/role1')]
|
||||
[MVCProduces('application/json')]
|
||||
[MVCHTTPMethod([httpGET])]
|
||||
@ -38,7 +38,7 @@ type
|
||||
[MVCPath('/role2')]
|
||||
[MVCProduces('text/html')]
|
||||
[MVCHTTPMethod([httpGET])]
|
||||
procedure OnlyRole2(ctx: TWebContext);
|
||||
procedure OnlyRole2;
|
||||
end;
|
||||
|
||||
implementation
|
||||
@ -48,12 +48,12 @@ uses
|
||||
|
||||
{ TApp1MainController }
|
||||
|
||||
procedure TApp1MainController.Index(ctx: TWebContext);
|
||||
procedure TApp1MainController.Index;
|
||||
begin
|
||||
Redirect('/index.html');
|
||||
end;
|
||||
|
||||
procedure TApp1MainController.PublicSection(ctx: TWebContext);
|
||||
procedure TApp1MainController.PublicSection;
|
||||
begin
|
||||
Render('This is a public section');
|
||||
end;
|
||||
@ -69,12 +69,12 @@ begin
|
||||
AHandled := False;
|
||||
end;
|
||||
|
||||
procedure TAdminController.OnlyRole1(ctx: TWebContext);
|
||||
procedure TAdminController.OnlyRole1;
|
||||
var
|
||||
lPair: TPair<String, String>;
|
||||
begin
|
||||
ContentType := TMVCMediaType.TEXT_PLAIN;
|
||||
ResponseStream.AppendLine('Hey! Hello ' + ctx.LoggedUser.UserName +
|
||||
ResponseStream.AppendLine('Hey! Hello ' + Context.LoggedUser.UserName +
|
||||
', now you are a logged user and this is a protected content!');
|
||||
ResponseStream.AppendLine('As logged user you have the following roles: ' +
|
||||
sLineBreak + string.Join(sLineBreak, Context.LoggedUser.Roles.ToArray));
|
||||
@ -119,10 +119,10 @@ begin
|
||||
Render(lJObj);
|
||||
end;
|
||||
|
||||
procedure TAdminController.OnlyRole2(ctx: TWebContext);
|
||||
procedure TAdminController.OnlyRole2;
|
||||
begin
|
||||
ContentType := TMVCMediaType.TEXT_PLAIN;
|
||||
ResponseStream.AppendLine('Hey! Hello ' + ctx.LoggedUser.UserName +
|
||||
ResponseStream.AppendLine('Hey! Hello ' + Context.LoggedUser.UserName +
|
||||
', now you are a logged user and this is a protected content!');
|
||||
ResponseStream.AppendLine('As logged user you have the following roles: ' +
|
||||
sLineBreak + string.Join(sLineBreak, Context.LoggedUser.Roles.ToArray));
|
||||
|
@ -64,7 +64,7 @@ begin
|
||||
Log(Context.Request.Headers['User-Agent']);
|
||||
if Context.Request.Headers['User-Agent'].Contains('Android') then
|
||||
begin
|
||||
Context.Response.Location := 'http://play.google.com';
|
||||
Context.Response.Location := 'https://play.google.com';
|
||||
Context.Response.StatusCode := HTTP_STATUS.TemporaryRedirect; // 307 - temporary redirect
|
||||
Handled := True;
|
||||
end;
|
||||
|
@ -14,9 +14,14 @@ type
|
||||
[MVCPath]
|
||||
[MVCHTTPMethod([httpGET])]
|
||||
procedure Index;
|
||||
|
||||
[MVCPath('/profilersample1')]
|
||||
[MVCHTTPMethod([httpGET])]
|
||||
procedure ProfilerSample1;
|
||||
|
||||
[MVCPath('/profilersample2')]
|
||||
[MVCHTTPMethod([httpGET])]
|
||||
procedure ProfilerSample2;
|
||||
protected
|
||||
fCalls: Integer;
|
||||
procedure ProcA;
|
||||
@ -46,6 +51,13 @@ begin
|
||||
NotProfiled(); //this line is not profiled
|
||||
end;
|
||||
|
||||
procedure TMyController.ProfilerSample2;
|
||||
begin
|
||||
var lProf := Profiler.Start(Context.ActionQualifiedName);
|
||||
Sleep(100);
|
||||
Render('Hello World');
|
||||
end;
|
||||
|
||||
procedure TMyController.DoSomething;
|
||||
begin
|
||||
begin var lProf := Profiler.Start('DoSomething');
|
||||
|
@ -13,7 +13,8 @@ uses
|
||||
IdContext,
|
||||
IdHTTPWebBrokerBridge,
|
||||
MainControllerU in 'MainControllerU.pas',
|
||||
WebModuleU in 'WebModuleU.pas', MVCFramework.Logger {MyWebModule: TWebModule};
|
||||
WebModuleU in 'WebModuleU.pas',
|
||||
MVCFramework.Logger {MyWebModule: TWebModule};
|
||||
|
||||
{$R *.res}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{9A719B2E-ABBC-406A-9B25-20444D1F53B3}</ProjectGuid>
|
||||
<ProjectVersion>19.5</ProjectVersion>
|
||||
<ProjectVersion>20.1</ProjectVersion>
|
||||
<FrameworkType>None</FrameworkType>
|
||||
<Base>True</Base>
|
||||
<Config Condition="'$(Config)'==''">Debug</Config>
|
||||
@ -9,6 +9,7 @@
|
||||
<TargetedPlatforms>1</TargetedPlatforms>
|
||||
<AppType>Console</AppType>
|
||||
<MainSource>ProfilingSample.dpr</MainSource>
|
||||
<ProjectName Condition="'$(ProjectName)'==''">ProfilingSample</ProjectName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''">
|
||||
<Base>true</Base>
|
||||
@ -23,11 +24,31 @@
|
||||
<CfgParent>Base</CfgParent>
|
||||
<Base>true</Base>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="('$(Platform)'=='iOSDevice64' and '$(Base)'=='true') or '$(Base_iOSDevice64)'!=''">
|
||||
<Base_iOSDevice64>true</Base_iOSDevice64>
|
||||
<CfgParent>Base</CfgParent>
|
||||
<Base>true</Base>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="('$(Platform)'=='iOSSimARM64' and '$(Base)'=='true') or '$(Base_iOSSimARM64)'!=''">
|
||||
<Base_iOSSimARM64>true</Base_iOSSimARM64>
|
||||
<CfgParent>Base</CfgParent>
|
||||
<Base>true</Base>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="('$(Platform)'=='Linux64' and '$(Base)'=='true') or '$(Base_Linux64)'!=''">
|
||||
<Base_Linux64>true</Base_Linux64>
|
||||
<CfgParent>Base</CfgParent>
|
||||
<Base>true</Base>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="('$(Platform)'=='OSX64' and '$(Base)'=='true') or '$(Base_OSX64)'!=''">
|
||||
<Base_OSX64>true</Base_OSX64>
|
||||
<CfgParent>Base</CfgParent>
|
||||
<Base>true</Base>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="('$(Platform)'=='OSXARM64' and '$(Base)'=='true') or '$(Base_OSXARM64)'!=''">
|
||||
<Base_OSXARM64>true</Base_OSXARM64>
|
||||
<CfgParent>Base</CfgParent>
|
||||
<Base>true</Base>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Base)'=='true') or '$(Base_Win32)'!=''">
|
||||
<Base_Win32>true</Base_Win32>
|
||||
<CfgParent>Base</CfgParent>
|
||||
@ -80,9 +101,29 @@
|
||||
<DCC_UsePackage>fmx;DbxCommonDriver;bindengine;IndyIPCommon;emsclient;FireDACCommonDriver;IndyProtocols;IndyIPClient;dbxcds;FmxTeeUI;bindcompfmx;ibmonitor;FireDACSqliteDriver;DbxClientDriver;soapmidas;fmxFireDAC;dbexpress;Python;inet;DataSnapCommon;dbrtl;FireDACDBXDriver;CustomIPTransport;DBXInterBaseDriver;IndySystem;ibxbindings;bindcomp;FireDACCommon;IndyCore;RESTBackendComponents;bindcompdbx;rtl;RESTComponents;DBXSqliteDriver;IndyIPServer;dsnapxml;DataSnapClient;DataSnapProviderClient;DataSnapFireDAC;emsclientfiredac;FireDAC;FireDACDSDriver;xmlrtl;tethering;ibxpress;dsnap;CloudService;FMXTee;DataSnapNativeClient;PythonFmx;soaprtl;soapserver;FireDACIBDriver;$(DCC_UsePackage)</DCC_UsePackage>
|
||||
<EnabledSysJars>annotation-1.2.0.dex.jar;asynclayoutinflater-1.0.0.dex.jar;billing-4.0.0.dex.jar;browser-1.0.0.dex.jar;cloud-messaging.dex.jar;collection-1.0.0.dex.jar;coordinatorlayout-1.0.0.dex.jar;core-1.5.0-rc02.dex.jar;core-common-2.0.1.dex.jar;core-runtime-2.0.1.dex.jar;cursoradapter-1.0.0.dex.jar;customview-1.0.0.dex.jar;documentfile-1.0.0.dex.jar;drawerlayout-1.0.0.dex.jar;firebase-annotations-16.0.0.dex.jar;firebase-common-20.0.0.dex.jar;firebase-components-17.0.0.dex.jar;firebase-datatransport-18.0.0.dex.jar;firebase-encoders-17.0.0.dex.jar;firebase-encoders-json-18.0.0.dex.jar;firebase-iid-interop-17.1.0.dex.jar;firebase-installations-17.0.0.dex.jar;firebase-installations-interop-17.0.0.dex.jar;firebase-measurement-connector-19.0.0.dex.jar;firebase-messaging-22.0.0.dex.jar;fmx.dex.jar;fragment-1.0.0.dex.jar;google-play-licensing.dex.jar;interpolator-1.0.0.dex.jar;javax.inject-1.dex.jar;legacy-support-core-ui-1.0.0.dex.jar;legacy-support-core-utils-1.0.0.dex.jar;lifecycle-common-2.0.0.dex.jar;lifecycle-livedata-2.0.0.dex.jar;lifecycle-livedata-core-2.0.0.dex.jar;lifecycle-runtime-2.0.0.dex.jar;lifecycle-service-2.0.0.dex.jar;lifecycle-viewmodel-2.0.0.dex.jar;listenablefuture-1.0.dex.jar;loader-1.0.0.dex.jar;localbroadcastmanager-1.0.0.dex.jar;play-services-ads-20.1.0.dex.jar;play-services-ads-base-20.1.0.dex.jar;play-services-ads-identifier-17.0.0.dex.jar;play-services-ads-lite-20.1.0.dex.jar;play-services-base-17.5.0.dex.jar;play-services-basement-17.6.0.dex.jar;play-services-cloud-messaging-16.0.0.dex.jar;play-services-drive-17.0.0.dex.jar;play-services-games-21.0.0.dex.jar;play-services-location-18.0.0.dex.jar;play-services-maps-17.0.1.dex.jar;play-services-measurement-base-18.0.0.dex.jar;play-services-measurement-sdk-api-18.0.0.dex.jar;play-services-places-placereport-17.0.0.dex.jar;play-services-stats-17.0.0.dex.jar;play-services-tasks-17.2.0.dex.jar;print-1.0.0.dex.jar;room-common-2.1.0.dex.jar;room-runtime-2.1.0.dex.jar;slidingpanelayout-1.0.0.dex.jar;sqlite-2.0.1.dex.jar;sqlite-framework-2.0.1.dex.jar;swiperefreshlayout-1.0.0.dex.jar;transport-api-3.0.0.dex.jar;transport-backend-cct-3.0.0.dex.jar;transport-runtime-3.0.0.dex.jar;user-messaging-platform-1.0.0.dex.jar;versionedparcelable-1.1.1.dex.jar;viewpager-1.0.0.dex.jar;work-runtime-2.1.0.dex.jar</EnabledSysJars>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Base_iOSDevice64)'!=''">
|
||||
<VerInfo_Keys>CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;NSLocationAlwaysAndWhenInUseUsageDescription=The reason for accessing the location information of the user;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSPhotoLibraryAddUsageDescription=The reason for adding to the photo library;NSCameraUsageDescription=The reason for accessing the camera;NSFaceIDUsageDescription=The reason for accessing the face id;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSSiriUsageDescription=The reason for accessing Siri;ITSAppUsesNonExemptEncryption=false;NSBluetoothAlwaysUsageDescription=The reason for accessing bluetooth;NSBluetoothPeripheralUsageDescription=The reason for accessing bluetooth peripherals;NSCalendarsUsageDescription=The reason for accessing the calendar data;NSRemindersUsageDescription=The reason for accessing the reminders;NSMotionUsageDescription=The reason for accessing the accelerometer;NSSpeechRecognitionUsageDescription=The reason for requesting to send user data to Apple's speech recognition servers</VerInfo_Keys>
|
||||
<VerInfo_UIDeviceFamily>iPhoneAndiPad</VerInfo_UIDeviceFamily>
|
||||
<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
|
||||
<BT_BuildType>Debug</BT_BuildType>
|
||||
<VerInfo_BundleId>$(MSBuildProjectName)</VerInfo_BundleId>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Base_iOSSimARM64)'!=''">
|
||||
<VerInfo_Keys>CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;NSLocationAlwaysAndWhenInUseUsageDescription=The reason for accessing the location information of the user;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSPhotoLibraryAddUsageDescription=The reason for adding to the photo library;NSCameraUsageDescription=The reason for accessing the camera;NSFaceIDUsageDescription=The reason for accessing the face id;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSSiriUsageDescription=The reason for accessing Siri;ITSAppUsesNonExemptEncryption=false;NSBluetoothAlwaysUsageDescription=The reason for accessing bluetooth;NSBluetoothPeripheralUsageDescription=The reason for accessing bluetooth peripherals;NSCalendarsUsageDescription=The reason for accessing the calendar data;NSRemindersUsageDescription=The reason for accessing the reminders;NSMotionUsageDescription=The reason for accessing the accelerometer;NSSpeechRecognitionUsageDescription=The reason for requesting to send user data to Apple's speech recognition servers</VerInfo_Keys>
|
||||
<VerInfo_UIDeviceFamily>iPhoneAndiPad</VerInfo_UIDeviceFamily>
|
||||
<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Base_Linux64)'!=''">
|
||||
<DCC_UsePackage>DataSnapServer;fmx;emshosting;DbxCommonDriver;bindengine;FireDACCommonODBC;emsclient;FireDACCommonDriver;IndyProtocols;dbxcds;emsedge;inetdb;FireDACSqliteDriver;DbxClientDriver;FireDACASADriver;soapmidas;dbexpress;Python;FireDACInfxDriver;inet;DataSnapCommon;dbrtl;FireDACOracleDriver;CustomIPTransport;FireDACMSSQLDriver;DataSnapIndy10ServerTransport;DataSnapConnectors;FireDACMongoDBDriver;IndySystem;FireDACTDataDriver;bindcomp;FireDACCommon;DataSnapServerMidas;FireDACODBCDriver;emsserverresource;IndyCore;RESTBackendComponents;rtl;FireDACMySQLDriver;FireDACADSDriver;RESTComponents;dsnapxml;DataSnapClient;LockBoxDR;DataSnapFireDAC;emsclientfiredac;FireDACPgDriver;FireDAC;xmlrtl;dsnap;CloudService;FireDACDb2Driver;DataSnapNativeClient;DatasnapConnectorsFreePascal;soaprtl;soapserver;FireDACIBDriver;$(DCC_UsePackage)</DCC_UsePackage>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Base_OSX64)'!=''">
|
||||
<VerInfo_Keys>CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSLocationUsageDescription=The reason for accessing the location information of the user;NSContactsUsageDescription=The reason for accessing the contacts;NSCalendarsUsageDescription=The reason for accessing the calendar data;NSRemindersUsageDescription=The reason for accessing the reminders;NSCameraUsageDescription=The reason for accessing the camera;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSMotionUsageDescription=The reason for accessing the accelerometer;NSDesktopFolderUsageDescription=The reason for accessing the Desktop folder;NSDocumentsFolderUsageDescription=The reason for accessing the Documents folder;NSDownloadsFolderUsageDescription=The reason for accessing the Downloads folder;NSNetworkVolumesUsageDescription=The reason for accessing files on a network volume;NSRemovableVolumesUsageDescription=The reason for accessing files on a removable volume;NSSpeechRecognitionUsageDescription=The reason for requesting to send user data to Apple's speech recognition servers;ITSAppUsesNonExemptEncryption=false;NSBluetoothAlwaysUsageDescription=The reason for accessing the Bluetooth interface</VerInfo_Keys>
|
||||
<BT_BuildType>Debug</BT_BuildType>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Base_OSXARM64)'!=''">
|
||||
<VerInfo_Keys>CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSLocationUsageDescription=The reason for accessing the location information of the user;NSContactsUsageDescription=The reason for accessing the contacts;NSCalendarsUsageDescription=The reason for accessing the calendar data;NSRemindersUsageDescription=The reason for accessing the reminders;NSCameraUsageDescription=The reason for accessing the camera;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSMotionUsageDescription=The reason for accessing the accelerometer;NSDesktopFolderUsageDescription=The reason for accessing the Desktop folder;NSDocumentsFolderUsageDescription=The reason for accessing the Documents folder;NSDownloadsFolderUsageDescription=The reason for accessing the Downloads folder;NSNetworkVolumesUsageDescription=The reason for accessing files on a network volume;NSRemovableVolumesUsageDescription=The reason for accessing files on a removable volume;NSSpeechRecognitionUsageDescription=The reason for requesting to send user data to Apple's speech recognition servers;ITSAppUsesNonExemptEncryption=false;NSBluetoothAlwaysUsageDescription=The reason for accessing the Bluetooth interface</VerInfo_Keys>
|
||||
<BT_BuildType>Debug</BT_BuildType>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Base_Win32)'!=''">
|
||||
<DCC_UsePackage>RaizeComponentsVcl;JvNet;vclwinx;DataSnapServer;fmx;emshosting;vclie;DbxCommonDriver;bindengine;IndyIPCommon;VCLRESTComponents;DBXMSSQLDriver;FireDACCommonODBC;emsclient;FireDACCommonDriver;appanalytics;IndyProtocols;vclx;IndyIPClient;dbxcds;vcledge;bindcompvclwinx;FmxTeeUI;emsedge;bindcompfmx;DBXFirebirdDriver;JvBands;inetdb;JvAppFrm;ibmonitor;FireDACSqliteDriver;DbxClientDriver;FireDACASADriver;Tee;soapmidas;SVGIconImageListFMX;JclVcl;vclactnband;TeeUI;fmxFireDAC;dbexpress;Python;Jcl;FireDACInfxDriver;JvManagedThreads;DBXMySQLDriver;VclSmp;inet;DataSnapCommon;JvPascalInterpreter;PythonVcl;vcltouch;fmxase;JvPluginSystem;DBXOdbcDriver;JvDB;dbrtl;JvTimeFramework;FireDACDBXDriver;FireDACOracleDriver;fmxdae;TeeDB;FireDACMSAccDriver;JvCustom;CustomIPTransport;FireDACMSSQLDriver;SVGIconPackage;JvSystem;DataSnapIndy10ServerTransport;JclDeveloperTools;JvControls;DataSnapConnectors;vcldsnap;DBXInterBaseDriver;JvCrypt;FireDACMongoDBDriver;JvJans;JvMM;IndySystem;JvWizards;FireDACTDataDriver;JvGlobus;vcldb;ibxbindings;SynEditDR;JclContainers;JvPageComps;vclFireDAC;JvCore;bindcomp;FireDACCommon;DataSnapServerMidas;FireDACODBCDriver;emsserverresource;IndyCore;RESTBackendComponents;dmvcframeworkDT;bindcompdbx;rtl;FireDACMySQLDriver;FireDACADSDriver;RaizeComponentsVclDb;RESTComponents;DBXSqliteDriver;vcl;IndyIPServer;dsnapxml;dsnapcon;DataSnapClient;DataSnapProviderClient;adortl;JvDotNetCtrls;JvHMI;DBXSybaseASEDriver;LockBoxDR;JvRuntimeDesign;DBXDb2Driver;JvXPCtrls;vclimg;DataSnapFireDAC;emsclientfiredac;FireDACPgDriver;FireDAC;FireDACDSDriver;inetdbxpress;xmlrtl;tethering;dmvcframeworkRT;ibxpress;JvStdCtrls;JvDlgs;bindcompvcl;dsnap;JvDocking;JvCmp;JvPrintPreview;CloudService;DBXSybaseASADriver;DBXOracleDriver;FireDACDb2Driver;DBXInformixDriver;vclib;fmxobj;bindcompvclsmp;FMXTee;DataSnapNativeClient;PythonFmx;DatasnapConnectorsFreePascal;soaprtl;SVGIconImageList;soapserver;FireDACIBDriver;$(DCC_UsePackage)</DCC_UsePackage>
|
||||
<DCC_Namespace>Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)</DCC_Namespace>
|
||||
@ -258,6 +299,16 @@
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="AndroidSplashImageDefV21">
|
||||
<Platform Name="Android">
|
||||
<RemoteDir>res\drawable-anydpi-v21</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="Android64">
|
||||
<RemoteDir>res\drawable-anydpi-v21</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="AndroidSplashStyles">
|
||||
<Platform Name="Android">
|
||||
<RemoteDir>res\values</RemoteDir>
|
||||
@ -278,6 +329,66 @@
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="AndroidSplashStylesV31">
|
||||
<Platform Name="Android">
|
||||
<RemoteDir>res\values-v31</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="Android64">
|
||||
<RemoteDir>res\values-v31</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="Android_AdaptiveIcon">
|
||||
<Platform Name="Android">
|
||||
<RemoteDir>res\drawable-anydpi-v26</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="Android64">
|
||||
<RemoteDir>res\drawable-anydpi-v26</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="Android_AdaptiveIconBackground">
|
||||
<Platform Name="Android">
|
||||
<RemoteDir>res\drawable</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="Android64">
|
||||
<RemoteDir>res\drawable</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="Android_AdaptiveIconForeground">
|
||||
<Platform Name="Android">
|
||||
<RemoteDir>res\drawable</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="Android64">
|
||||
<RemoteDir>res\drawable</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="Android_AdaptiveIconMonochrome">
|
||||
<Platform Name="Android">
|
||||
<RemoteDir>res\drawable</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="Android64">
|
||||
<RemoteDir>res\drawable</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="Android_AdaptiveIconV33">
|
||||
<Platform Name="Android">
|
||||
<RemoteDir>res\drawable-anydpi-v33</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="Android64">
|
||||
<RemoteDir>res\drawable-anydpi-v33</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="Android_Colors">
|
||||
<Platform Name="Android">
|
||||
<RemoteDir>res\values</RemoteDir>
|
||||
@ -288,6 +399,16 @@
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="Android_ColorsDark">
|
||||
<Platform Name="Android">
|
||||
<RemoteDir>res\values-night-v21</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="Android64">
|
||||
<RemoteDir>res\values-night-v21</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="Android_DefaultAppIcon">
|
||||
<Platform Name="Android">
|
||||
<RemoteDir>res\drawable</RemoteDir>
|
||||
@ -458,6 +579,56 @@
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="Android_VectorizedNotificationIcon">
|
||||
<Platform Name="Android">
|
||||
<RemoteDir>res\drawable-anydpi-v24</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="Android64">
|
||||
<RemoteDir>res\drawable-anydpi-v24</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="Android_VectorizedSplash">
|
||||
<Platform Name="Android">
|
||||
<RemoteDir>res\drawable</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="Android64">
|
||||
<RemoteDir>res\drawable</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="Android_VectorizedSplashDark">
|
||||
<Platform Name="Android">
|
||||
<RemoteDir>res\drawable-night-anydpi-v21</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="Android64">
|
||||
<RemoteDir>res\drawable-night-anydpi-v21</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="Android_VectorizedSplashV31">
|
||||
<Platform Name="Android">
|
||||
<RemoteDir>res\drawable-anydpi-v31</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="Android64">
|
||||
<RemoteDir>res\drawable-anydpi-v31</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="Android_VectorizedSplashV31Dark">
|
||||
<Platform Name="Android">
|
||||
<RemoteDir>res\drawable-night-anydpi-v31</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="Android64">
|
||||
<RemoteDir>res\drawable-night-anydpi-v31</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="DebugSymbols">
|
||||
<Platform Name="iOSSimulator">
|
||||
<Operation>1</Operation>
|
||||
@ -563,6 +734,130 @@
|
||||
<Operation>0</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="ProjectAndroidManifest">
|
||||
<Platform Name="Android">
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="Android64">
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="ProjectOSXDebug"/>
|
||||
<DeployClass Name="ProjectOSXEntitlements"/>
|
||||
<DeployClass Name="ProjectOSXInfoPList"/>
|
||||
<DeployClass Name="ProjectOSXResource">
|
||||
<Platform Name="OSX32">
|
||||
<RemoteDir>Contents\Resources</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="OSX64">
|
||||
<RemoteDir>Contents\Resources</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="OSXARM64">
|
||||
<RemoteDir>Contents\Resources</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Required="true" Name="ProjectOutput">
|
||||
<Platform Name="Android">
|
||||
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="Android64">
|
||||
<RemoteDir>library\lib\arm64-v8a</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="iOSDevice32">
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="iOSDevice64">
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="iOSSimARM64">
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="Linux64">
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="OSX32">
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="OSX64">
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="OSXARM64">
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="Win32">
|
||||
<Operation>0</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="ProjectOutput_Android32">
|
||||
<Platform Name="Android64">
|
||||
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="ProjectUWPManifest">
|
||||
<Platform Name="Win32">
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="Win64">
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="Win64x">
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="ProjectiOSDeviceDebug">
|
||||
<Platform Name="iOSDevice32">
|
||||
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="iOSDevice64">
|
||||
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="iOSSimARM64">
|
||||
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="ProjectiOSEntitlements"/>
|
||||
<DeployClass Name="ProjectiOSInfoPList"/>
|
||||
<DeployClass Name="ProjectiOSLaunchScreen"/>
|
||||
<DeployClass Name="ProjectiOSResource">
|
||||
<Platform Name="iOSDevice32">
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="iOSDevice64">
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="iOSSimARM64">
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="UWP_DelphiLogo150">
|
||||
<Platform Name="Win32">
|
||||
<RemoteDir>Assets</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="Win64">
|
||||
<RemoteDir>Assets</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="UWP_DelphiLogo44">
|
||||
<Platform Name="Win32">
|
||||
<RemoteDir>Assets</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="Win64">
|
||||
<RemoteDir>Assets</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="iOS_AppStore1024">
|
||||
<Platform Name="iOSDevice64">
|
||||
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
|
||||
@ -763,127 +1058,6 @@
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="ProjectAndroidManifest">
|
||||
<Platform Name="Android">
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="Android64">
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="ProjectiOSDeviceDebug">
|
||||
<Platform Name="iOSDevice32">
|
||||
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="iOSDevice64">
|
||||
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="iOSSimARM64">
|
||||
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="ProjectiOSEntitlements"/>
|
||||
<DeployClass Name="ProjectiOSInfoPList"/>
|
||||
<DeployClass Name="ProjectiOSLaunchScreen"/>
|
||||
<DeployClass Name="ProjectiOSResource">
|
||||
<Platform Name="iOSDevice32">
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="iOSDevice64">
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="iOSSimARM64">
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="ProjectOSXDebug"/>
|
||||
<DeployClass Name="ProjectOSXEntitlements"/>
|
||||
<DeployClass Name="ProjectOSXInfoPList"/>
|
||||
<DeployClass Name="ProjectOSXResource">
|
||||
<Platform Name="OSX32">
|
||||
<RemoteDir>Contents\Resources</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="OSX64">
|
||||
<RemoteDir>Contents\Resources</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="OSXARM64">
|
||||
<RemoteDir>Contents\Resources</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Required="true" Name="ProjectOutput">
|
||||
<Platform Name="Android">
|
||||
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="Android64">
|
||||
<RemoteDir>library\lib\arm64-v8a</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="iOSDevice32">
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="iOSDevice64">
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="iOSSimARM64">
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="Linux64">
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="OSX32">
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="OSX64">
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="OSXARM64">
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="Win32">
|
||||
<Operation>0</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="ProjectOutput_Android32">
|
||||
<Platform Name="Android64">
|
||||
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="ProjectUWPManifest">
|
||||
<Platform Name="Win32">
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="Win64">
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="UWP_DelphiLogo150">
|
||||
<Platform Name="Win32">
|
||||
<RemoteDir>Assets</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="Win64">
|
||||
<RemoteDir>Assets</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="UWP_DelphiLogo44">
|
||||
<Platform Name="Win32">
|
||||
<RemoteDir>Assets</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="Win64">
|
||||
<RemoteDir>Assets</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<ProjectRoot Platform="Android" Name="$(PROJECTNAME)"/>
|
||||
<ProjectRoot Platform="Android64" Name="$(PROJECTNAME)"/>
|
||||
<ProjectRoot Platform="iOSDevice32" Name="$(PROJECTNAME).app"/>
|
||||
@ -895,11 +1069,16 @@
|
||||
<ProjectRoot Platform="OSXARM64" Name="$(PROJECTNAME)"/>
|
||||
<ProjectRoot Platform="Win32" Name="$(PROJECTNAME)"/>
|
||||
<ProjectRoot Platform="Win64" Name="$(PROJECTNAME)"/>
|
||||
<ProjectRoot Platform="Win64x" Name="$(PROJECTNAME)"/>
|
||||
</Deployment>
|
||||
<Platforms>
|
||||
<Platform value="Android">False</Platform>
|
||||
<Platform value="Android64">False</Platform>
|
||||
<Platform value="iOSDevice64">False</Platform>
|
||||
<Platform value="iOSSimARM64">False</Platform>
|
||||
<Platform value="Linux64">False</Platform>
|
||||
<Platform value="OSX64">False</Platform>
|
||||
<Platform value="OSXARM64">False</Platform>
|
||||
<Platform value="Win32">True</Platform>
|
||||
<Platform value="Win64">False</Platform>
|
||||
</Platforms>
|
||||
|
@ -1,60 +0,0 @@
|
||||
program ServerSideViewsCustomEngine;
|
||||
|
||||
{$APPTYPE CONSOLE}
|
||||
|
||||
|
||||
uses
|
||||
System.SysUtils,
|
||||
{$IFDEF MSWINDOWS}
|
||||
Winapi.ShellAPI,
|
||||
Winapi.Windows,
|
||||
{$ENDIF }
|
||||
IdHTTPWebBrokerBridge,
|
||||
Web.WebReq,
|
||||
Web.WebBroker,
|
||||
WebModuleU in 'WebModuleU.pas' {WebModule1: TWebModule},
|
||||
WebSiteControllerU in 'WebSiteControllerU.pas',
|
||||
MVCFramework.View.Renderers.TemplatePro in 'MVCFramework.View.Renderers.TemplatePro.pas',
|
||||
TemplateProU in 'lib\TemplateProU.pas',
|
||||
MyDataModuleU in '..\renders\MyDataModuleU.pas' {MyDataModule: TDataModule},
|
||||
MVCFramework.Cache in '..\..\sources\MVCFramework.Cache.pas',
|
||||
MVCFramework.Serializer.HTML in '..\..\sources\MVCFramework.Serializer.HTML.pas';
|
||||
|
||||
{$R *.res}
|
||||
|
||||
|
||||
procedure RunServer(APort: Integer);
|
||||
var
|
||||
LServer: TIdHTTPWebBrokerBridge;
|
||||
begin
|
||||
ReportMemoryLeaksOnShutdown := True;
|
||||
Writeln(Format('Starting HTTP Server on port %d', [APort]));
|
||||
LServer := TIdHTTPWebBrokerBridge.Create(nil);
|
||||
try
|
||||
LServer.DefaultPort := APort;
|
||||
LServer.Active := True;
|
||||
Writeln('Press RETURN to stop the server');
|
||||
|
||||
{$IFDEF MSWINDOWS}
|
||||
|
||||
ShellExecute(0, 'open', 'http://localhost:8080', nil, nil, SW_SHOW);
|
||||
|
||||
{$ENDIF}
|
||||
|
||||
ReadLn;
|
||||
finally
|
||||
LServer.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
begin
|
||||
try
|
||||
if WebRequestHandler <> nil then
|
||||
WebRequestHandler.WebModuleClass := WebModuleClass;
|
||||
RunServer(8080);
|
||||
except
|
||||
on E: Exception do
|
||||
Writeln(E.ClassName, ': ', E.Message);
|
||||
end;
|
||||
|
||||
end.
|
@ -1,40 +0,0 @@
|
||||
unit WebSiteControllerU;
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
MVCFramework, System.Diagnostics, System.JSON, MVCFramework.Commons;
|
||||
|
||||
type
|
||||
|
||||
[MVCPath('/')]
|
||||
TWebSiteController = class(TMVCController)
|
||||
public
|
||||
[MVCPath('/')]
|
||||
[MVCHTTPMethods([httpGET])]
|
||||
procedure Index;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TWebSiteController }
|
||||
|
||||
uses System.SysUtils, Web.HTTPApp, MyDataModuleU;
|
||||
|
||||
procedure TWebSiteController.Index;
|
||||
var
|
||||
lDM: TMyDataModule;
|
||||
begin
|
||||
ContentType := BuildContentType(TMVCMediaType.TEXT_HTML, tmvcCharSet.UTF_8);
|
||||
lDM := TMyDataModule.Create(nil);
|
||||
try
|
||||
lDM.qryCustomers.Open;
|
||||
ViewData['people'] := lDM.qryCustomers;
|
||||
LoadView(['header', 'people_list', 'footer']);
|
||||
finally
|
||||
lDM.Free;
|
||||
end;
|
||||
RenderResponseStream; // rember to call RenderResponseStream!!!
|
||||
end;
|
||||
|
||||
end.
|
@ -1 +0,0 @@
|
||||
[{"first_name":"Scott","last_name":"Summers","age":35,"items":"","guid":""},{"first_name":"Bruce","last_name":"Banner","age":56,"items":"","guid":""},{"first_name":"ITDevConGuy","last_name":"Hello","age":36,"items":"","guid":""},{"first_name":"Hello","last_name":"World","age":56,"items":"","guid":"{556B6DA7-F0FF-4261-92D1-8477DACF1542}"}]
|
@ -1,36 +0,0 @@
|
||||
<div class="row_fluid">
|
||||
<div class="col-sm-12">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Customer</th>
|
||||
<th>First Name</th>
|
||||
<th>Last Name</th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{loop(people)}}
|
||||
<tr>
|
||||
<td>{{:people.customer}}</td>
|
||||
<td>{{:people.contact_first}}</td>
|
||||
<td>{{:people.contact_last}}</td>
|
||||
<td class="text-right">
|
||||
<a class="btn btn-default" href="javascript:alert('Not implemented, check the main demo');">
|
||||
<span class="glyphicon glyphicon-pencil"></span> View</a>
|
||||
</td>
|
||||
</tr>
|
||||
{{endloop}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<div class="row_fluid">
|
||||
<div class="col-sm-2">
|
||||
<a class="btn btn-default btn-block" href="javascript:alert('Not implemented, check the main demo');">Export as CSV</a>
|
||||
</div>
|
||||
<div class="col-sm-2 col-sm-offset-8">
|
||||
<a class="btn btn-primary btn-block" href="javascript:alert('Not implemented, check the main demo');">Add New Person</a>
|
||||
</div>
|
||||
</div>
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,58 @@
|
||||
// ***************************************************************************
|
||||
//
|
||||
// Delphi MVC Framework
|
||||
//
|
||||
// Copyright (c) 2010-2024 Daniele Teti and the DMVCFramework Team
|
||||
//
|
||||
// https://github.com/danieleteti/delphimvcframework
|
||||
//
|
||||
// ***************************************************************************
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// ***************************************************************************
|
||||
|
||||
unit CustomTemplateProFiltersU;
|
||||
|
||||
interface
|
||||
|
||||
//uses
|
||||
// mormot.core.mustache;
|
||||
|
||||
type
|
||||
TMyMustacheHelpers = class sealed
|
||||
public
|
||||
class procedure MyHelper1(const Value: variant; out Result: variant);
|
||||
class procedure MyHelper2(const Value: variant; out Result: variant);
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
MVCFramework.View.Renderers.Mustache, System.SysUtils;
|
||||
|
||||
{ TMyMustacheHelpers }
|
||||
|
||||
class procedure TMyMustacheHelpers.MyHelper1(const Value: variant;
|
||||
out Result: variant);
|
||||
begin
|
||||
Result := Value + ' (I''m The MyHelper1)';
|
||||
end;
|
||||
|
||||
class procedure TMyMustacheHelpers.MyHelper2(const Value: variant; out Result: variant);
|
||||
begin
|
||||
Result := Value + ' (I''m The MyHelper2)';
|
||||
end;
|
||||
|
||||
|
||||
end.
|
279
samples/serversideviews_templatepro/DAL.pas
Normal file
279
samples/serversideviews_templatepro/DAL.pas
Normal file
@ -0,0 +1,279 @@
|
||||
unit DAL;
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
System.JSON,
|
||||
MVCFramework.SystemJSONUtils,
|
||||
System.Generics.Collections,
|
||||
MVCFramework.Serializer.Commons;
|
||||
|
||||
type
|
||||
|
||||
[MVCNameCase(ncLowerCase)]
|
||||
TPerson = class
|
||||
private
|
||||
FFirstName: string;
|
||||
FLastName: string;
|
||||
FAge: Integer;
|
||||
FDevices: TArray<string>;
|
||||
FGUID: string;
|
||||
procedure SetFirstName(const Value: string);
|
||||
procedure SetLastName(const Value: string);
|
||||
procedure SetAge(const Value: Integer);
|
||||
procedure SetGUID(const Value: string);
|
||||
procedure SetDevices(const Value: TArray<string>);
|
||||
public
|
||||
[MVCNameAs('first_name')]
|
||||
property FirstName: string read FFirstName write SetFirstName;
|
||||
[MVCNameAs('last_name')]
|
||||
property LastName: string read FLastName write SetLastName;
|
||||
property Age: Integer read FAge write SetAge;
|
||||
property Devices: TArray<string> read FDevices write SetDevices;
|
||||
property GUID: string read FGUID write SetGUID;
|
||||
end;
|
||||
|
||||
TPeople = class(TObjectList<TPerson>)
|
||||
end;
|
||||
|
||||
{$M+}
|
||||
TMyObj = class
|
||||
private
|
||||
FRawHTML: String;
|
||||
procedure SetRawHTML(const Value: String);
|
||||
published
|
||||
property RawHTML: String read FRawHTML write SetRawHTML;
|
||||
end;
|
||||
{$M-}
|
||||
|
||||
TDevice = class
|
||||
private
|
||||
fDeviceName: string;
|
||||
fSelected: Boolean;
|
||||
public
|
||||
property DeviceName: string read fDeviceName write fDeviceName;
|
||||
property Selected: Boolean read fSelected write fSelected;
|
||||
constructor Create(aDeviceName: string; aSelected: Boolean);
|
||||
end;
|
||||
|
||||
TDeviceList = class(TObjectList<TDevice>)
|
||||
public
|
||||
function Contains(const aDeviceName: string): Boolean;
|
||||
function IndexOf(const aDeviceName: string): Integer;
|
||||
end;
|
||||
|
||||
IPeopleDAL = interface
|
||||
['{3E534A3E-EAEB-44ED-B74E-EFBBAAAE11B4}']
|
||||
function GetPeople: TPeople;
|
||||
procedure AddPerson(FirstName, LastName: string; Age: Integer;
|
||||
Items: TArray<string>);
|
||||
procedure DeleteByGUID(GUID: string);
|
||||
function GetPersonByGUID(GUID: string): TPerson;
|
||||
function GetDevicesList: TArray<String>;
|
||||
end;
|
||||
|
||||
TPeopleDAL = class(TInterfacedObject, IPeopleDAL)
|
||||
private const
|
||||
DATAFILE: string = 'people.data';
|
||||
public
|
||||
function GetPeople: TPeople;
|
||||
procedure AddPerson(FirstName, LastName: string; Age: Integer;
|
||||
Items: TArray<string>);
|
||||
procedure DeleteByGUID(GUID: string);
|
||||
function GetPersonByGUID(GUID: string): TPerson;
|
||||
function GetDevicesList: TArray<String>;
|
||||
end;
|
||||
|
||||
TServicesFactory = class sealed
|
||||
class function GetPeopleDAL: IPeopleDAL;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
System.SyncObjs,
|
||||
System.IOUtils,
|
||||
MVCFramework.Serializer.Defaults,
|
||||
System.SysUtils;
|
||||
|
||||
var
|
||||
// Hey! The storage is a simple json file, so some synchronization is needed
|
||||
_CS: TCriticalSection = nil;
|
||||
|
||||
{ TSimpleDAL }
|
||||
|
||||
procedure TPeopleDAL.AddPerson(FirstName, LastName: string; Age: Integer;
|
||||
Items: TArray<string>);
|
||||
var
|
||||
lPeople: TPeople;
|
||||
lPerson: TPerson;
|
||||
begin
|
||||
_CS.Enter;
|
||||
try
|
||||
lPeople := GetPeople;
|
||||
try
|
||||
lPerson := TPerson.Create;
|
||||
lPeople.Add(lPerson);
|
||||
lPerson.FirstName := FirstName;
|
||||
lPerson.LastName := LastName;
|
||||
lPerson.Age := Age;
|
||||
lPerson.Devices := Items;
|
||||
lPerson.GUID := TGuid.NewGuid.ToString.Replace('{', '').Replace('}', '')
|
||||
.Replace('-', '');
|
||||
TFile.WriteAllText(DATAFILE, GetDefaultSerializer.SerializeCollection
|
||||
(lPeople));
|
||||
finally
|
||||
lPeople.Free;
|
||||
end;
|
||||
finally
|
||||
_CS.Leave;
|
||||
end;
|
||||
end;
|
||||
|
||||
class function TServicesFactory.GetPeopleDAL: IPeopleDAL;
|
||||
begin
|
||||
Result := TPeopleDAL.Create;
|
||||
end;
|
||||
|
||||
procedure TPeopleDAL.DeleteByGUID(GUID: string);
|
||||
var
|
||||
LJPeople: TPeople;
|
||||
I: Integer;
|
||||
begin
|
||||
_CS.Enter;
|
||||
try
|
||||
LJPeople := GetPeople;
|
||||
try
|
||||
for I := 0 to LJPeople.Count - 1 do
|
||||
begin
|
||||
if LJPeople[I].GUID = GUID then
|
||||
begin
|
||||
LJPeople.Delete(I);
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
TFile.WriteAllText(DATAFILE, GetDefaultSerializer.SerializeCollection
|
||||
(LJPeople));
|
||||
finally
|
||||
LJPeople.Free;
|
||||
end;
|
||||
finally
|
||||
_CS.Leave;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TPeopleDAL.GetDevicesList: TArray<String>;
|
||||
begin
|
||||
Result := ['smartphone', 'dumbphone', 'laptop', 'desktop'];
|
||||
end;
|
||||
|
||||
function TPeopleDAL.GetPeople: TPeople;
|
||||
var
|
||||
LData: string;
|
||||
begin
|
||||
_CS.Enter;
|
||||
try
|
||||
Result := TPeople.Create;
|
||||
if TFile.Exists(DATAFILE) then
|
||||
LData := TFile.ReadAllText(DATAFILE).Trim;
|
||||
if not LData.IsEmpty then
|
||||
begin
|
||||
GetDefaultSerializer.DeserializeCollection(LData, Result, TPerson);
|
||||
end;
|
||||
finally
|
||||
_CS.Leave;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TPeopleDAL.GetPersonByGUID(GUID: string): TPerson;
|
||||
var
|
||||
lPeople: TPeople;
|
||||
lPerson: TPerson;
|
||||
begin
|
||||
Result := nil;
|
||||
lPeople := GetPeople;
|
||||
try
|
||||
for lPerson in lPeople do
|
||||
begin
|
||||
if lPerson.GUID = GUID then
|
||||
begin
|
||||
Result := lPeople.Extract(lPerson);
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
finally
|
||||
lPeople.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ TPerson }
|
||||
|
||||
procedure TPerson.SetAge(const Value: Integer);
|
||||
begin
|
||||
FAge := Value;
|
||||
end;
|
||||
|
||||
procedure TPerson.SetDevices(const Value: TArray<string>);
|
||||
begin
|
||||
FDevices := Value;
|
||||
end;
|
||||
|
||||
procedure TPerson.SetFirstName(const Value: string);
|
||||
begin
|
||||
FFirstName := Value;
|
||||
end;
|
||||
|
||||
procedure TPerson.SetGUID(const Value: string);
|
||||
begin
|
||||
FGUID := Value;
|
||||
end;
|
||||
|
||||
procedure TPerson.SetLastName(const Value: string);
|
||||
begin
|
||||
FLastName := Value;
|
||||
end;
|
||||
|
||||
{ TDevice }
|
||||
|
||||
constructor TDevice.Create(aDeviceName: string; aSelected: Boolean);
|
||||
begin
|
||||
inherited Create;
|
||||
fDeviceName := aDeviceName;
|
||||
fSelected := aSelected;
|
||||
end;
|
||||
|
||||
{ TDeviceList }
|
||||
|
||||
function TDeviceList.Contains(const aDeviceName: string): Boolean;
|
||||
begin
|
||||
Result := IndexOf(aDeviceName) > -1;
|
||||
end;
|
||||
|
||||
function TDeviceList.IndexOf(const aDeviceName: string): Integer;
|
||||
var
|
||||
I: Integer;
|
||||
begin
|
||||
Result := -1;
|
||||
for I := 0 to Self.Count - 1 do
|
||||
begin
|
||||
if SameText(Self[I].DeviceName, aDeviceName) then
|
||||
Exit(I);
|
||||
end;
|
||||
end;
|
||||
|
||||
{ TRawObj }
|
||||
|
||||
procedure TMyObj.SetRawHTML(const Value: String);
|
||||
begin
|
||||
FRawHTML := Value;
|
||||
end;
|
||||
|
||||
initialization
|
||||
|
||||
_CS := TCriticalSection.Create;
|
||||
|
||||
finalization
|
||||
|
||||
_CS.Free;
|
||||
|
||||
end.
|
@ -0,0 +1,68 @@
|
||||
program ServerSideViewsTemplatePro;
|
||||
|
||||
{$APPTYPE CONSOLE}
|
||||
|
||||
uses
|
||||
System.SysUtils,
|
||||
MVCFramework,
|
||||
MVCFramework.Signal,
|
||||
MVCFramework.Logger,
|
||||
{$IFDEF MSWINDOWS}
|
||||
Winapi.ShellAPI,
|
||||
Winapi.Windows,
|
||||
{$ENDIF }
|
||||
IdHTTPWebBrokerBridge,
|
||||
MVCFramework.View.Renderers.TemplatePro,
|
||||
Web.WebReq,
|
||||
Web.WebBroker,
|
||||
WebModuleU in 'WebModuleU.pas' {WebModule1: TWebModule},
|
||||
WebSiteControllerU in 'WebSiteControllerU.pas',
|
||||
DAL in 'DAL.pas',
|
||||
MyDataModuleU in '..\renders\MyDataModuleU.pas' {MyDataModule: TDataModule},
|
||||
CustomTemplateProFiltersU in 'CustomTemplateProFiltersU.pas',
|
||||
TemplatePro in '..\..\..\templatepro\TemplatePro.pas';
|
||||
|
||||
{$R *.res}
|
||||
|
||||
procedure RunServer(APort: Integer);
|
||||
var
|
||||
LServer: TIdHTTPWebBrokerBridge;
|
||||
begin
|
||||
ReportMemoryLeaksOnShutdown := True;
|
||||
LogI(Format('Starting HTTP Server on port %d', [APort]));
|
||||
LServer := TIdHTTPWebBrokerBridge.Create(nil);
|
||||
try
|
||||
LServer.DefaultPort := APort;
|
||||
LServer.Active := True;
|
||||
{$IFDEF MSWINDOWS}
|
||||
ShellExecute(0, 'open', PChar('http://localhost:' + inttostr(APort)), nil, nil, SW_SHOW);
|
||||
{$ENDIF}
|
||||
LogI('Ctrl+C to stop the server');
|
||||
WaitForTerminationSignal;
|
||||
EnterInShutdownState;
|
||||
LServer.Active := False;
|
||||
finally
|
||||
LServer.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
begin
|
||||
ReportMemoryLeaksOnShutdown := True;
|
||||
try
|
||||
if WebRequestHandler <> nil then
|
||||
WebRequestHandler.WebModuleClass := WebModuleClass;
|
||||
|
||||
// these helpers will be available to the mustache views as if they were the standard ones
|
||||
// TMVCMustacheHelpers.OnLoadCustomHelpers := procedure(var MustacheHelpers: TSynMustacheHelpers)
|
||||
// begin
|
||||
// TSynMustache.HelperAdd(MustacheHelpers, 'MyHelper1', TMyMustacheHelpers.MyHelper1);
|
||||
// TSynMustache.HelperAdd(MustacheHelpers, 'MyHelper2', TMyMustacheHelpers.MyHelper2);
|
||||
// end;
|
||||
|
||||
RunServer(8080);
|
||||
except
|
||||
on E: Exception do
|
||||
LogE(E.ClassName + ': ' + E.Message);
|
||||
end;
|
||||
|
||||
end.
|
1012
samples/serversideviews_templatepro/ServerSideViewsTemplatePro.dproj
Normal file
1012
samples/serversideviews_templatepro/ServerSideViewsTemplatePro.dproj
Normal file
File diff suppressed because it is too large
Load Diff
23
samples/serversideviews_templatepro/WebModuleU.dfm
Normal file
23
samples/serversideviews_templatepro/WebModuleU.dfm
Normal file
@ -0,0 +1,23 @@
|
||||
object WebModule1: TWebModule1
|
||||
OnCreate = WebModuleCreate
|
||||
OnDestroy = WebModuleDestroy
|
||||
Actions = <
|
||||
item
|
||||
Default = True
|
||||
Name = 'DefaultHandler'
|
||||
PathInfo = '/'
|
||||
end>
|
||||
Height = 230
|
||||
Width = 415
|
||||
object FDMemTable1: TFDMemTable
|
||||
FetchOptions.AssignedValues = [evMode]
|
||||
FetchOptions.Mode = fmAll
|
||||
ResourceOptions.AssignedValues = [rvSilentMode]
|
||||
ResourceOptions.SilentMode = True
|
||||
UpdateOptions.AssignedValues = [uvCheckRequired, uvAutoCommitUpdates]
|
||||
UpdateOptions.CheckRequired = False
|
||||
UpdateOptions.AutoCommitUpdates = True
|
||||
Left = 192
|
||||
Top = 96
|
||||
end
|
||||
end
|
@ -2,10 +2,13 @@ unit WebModuleU;
|
||||
|
||||
interface
|
||||
|
||||
uses System.SysUtils, System.Classes, Web.HTTPApp, MVCFramework;
|
||||
uses System.SysUtils, System.Classes, Web.HTTPApp, MVCFramework, FireDAC.Stan.Intf, FireDAC.Stan.Option,
|
||||
FireDAC.Stan.Param, FireDAC.Stan.Error, FireDAC.DatS, FireDAC.Phys.Intf, FireDAC.DApt.Intf, Data.DB,
|
||||
FireDAC.Comp.DataSet, FireDAC.Comp.Client;
|
||||
|
||||
type
|
||||
TWebModule1 = class(TWebModule)
|
||||
FDMemTable1: TFDMemTable;
|
||||
procedure WebModuleCreate(Sender: TObject);
|
||||
procedure WebModuleDestroy(Sender: TObject);
|
||||
private
|
||||
@ -23,13 +26,17 @@ implementation
|
||||
uses
|
||||
MVCFramework.View.Renderers.TemplatePro,
|
||||
WebSiteControllerU,
|
||||
System.IOUtils,
|
||||
MVCFramework.Commons,
|
||||
MVCFramework.Middleware.StaticFiles;
|
||||
MVCFramework.Middleware.StaticFiles,
|
||||
CustomTemplateProFiltersU,
|
||||
MVCFramework.Serializer.URLEncoded;
|
||||
|
||||
{ %CLASSGROUP 'Vcl.Controls.TControl' }
|
||||
|
||||
{$R *.dfm}
|
||||
|
||||
|
||||
procedure TWebModule1.WebModuleCreate(Sender: TObject);
|
||||
begin
|
||||
FMVCEngine := TMVCEngine.Create(Self,
|
||||
@ -46,14 +53,16 @@ begin
|
||||
// unhandled actions are permitted?
|
||||
Config[TMVCConfigKey.AllowUnhandledAction] := 'false';
|
||||
// default view file extension
|
||||
Config[TMVCConfigKey.DefaultViewFileExtension] := 'html';
|
||||
Config[TMVCConfigKey.DefaultViewFileExtension] := 'tpro';
|
||||
// view path
|
||||
Config[TMVCConfigKey.ViewPath] := 'templates';
|
||||
// Enable Server Signature in response
|
||||
Config[TMVCConfigKey.ExposeServerSignature] := 'true';
|
||||
Config[TMVCConfigKey.ViewCache] := 'false';
|
||||
end)
|
||||
.AddController(TWebSiteController)
|
||||
.SetViewEngine(TMVCTemplateProViewEngine);
|
||||
.SetViewEngine(TMVCTemplateProViewEngine)
|
||||
.AddSerializer(TMVCMediaType.APPLICATION_FORM_URLENCODED, TMVCURLEncodedSerializer.Create);
|
||||
end;
|
||||
|
||||
procedure TWebModule1.WebModuleDestroy(Sender: TObject);
|
267
samples/serversideviews_templatepro/WebSiteControllerU.pas
Normal file
267
samples/serversideviews_templatepro/WebSiteControllerU.pas
Normal file
@ -0,0 +1,267 @@
|
||||
unit WebSiteControllerU;
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
MVCFramework, System.Diagnostics, JsonDataObjects, MVCFramework.Commons, DAL,
|
||||
System.Generics.Collections;
|
||||
|
||||
type
|
||||
|
||||
[MVCPath('/')]
|
||||
TWebSiteController = class(TMVCController)
|
||||
protected
|
||||
procedure OnBeforeAction(Context: TWebContext; const AActionNAme: string;
|
||||
var Handled: Boolean); override;
|
||||
function GeneratePeopleListAsCSV: String;
|
||||
public
|
||||
[MVCPath('/people')]
|
||||
[MVCHTTPMethods([httpGET])]
|
||||
[MVCProduces(TMVCMediaType.TEXT_HTML)]
|
||||
function PeopleList: String;
|
||||
|
||||
[MVCPath('/people')]
|
||||
[MVCHTTPMethods([httpGET])]
|
||||
[MVCProduces(TMVCMediaType.TEXT_CSV)]
|
||||
// RESTful API, requires ACCEPT=text/csv
|
||||
function ExportPeopleListAsCSV_API: String;
|
||||
|
||||
[MVCPath('/people/formats/csv')]
|
||||
[MVCHTTPMethods([httpGET])]
|
||||
// Route usable by the browser, doesn't requires ACCEPT=text/csv
|
||||
function ExportPeopleListAsCSV: String;
|
||||
|
||||
[MVCPath('/people')]
|
||||
[MVCHTTPMethods([httpPOST])]
|
||||
[MVCConsumes(TMVCMediaType.APPLICATION_FORM_URLENCODED)]
|
||||
procedure SavePerson(const [MVCFromBody] Person: TPerson);
|
||||
|
||||
[MVCPath('/deleteperson')]
|
||||
[MVCHTTPMethods([httpPOST])]
|
||||
[MVCConsumes(TMVCMediaType.APPLICATION_FORM_URLENCODED)]
|
||||
procedure DeletePerson([MVCFromContentField('guid')] const GUID: String);
|
||||
|
||||
[MVCPath('/new')]
|
||||
[MVCHTTPMethods([httpGET])]
|
||||
[MVCProduces(TMVCMediaType.TEXT_HTML)]
|
||||
function NewPerson: String;
|
||||
|
||||
[MVCPath('/edit/($guid)')]
|
||||
[MVCHTTPMethods([httpGET])]
|
||||
[MVCProduces(TMVCMediaType.TEXT_HTML)]
|
||||
function EditPerson(guid: string): String;
|
||||
|
||||
[MVCPath('/')]
|
||||
[MVCHTTPMethods([httpGET])]
|
||||
[MVCProduces(TMVCMediaType.TEXT_HTML)]
|
||||
procedure Index;
|
||||
|
||||
[MVCPath('/showcase')]
|
||||
[MVCHTTPMethods([httpGET])]
|
||||
[MVCProduces(TMVCMediaType.TEXT_HTML)]
|
||||
function MustacheTemplateShowCase: String;
|
||||
|
||||
[MVCPath('/loadviewtest')]
|
||||
[MVCHTTPMethods([httpGET])]
|
||||
[MVCProduces(TMVCMediaType.TEXT_PLAIN)]
|
||||
procedure LoadViewTest;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TWebSiteController }
|
||||
|
||||
uses System.SysUtils, Web.HTTPApp, FireDAC.Stan.Intf, FireDAC.Stan.Option,
|
||||
FireDAC.Stan.Param, FireDAC.Stan.Error, FireDAC.DatS, FireDAC.Phys.Intf, FireDAC.DApt.Intf, Data.DB,
|
||||
FireDAC.Comp.DataSet, FireDAC.Comp.Client;
|
||||
|
||||
procedure TWebSiteController.DeletePerson(const GUID: String);
|
||||
var
|
||||
LDAL: IPeopleDAL;
|
||||
begin
|
||||
LDAL := TServicesFactory.GetPeopleDAL;
|
||||
LDAL.DeleteByGUID(GUID);
|
||||
Redirect('/people');
|
||||
end;
|
||||
|
||||
function TWebSiteController.EditPerson(guid: string): String;
|
||||
var
|
||||
LDAL: IPeopleDAL;
|
||||
lPerson: TPerson;
|
||||
lDevices: TArray<String>;
|
||||
lItem: string;
|
||||
lDeviceList: TDeviceList;
|
||||
lSelected: Boolean;
|
||||
begin
|
||||
LDAL := TServicesFactory.GetPeopleDAL;
|
||||
lPerson := LDAL.GetPersonByGUID(guid);
|
||||
try
|
||||
ViewData['person'] := lPerson;
|
||||
lDevices := LDAL.GetDevicesList;
|
||||
lDeviceList := TDeviceList.Create;
|
||||
try
|
||||
for lItem in lDevices do
|
||||
begin
|
||||
lSelected := TArray.Contains<String>(lPerson.Devices, lItem);
|
||||
lDeviceList.Add(TDevice.Create(lItem, lSelected))
|
||||
end;
|
||||
ViewData['devices'] := lDeviceList;
|
||||
Result := Page(['editperson']);
|
||||
finally
|
||||
lDeviceList.Free;
|
||||
end;
|
||||
finally
|
||||
lPerson.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TWebSiteController.ExportPeopleListAsCSV: String;
|
||||
begin
|
||||
Result := GeneratePeopleListAsCSV;
|
||||
// define the correct behaviour to download the csv inside the browser
|
||||
ContentType := TMVCMediaType.TEXT_CSV;
|
||||
Context.Response.CustomHeaders.Values['Content-Disposition'] :=
|
||||
'attachment; filename=people.csv';
|
||||
end;
|
||||
|
||||
function TWebSiteController.ExportPeopleListAsCSV_API: String;
|
||||
begin
|
||||
Result := GeneratePeopleListAsCSV;
|
||||
end;
|
||||
|
||||
function TWebSiteController.GeneratePeopleListAsCSV: String;
|
||||
var
|
||||
LDAL: IPeopleDAL;
|
||||
lPeople: TPeople;
|
||||
begin
|
||||
LDAL := TServicesFactory.GetPeopleDAL;
|
||||
lPeople := LDAL.GetPeople;
|
||||
try
|
||||
ViewData['people'] := lPeople;
|
||||
Result := PageFragment(['people_header.csv', 'people_list.csv']);
|
||||
finally
|
||||
lPeople.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TWebSiteController.Index;
|
||||
begin
|
||||
Redirect('/people');
|
||||
end;
|
||||
|
||||
procedure TWebSiteController.LoadViewTest;
|
||||
var
|
||||
lDS: TFDMemTable;
|
||||
begin
|
||||
lDS := TFDMemTable.Create(nil);
|
||||
try
|
||||
lDS.FieldDefs.Add('id', ftInteger);
|
||||
lDS.FieldDefs.Add('first_name', ftString, 40);
|
||||
lDS.FieldDefs.Add('last_name', ftString, 40);
|
||||
lDS.FieldDefs.Add('age', ftInteger);
|
||||
lDS.CreateDataSet;
|
||||
lDS.AppendRecord([1,'Daniele','Teti',44]);
|
||||
lDS.AppendRecord([2,'Bruce','Banner',54]);
|
||||
lDS.AppendRecord([3,'Peter','Parker',34]);
|
||||
lDS.First;
|
||||
|
||||
ViewData['people'] := lDS;
|
||||
LoadView(['people_list_test','people_list_test']);
|
||||
RenderResponseStream;
|
||||
finally
|
||||
lDS.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TWebSiteController.MustacheTemplateShowCase: String;
|
||||
var
|
||||
LDAL: IPeopleDAL;
|
||||
lPeople, lPeople2: TPeople;
|
||||
lMyObj: TMyObj;
|
||||
begin
|
||||
LDAL := TServicesFactory.GetPeopleDAL;
|
||||
lPeople := LDAL.GetPeople;
|
||||
try
|
||||
lPeople2 := TPeople.Create;
|
||||
try
|
||||
lMyObj := TMyObj.Create;
|
||||
try
|
||||
lMyObj.RawHTML := '<h1>This is</h1>Raw<br><span>HTML</span>';
|
||||
ViewData['people'] := lPeople;
|
||||
ViewData['people2'] := lPeople2;
|
||||
ViewData['myobj'] := lMyObj;
|
||||
Result := Page(['showcase'], False);
|
||||
finally
|
||||
lMyObj.Free;
|
||||
end;
|
||||
finally
|
||||
lPeople2.Free;
|
||||
end;
|
||||
finally
|
||||
lPeople.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TWebSiteController.NewPerson: String;
|
||||
var
|
||||
LDAL: IPeopleDAL;
|
||||
lDevices: TArray<String>;
|
||||
lDeviceList: TDeviceList;
|
||||
lItem: String;
|
||||
begin
|
||||
LDAL := TServicesFactory.GetPeopleDAL;
|
||||
lDevices := LDAL.GetDevicesList;
|
||||
lDeviceList := TDeviceList.Create;
|
||||
try
|
||||
for lItem in lDevices do
|
||||
begin
|
||||
lDeviceList.Add(TDevice.Create(lItem, False))
|
||||
end;
|
||||
ViewData['devices'] := lDeviceList;
|
||||
Result := Page(['editperson']);
|
||||
finally
|
||||
lDeviceList.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TWebSiteController.OnBeforeAction(Context: TWebContext;
|
||||
const AActionNAme: string; var Handled: Boolean);
|
||||
begin
|
||||
inherited;
|
||||
if not AActionNAme.ToLower.Contains('test') then ContentType := 'text/html';
|
||||
Handled := False;
|
||||
end;
|
||||
|
||||
function TWebSiteController.PeopleList: String;
|
||||
var
|
||||
LDAL: IPeopleDAL;
|
||||
lPeople: TPeople;
|
||||
begin
|
||||
LDAL := TServicesFactory.GetPeopleDAL;
|
||||
lPeople := LDAL.GetPeople;
|
||||
try
|
||||
ViewData['people'] := lPeople;
|
||||
Result := Page(['people_list']);
|
||||
finally
|
||||
lPeople.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TWebSiteController.SavePerson(const [MVCFromBody] Person: TPerson);
|
||||
var
|
||||
LPeopleDAL: IPeopleDAL;
|
||||
begin
|
||||
if Person.FirstName.IsEmpty or Person.LastName.IsEmpty or (Person.Age <= 0) then
|
||||
begin
|
||||
{ TODO -oDaniele -cGeneral : Show how to properly render an exception }
|
||||
raise EMVCException.Create('Invalid data',
|
||||
'First name, last name and age are not optional', 0);
|
||||
end;
|
||||
|
||||
LPeopleDAL := TServicesFactory.GetPeopleDAL;
|
||||
LPeopleDAL.AddPerson(Person.FirstName, Person.LastName,
|
||||
Person.Age, Person.Devices);
|
||||
Redirect('/people');
|
||||
end;
|
||||
|
||||
end.
|
1
samples/serversideviews_templatepro/bin/people.data
Normal file
1
samples/serversideviews_templatepro/bin/people.data
Normal file
@ -0,0 +1 @@
|
||||
[{"first_name":"Daniele","last_name":"Teti","age":43,"devices":[],"guid":"49E8419B66C744529D63DB292389D541"},{"first_name":"Peter","last_name":"Parker","age":23,"devices":[],"guid":"C5489969A04D4AE4B00D4FC50C8ADB5C"},{"first_name":"Bruce","last_name":"Banner","age":50,"devices":[],"guid":"B41D180F30584558B4F4A1AAF849FFA3"},{"first_name":"Sue","last_name":"Storm","age":33,"devices":[],"guid":"3F058118B8C6470D9684E127BC30A84A"},{"first_name":"Scott","last_name":"Summer","age":35,"devices":[],"guid":"3518D8C6F60E42D19C5A7250ADEADC33"},{"first_name":"Reed","last_name":"Richards","age":45,"devices":["smartphone","desktop"],"guid":"09C85C9DEB714476AADB9EB0AD689536"}]
|
@ -0,0 +1,83 @@
|
||||
{{include("partials/header.tpro")}}
|
||||
<script>
|
||||
function doDelete(id) {
|
||||
if (confirm('Are you sure?')) {
|
||||
let form = document.getElementById("myForm");
|
||||
form.action = "/deleteperson";
|
||||
form.submit();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<div class="row_fluid">
|
||||
<div class="col-sm-12">
|
||||
<form class="form form-horizontal" id="myForm" name="myForm" method="POST" action="/people">
|
||||
<input type="hidden" value="{{person.guid}}" name="guid">
|
||||
<div class="row">
|
||||
<div class="col-sm-offset-2 col-sm-8">
|
||||
{{if(!person)}}
|
||||
<h3>New Person</h3>
|
||||
{{else}}
|
||||
<h3>Edit Person</h3>
|
||||
{{endif}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<label for="first_name" class="col-sm-2 control-label">First name</label>
|
||||
<div class="col-sm-4">
|
||||
<input type="text" value="{{person.FirstName}}" class="form-control" id="first_name" placeholder="First name" name="first_name">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<label for="last_name" class="col-sm-2 control-label">Last name</label>
|
||||
<div class="col-sm-4">
|
||||
<input type="text" value="{{person.LastName}}" class="form-control" id="last_name" placeholder="Last name" name="last_name">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<label for="age" class="col-sm-2 control-label">Age</label>
|
||||
<div class="col-sm-4">
|
||||
<input type="number" value="{{person.Age}}" class="form-control" id="age" placeholder="Age" name="age">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<label for="devices" class="col-sm-2 control-label">Devices</label>
|
||||
<div class="col-sm-4">
|
||||
<select name="devices" multiple class="form-control">
|
||||
{{loop(devices)}}
|
||||
<option value="{{devices.DeviceName}}" {{if(devices.Selected)}}selected{{endif}}>{{devices.DeviceName}}</option>
|
||||
{{endloop}}
|
||||
</select>
|
||||
<span style="font-size: 80%">(Ctrl+Click to select multiple devices)</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-2">
|
||||
<button type="button" class="btn btn-default btn-block" onclick="history.back()">Return to the list</button>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
|
||||
{{if(person)}}
|
||||
<button type="button" onclick="doDelete()" class="btn btn-primary btn-block">Delete</button>
|
||||
{{else}}
|
||||
<button type="submit" class="btn btn-primary btn-block">Save</button>
|
||||
{{endif}}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{{include("partials/footer.tpro")}}
|
@ -0,0 +1 @@
|
||||
{{people.FirstName}}, {{people.LastName}}
|
@ -1,18 +1,17 @@
|
||||
<div class="row_fluid">
|
||||
<div class="row_fluid">
|
||||
<div class="col-sm-12">
|
||||
<div style="height: 100px"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row_fluid">
|
||||
<div class="col-sm-12">
|
||||
<span>N.B. All these views are UTF-8 without BOM (and this is Unicode Text: "Manutenção")</span>
|
||||
<span>N.B. All these views are UTF-8 encoded with BOM</span>
|
||||
</div>
|
||||
<div class="col-sm-8 bg-primary">
|
||||
<span>Powered by DMVCFramework</span>
|
||||
</div>
|
||||
<div class="col-sm-4 bg-success">
|
||||
<span>Page generated in with a custom view engine</span>
|
||||
<span>Server Side Views <a href="/showcase">showcase</a></span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,17 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<header>
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
|
||||
<style>
|
||||
body {
|
||||
padding: 20px auto;
|
||||
padding: 20px 50px 20px 50px;
|
||||
}
|
||||
</style>
|
||||
</header>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main" class="container">
|
||||
<h1>Customer Server Side Views Primer <small>DMVCFramework</small></h1>
|
||||
<blockquote>
|
||||
<p>This page uses the TemplatePro engine (it's a sample to show how to use a custom view engine)</p>
|
||||
</blockquote>
|
||||
|
||||
<h1>Server Side Views Primer <small>DMVCFramework</small></h1>
|
@ -0,0 +1 @@
|
||||
GUID;FirstName;LastName;Age
|
@ -0,0 +1,2 @@
|
||||
{{loop(people)}}{{people.guid}};"{{people.FirstName}}";"{{people.LastName}}";{{people.Age}}
|
||||
{{endloop}}
|
@ -0,0 +1,45 @@
|
||||
{{include("partials/header.tpro")}}
|
||||
<div class="row_fluid">
|
||||
<div class="col-sm-12">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>First name</th>
|
||||
<th>Last name</th>
|
||||
<th>Age</th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{if(people)}}
|
||||
{{loop(people)}}
|
||||
<tr>
|
||||
<td>{{people.@@index}}</td>
|
||||
<td>{{people.FirstName}}</td>
|
||||
<td>{{people.LastName}}</td>
|
||||
<td>{{people.Age}}</td>
|
||||
<td class="text-right">
|
||||
{{include("view_person_link.tpro")}}
|
||||
</td>
|
||||
</tr>
|
||||
{{endloop}}
|
||||
{{else}}
|
||||
<tr>
|
||||
<td class="text-center" colspan="5"><<No People Found>></td>
|
||||
</tr>
|
||||
{{endif}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<div class="row_fluid">
|
||||
<div class="col-sm-2">
|
||||
<a class="btn btn-default btn-block" href="/people/formats/csv">Export as CSV</a>
|
||||
</div>
|
||||
<div class="col-sm-2 col-sm-offset-8">
|
||||
<a class="btn btn-primary btn-block" href="/new">Add New Person</a>
|
||||
</div>
|
||||
</div>
|
||||
{{include("partials/footer.tpro")}}
|
@ -0,0 +1,4 @@
|
||||
LIST
|
||||
{{#people}}
|
||||
- {{id}};"{{first_name}}";"{{last_name}}";{{age}}
|
||||
{{/people}}
|
135
samples/serversideviews_templatepro/bin/templates/showcase.tpro
Normal file
135
samples/serversideviews_templatepro/bin/templates/showcase.tpro
Normal file
@ -0,0 +1,135 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<header>
|
||||
<style>
|
||||
body {
|
||||
font-family: Consolas, 'Courier New';
|
||||
}
|
||||
|
||||
blockquote {
|
||||
font-style: italic;
|
||||
color: #a0a0a0;
|
||||
padding: 0.2em;
|
||||
}
|
||||
|
||||
.section {
|
||||
background-color: #3a3a3a;
|
||||
color: white;
|
||||
border-left: 0.5em red solid;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.box {
|
||||
border: thin black solid;
|
||||
margin: auto;
|
||||
width: 80%;
|
||||
padding: 2em;
|
||||
}
|
||||
</style>
|
||||
</header>
|
||||
|
||||
<body>
|
||||
<h1>TemplatePro Showcase</h1>
|
||||
<p>
|
||||
This page is a showcase for all the TemplatePro features usable from DMVCFramework Server Side Views using the default TemplatePro engine.
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<h2 class="section">List of objects</h2>
|
||||
<div>
|
||||
{{if(people)}}
|
||||
<div>No People Found</div>
|
||||
{{else}}
|
||||
{{loop(people)}}
|
||||
<div>{{people.@@index}}. {{people.FirstName}} {{people.LastName}}</div>
|
||||
{{endloop}}
|
||||
{{endif}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 class="section">Handle empty list of objects</h2>
|
||||
<div>
|
||||
{{if(!people2)}}
|
||||
<div>No People Found</div>
|
||||
{{else}}
|
||||
{{loop(people2)}}
|
||||
<div>{{people2.@@index}}. {{people2.FirstName}} {{people2.LastName}}</div>
|
||||
{{endloop}}
|
||||
{{endif}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 class="section">Avoid HTML automatic escaping using {{{variablename$}} (put a $ after variable name)</h2>
|
||||
|
||||
<div class="box">
|
||||
{{myobj.rawhtml$}}
|
||||
<br>
|
||||
<blockquote >Check source code to know how to escape curly braces</blockquote >
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 class="section">Handling Include</h2>
|
||||
<div>
|
||||
<h2><pre>Using include("partial_person.tpro")</pre></h2>
|
||||
<ul>
|
||||
{{loop(people)}}
|
||||
<li>{{include("partial_person.tpro")}}</li>
|
||||
{{endloop}}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 class="section">Using Filters</h2>
|
||||
<ul>
|
||||
<li>Using syntax {{{"value as string"|uppercase}} = {{"value as string"|uppercase}} Filter "uppercase" is invoked passing "value as string" as constant value.</li>
|
||||
<li>Using syntax {{{first_name!uppercase}} = {{first_name|uppercase}} Filter "uppercase" is invoked passing the value contained in the attribute first_name.</li>
|
||||
<li>Helpers cannot be nested (yet)</li>
|
||||
</ul>
|
||||
<div>
|
||||
{{loop(people)}}
|
||||
<div>{{people.@@index}}. {{people.first_name|uppercase}} {{people.last_name|uppercase}}</div>
|
||||
{{endloop}}
|
||||
</div>
|
||||
<hr>
|
||||
<h3>TemplatePro provides the following built-in helpers</h3>
|
||||
<p>uppercase</p>
|
||||
<p>lowercase</p>
|
||||
<p>capitalize</p>
|
||||
<p>rpad</p>
|
||||
<p>lpad</p>
|
||||
<p>datetostr</p>
|
||||
<p>datetimetostr</p>
|
||||
<p>formatdatetime</p>
|
||||
<hr>
|
||||
<h3>DMVCFramework provides some extensions to the TemplatePro helpers</h3>
|
||||
<p>
|
||||
{{#=<% %>=}} {{#UpperCase "daniele teti"}} <%={{# }}=%> outputs: {{#UpperCase "daniele teti"}}
|
||||
</p>
|
||||
<p>
|
||||
{{#=<% %>=}} {{#LowerCase "DANIELE TETI"}} <%={{# }}=%> outputs: {{#LowerCase "daniele teti"}}
|
||||
</p>
|
||||
<p>
|
||||
{{#=<% %>=}} {{#Capitalize "daniele teti"}} <%={{# }}=%> outputs: {{#Capitalize "daniele teti"}}
|
||||
</p>
|
||||
<p>
|
||||
{{#=<% %>=}} {{#SnakeCase "daniele teti"}} <%={{# }}=%> outputs: {{#SnakeCase "daniele teti"}}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 class="section">Using Project Specific Helpers</h2>
|
||||
<h3>Any project can define its own custom helpers</h3>
|
||||
<p>
|
||||
{{#MyHelper1 "this is a text"}}
|
||||
</p>
|
||||
<p>
|
||||
{{#MyHelper2 "this is another text"}}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1 @@
|
||||
<a class="btn btn-default" href="/edit/{{people.guid}}"><span class="glyphicon glyphicon-pencil"></span> View</a>
|
@ -24,6 +24,26 @@
|
||||
<CfgParent>Base</CfgParent>
|
||||
<Base>true</Base>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="('$(Platform)'=='iOSDevice64' and '$(Base)'=='true') or '$(Base_iOSDevice64)'!=''">
|
||||
<Base_iOSDevice64>true</Base_iOSDevice64>
|
||||
<CfgParent>Base</CfgParent>
|
||||
<Base>true</Base>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="('$(Platform)'=='iOSSimARM64' and '$(Base)'=='true') or '$(Base_iOSSimARM64)'!=''">
|
||||
<Base_iOSSimARM64>true</Base_iOSSimARM64>
|
||||
<CfgParent>Base</CfgParent>
|
||||
<Base>true</Base>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="('$(Platform)'=='OSX64' and '$(Base)'=='true') or '$(Base_OSX64)'!=''">
|
||||
<Base_OSX64>true</Base_OSX64>
|
||||
<CfgParent>Base</CfgParent>
|
||||
<Base>true</Base>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="('$(Platform)'=='OSXARM64' and '$(Base)'=='true') or '$(Base_OSXARM64)'!=''">
|
||||
<Base_OSXARM64>true</Base_OSXARM64>
|
||||
<CfgParent>Base</CfgParent>
|
||||
<Base>true</Base>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Base)'=='true') or '$(Base_Win32)'!=''">
|
||||
<Base_Win32>true</Base_Win32>
|
||||
<CfgParent>Base</CfgParent>
|
||||
@ -78,6 +98,26 @@
|
||||
<BT_BuildType>Debug</BT_BuildType>
|
||||
<EnabledSysJars>activity-1.7.2.dex.jar;annotation-experimental-1.3.0.dex.jar;annotation-jvm-1.6.0.dex.jar;annotations-13.0.dex.jar;appcompat-1.2.0.dex.jar;appcompat-resources-1.2.0.dex.jar;billing-6.0.1.dex.jar;biometric-1.1.0.dex.jar;browser-1.4.0.dex.jar;cloud-messaging.dex.jar;collection-1.1.0.dex.jar;concurrent-futures-1.1.0.dex.jar;core-1.10.1.dex.jar;core-common-2.2.0.dex.jar;core-ktx-1.10.1.dex.jar;core-runtime-2.2.0.dex.jar;cursoradapter-1.0.0.dex.jar;customview-1.0.0.dex.jar;documentfile-1.0.0.dex.jar;drawerlayout-1.0.0.dex.jar;error_prone_annotations-2.9.0.dex.jar;exifinterface-1.3.6.dex.jar;firebase-annotations-16.2.0.dex.jar;firebase-common-20.3.1.dex.jar;firebase-components-17.1.0.dex.jar;firebase-datatransport-18.1.7.dex.jar;firebase-encoders-17.0.0.dex.jar;firebase-encoders-json-18.0.0.dex.jar;firebase-encoders-proto-16.0.0.dex.jar;firebase-iid-interop-17.1.0.dex.jar;firebase-installations-17.1.3.dex.jar;firebase-installations-interop-17.1.0.dex.jar;firebase-measurement-connector-19.0.0.dex.jar;firebase-messaging-23.1.2.dex.jar;fmx.dex.jar;fragment-1.2.5.dex.jar;google-play-licensing.dex.jar;interpolator-1.0.0.dex.jar;javax.inject-1.dex.jar;kotlin-stdlib-1.8.22.dex.jar;kotlin-stdlib-common-1.8.22.dex.jar;kotlin-stdlib-jdk7-1.8.22.dex.jar;kotlin-stdlib-jdk8-1.8.22.dex.jar;kotlinx-coroutines-android-1.6.4.dex.jar;kotlinx-coroutines-core-jvm-1.6.4.dex.jar;legacy-support-core-utils-1.0.0.dex.jar;lifecycle-common-2.6.1.dex.jar;lifecycle-livedata-2.6.1.dex.jar;lifecycle-livedata-core-2.6.1.dex.jar;lifecycle-runtime-2.6.1.dex.jar;lifecycle-service-2.6.1.dex.jar;lifecycle-viewmodel-2.6.1.dex.jar;lifecycle-viewmodel-savedstate-2.6.1.dex.jar;listenablefuture-1.0.dex.jar;loader-1.0.0.dex.jar;localbroadcastmanager-1.0.0.dex.jar;okio-jvm-3.4.0.dex.jar;play-services-ads-22.2.0.dex.jar;play-services-ads-base-22.2.0.dex.jar;play-services-ads-identifier-18.0.0.dex.jar;play-services-ads-lite-22.2.0.dex.jar;play-services-appset-16.0.1.dex.jar;play-services-base-18.1.0.dex.jar;play-services-basement-18.1.0.dex.jar;play-services-cloud-messaging-17.0.1.dex.jar;play-services-location-21.0.1.dex.jar;play-services-maps-18.1.0.dex.jar;play-services-measurement-base-20.1.2.dex.jar;play-services-measurement-sdk-api-20.1.2.dex.jar;play-services-stats-17.0.2.dex.jar;play-services-tasks-18.0.2.dex.jar;print-1.0.0.dex.jar;profileinstaller-1.3.0.dex.jar;room-common-2.2.5.dex.jar;room-runtime-2.2.5.dex.jar;savedstate-1.2.1.dex.jar;sqlite-2.1.0.dex.jar;sqlite-framework-2.1.0.dex.jar;startup-runtime-1.1.1.dex.jar;tracing-1.0.0.dex.jar;transport-api-3.0.0.dex.jar;transport-backend-cct-3.1.8.dex.jar;transport-runtime-3.1.8.dex.jar;user-messaging-platform-2.0.0.dex.jar;vectordrawable-1.1.0.dex.jar;vectordrawable-animated-1.1.0.dex.jar;versionedparcelable-1.1.1.dex.jar;viewpager-1.0.0.dex.jar;work-runtime-2.7.0.dex.jar</EnabledSysJars>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Base_iOSDevice64)'!=''">
|
||||
<VerInfo_Keys>CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;NSLocationAlwaysAndWhenInUseUsageDescription=The reason for accessing the location information of the user;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSPhotoLibraryAddUsageDescription=The reason for adding to the photo library;NSCameraUsageDescription=The reason for accessing the camera;NSFaceIDUsageDescription=The reason for accessing the face id;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSSiriUsageDescription=The reason for accessing Siri;ITSAppUsesNonExemptEncryption=false;NSBluetoothAlwaysUsageDescription=The reason for accessing bluetooth;NSBluetoothPeripheralUsageDescription=The reason for accessing bluetooth peripherals;NSCalendarsUsageDescription=The reason for accessing the calendar data;NSRemindersUsageDescription=The reason for accessing the reminders;NSMotionUsageDescription=The reason for accessing the accelerometer;NSSpeechRecognitionUsageDescription=The reason for requesting to send user data to Apple's speech recognition servers</VerInfo_Keys>
|
||||
<VerInfo_UIDeviceFamily>iPhoneAndiPad</VerInfo_UIDeviceFamily>
|
||||
<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
|
||||
<BT_BuildType>Debug</BT_BuildType>
|
||||
<VerInfo_BundleId>$(MSBuildProjectName)</VerInfo_BundleId>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Base_iOSSimARM64)'!=''">
|
||||
<VerInfo_Keys>CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;NSLocationAlwaysAndWhenInUseUsageDescription=The reason for accessing the location information of the user;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSPhotoLibraryAddUsageDescription=The reason for adding to the photo library;NSCameraUsageDescription=The reason for accessing the camera;NSFaceIDUsageDescription=The reason for accessing the face id;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSSiriUsageDescription=The reason for accessing Siri;ITSAppUsesNonExemptEncryption=false;NSBluetoothAlwaysUsageDescription=The reason for accessing bluetooth;NSBluetoothPeripheralUsageDescription=The reason for accessing bluetooth peripherals;NSCalendarsUsageDescription=The reason for accessing the calendar data;NSRemindersUsageDescription=The reason for accessing the reminders;NSMotionUsageDescription=The reason for accessing the accelerometer;NSSpeechRecognitionUsageDescription=The reason for requesting to send user data to Apple's speech recognition servers</VerInfo_Keys>
|
||||
<VerInfo_UIDeviceFamily>iPhoneAndiPad</VerInfo_UIDeviceFamily>
|
||||
<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Base_OSX64)'!=''">
|
||||
<VerInfo_Keys>CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSLocationUsageDescription=The reason for accessing the location information of the user;NSContactsUsageDescription=The reason for accessing the contacts;NSCalendarsUsageDescription=The reason for accessing the calendar data;NSRemindersUsageDescription=The reason for accessing the reminders;NSCameraUsageDescription=The reason for accessing the camera;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSMotionUsageDescription=The reason for accessing the accelerometer;NSDesktopFolderUsageDescription=The reason for accessing the Desktop folder;NSDocumentsFolderUsageDescription=The reason for accessing the Documents folder;NSDownloadsFolderUsageDescription=The reason for accessing the Downloads folder;NSNetworkVolumesUsageDescription=The reason for accessing files on a network volume;NSRemovableVolumesUsageDescription=The reason for accessing files on a removable volume;NSSpeechRecognitionUsageDescription=The reason for requesting to send user data to Apple's speech recognition servers;ITSAppUsesNonExemptEncryption=false;NSBluetoothAlwaysUsageDescription=The reason for accessing the Bluetooth interface</VerInfo_Keys>
|
||||
<BT_BuildType>Debug</BT_BuildType>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Base_OSXARM64)'!=''">
|
||||
<VerInfo_Keys>CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSLocationUsageDescription=The reason for accessing the location information of the user;NSContactsUsageDescription=The reason for accessing the contacts;NSCalendarsUsageDescription=The reason for accessing the calendar data;NSRemindersUsageDescription=The reason for accessing the reminders;NSCameraUsageDescription=The reason for accessing the camera;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSMotionUsageDescription=The reason for accessing the accelerometer;NSDesktopFolderUsageDescription=The reason for accessing the Desktop folder;NSDocumentsFolderUsageDescription=The reason for accessing the Documents folder;NSDownloadsFolderUsageDescription=The reason for accessing the Downloads folder;NSNetworkVolumesUsageDescription=The reason for accessing files on a network volume;NSRemovableVolumesUsageDescription=The reason for accessing files on a removable volume;NSSpeechRecognitionUsageDescription=The reason for requesting to send user data to Apple's speech recognition servers;ITSAppUsesNonExemptEncryption=false;NSBluetoothAlwaysUsageDescription=The reason for accessing the Bluetooth interface</VerInfo_Keys>
|
||||
<BT_BuildType>Debug</BT_BuildType>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Base_Win32)'!=''">
|
||||
<DCC_UsePackage>DBXSqliteDriver;RESTComponents;DataSnapServerMidas;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;emsclientfiredac;DataSnapFireDAC;svnui;tethering;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;svn;DBXOracleDriver;inetdb;FmxTeeUI;emsedge;FireDACIBDriver;fmx;fmxdae;vclib;FireDACDBXDriver;dbexpress;IndyCore;vclx;dsnap;DataSnapCommon;emsclient;FireDACCommon;RESTBackendComponents;DataSnapConnectors;VCLRESTComponents;soapserver;vclie;bindengine;DBXMySQLDriver;FireDACOracleDriver;CloudService;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;FireDACCommonDriver;DataSnapClient;inet;bindcompdbx;IndyIPCommon;vcl;DBXSybaseASEDriver;IndyIPServer;IndySystem;FireDACDb2Driver;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;TeeDB;FireDAC;emshosting;FireDACSqliteDriver;FireDACPgDriver;ibmonitor;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;FMXTee;soaprtl;DbxCommonDriver;ibxpress;Tee;DataSnapServer;xmlrtl;soapmidas;DataSnapNativeClient;fmxobj;vclwinx;ibxbindings;rtl;emsserverresource;DbxClientDriver;FireDACDSDriver;DBXSybaseASADriver;CustomIPTransport;vcldsnap;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;TeeUI;dmvcframeworkRT;dbxcds;VclSmp;adortl;FireDACODBCDriver;DataSnapIndy10ServerTransport;dmvcframeworkDT;dsnapxml;DataSnapProviderClient;dbrtl;inetdbxpress;FireDACMongoDBDriver;IndyProtocols;fmxase;$(DCC_UsePackage)</DCC_UsePackage>
|
||||
<DCC_Namespace>Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)</DCC_Namespace>
|
||||
@ -1024,7 +1064,11 @@
|
||||
<Platforms>
|
||||
<Platform value="Android">False</Platform>
|
||||
<Platform value="Android64">False</Platform>
|
||||
<Platform value="iOSDevice64">False</Platform>
|
||||
<Platform value="iOSSimARM64">False</Platform>
|
||||
<Platform value="Linux64">False</Platform>
|
||||
<Platform value="OSX64">False</Platform>
|
||||
<Platform value="OSXARM64">False</Platform>
|
||||
<Platform value="Win32">True</Platform>
|
||||
<Platform value="Win64">False</Platform>
|
||||
</Platforms>
|
||||
|
@ -44,7 +44,7 @@ uses
|
||||
MVCFramework.Serializer.Defaults,
|
||||
MVCFramework.Serializer.Intf,
|
||||
MVCFramework.DuckTyping,
|
||||
TemplateProU,
|
||||
TemplatePro,
|
||||
MVCFramework.Cache,
|
||||
Data.DB, System.Rtti;
|
||||
|
||||
@ -53,12 +53,14 @@ uses
|
||||
procedure TMVCTemplateProViewEngine.Execute(const ViewName: string;
|
||||
const OutputStream: TStream);
|
||||
var
|
||||
lTP: TTemplateProEngine;
|
||||
lTP: TTProCompiler;
|
||||
lViewFileName: string;
|
||||
lViewTemplate: UTF8String;
|
||||
lCacheItem: TMVCCacheItem;
|
||||
lCompiledTemplate: ITProCompiledTemplate;
|
||||
lPair: TPair<String, TValue>;
|
||||
begin
|
||||
lTP := TTemplateProEngine.Create;
|
||||
lTP := TTProCompiler.Create;
|
||||
try
|
||||
lViewFileName := GetRealFileName(ViewName);
|
||||
if not FileExists(lViewFileName) then
|
||||
@ -79,16 +81,24 @@ begin
|
||||
if lCacheItem.TimeStamp < TFile.GetLastWriteTime(lViewFileName) then
|
||||
begin
|
||||
lViewTemplate := TFile.ReadAllText(lViewFileName, TEncoding.UTF8);
|
||||
TMVCCacheSingleton.Instance.SetValue(lViewFileName,
|
||||
lViewTemplate);
|
||||
TMVCCacheSingleton.Instance.SetValue(lViewFileName, lViewTemplate);
|
||||
end;
|
||||
end;
|
||||
|
||||
lViewTemplate := lCacheItem.Value.AsString;
|
||||
try
|
||||
lTP.Execute(lViewTemplate, TTPObjectListDictionary(ViewModel), nil, OutputStream);
|
||||
lCompiledTemplate := lTP.Compile(lViewTemplate, lViewFileName);
|
||||
if Assigned(ViewModel) then
|
||||
begin
|
||||
for lPair in ViewModel do
|
||||
begin
|
||||
lCompiledTemplate.SetData(lPair.Key, ViewModel[lPair.Key]);
|
||||
end;
|
||||
end;
|
||||
//lCompiledTemplate.DumpToFile(TPath.Combine(AppPath, 'TProDump.txt'));
|
||||
TStringStream(OutputStream).WriteString(lCompiledTemplate.Render);
|
||||
except
|
||||
on E: EParserException do
|
||||
on E: ETProException do
|
||||
begin
|
||||
raise EMVCViewError.CreateFmt('View [%s] error: %s (%s)',
|
||||
[ViewName, E.Message, E.ClassName]);
|
1861
sources/TemplatePro.pas
Normal file
1861
sources/TemplatePro.pas
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user