Improved WebStancils ViewEngine
Some checks are pending
TOC Generator / TOC Generator (push) Waiting to run

This commit is contained in:
Daniele Teti 2024-11-08 12:37:13 +01:00
parent 6ac033b809
commit 96088127ec
2 changed files with 207 additions and 12 deletions

View File

@ -43,8 +43,8 @@ uses
MVCFramework.Serializer.Defaults, MVCFramework.Serializer.Defaults,
MVCFramework.Serializer.Intf, MVCFramework.Serializer.Intf,
MVCFramework.DuckTyping, MVCFramework.DuckTyping,
TemplatePro,
MVCFramework.Cache, MVCFramework.Cache,
TemplatePro,
Data.DB, Data.DB,
System.Rtti, System.Rtti,
JsonDataObjects; JsonDataObjects;
@ -136,7 +136,10 @@ begin
if FUseViewCache then if FUseViewCache then
begin begin
lCacheDir := TPath.Combine(TPath.GetDirectoryName(lViewFileName), '__cache__'); lCacheDir := TPath.Combine(TPath.GetDirectoryName(lViewFileName), '__cache__');
TDirectory.CreateDirectory(lCacheDir); if not TDirectory.Exists(lCacheDir) then
begin
TDirectory.CreateDirectory(lCacheDir);
end;
lCompiledViewFileName := TPath.Combine(lCacheDir, TPath.ChangeExtension(TPath.GetFileName(lViewFileName), '.' + TEMPLATEPRO_VERSION + '.tpcu')); lCompiledViewFileName := TPath.Combine(lCacheDir, TPath.ChangeExtension(TPath.GetFileName(lViewFileName), '.' + TEMPLATEPRO_VERSION + '.tpcu'));
if not FileAge(lViewFileName, lActualFileTimeStamp) then if not FileAge(lViewFileName, lActualFileTimeStamp) then

View File

@ -34,14 +34,19 @@ interface
uses uses
MVCFramework, System.Generics.Collections, System.SysUtils, MVCFramework, System.Generics.Collections, System.SysUtils,
MVCFramework.Commons, System.IOUtils, System.Classes, Web.Stencils; MVCFramework.Commons, System.IOUtils, System.Classes, Web.Stencils,
System.Rtti, MVCFramework.Nullables, System.DateUtils;
type type
TMVCWebStencilsEvent = reference to procedure(const WebStencilsProcessor: TWebStencilsProcessor); TMVCWebStencilsEvent = reference to procedure(const WebStencilsProcessor: TWebStencilsProcessor);
{ This class implements the WebStencils view engine for server side views } { This class implements the WebStencils view engine for server side views }
TMVCWebStencilsViewEngine = class(TMVCBaseViewEngine) TMVCWebStencilsViewEngine = class(TMVCBaseViewEngine)
protected
procedure RegisterWSFunctions(WSProcessor: TWebStencilsProcessor);
procedure OnGetValue(Sender: TObject; const AObjectName, APropName: string; var AReplaceText: string; var AHandled: Boolean);
public public
class function GetTValueVarAsString(const Value: TValue; const VarName: string; const Processor: TWebStencilsProcessor): String;
procedure Execute(const ViewName: string; const Builder: TStringBuilder); override; procedure Execute(const ViewName: string; const Builder: TStringBuilder); override;
end; end;
@ -68,7 +73,6 @@ uses
System.Bindings.Methods, System.Bindings.Methods,
MVCFramework.Cache, MVCFramework.Cache,
Data.DB, Data.DB,
System.Rtti,
JsonDataObjects; JsonDataObjects;
{$WARNINGS OFF} {$WARNINGS OFF}
@ -81,7 +85,7 @@ function GetDataSetOrObjectListCount(const aValue: TValue; const aParameters: TA
var var
lWrappedList: IMVCList; lWrappedList: IMVCList;
begin begin
if not aValue.IsObject then if aValue.IsEmpty or (not aValue.IsObject) then
begin begin
Result := False; Result := False;
end; end;
@ -141,13 +145,32 @@ end;
function MakeMethodJSON: IInvokable; function MakeMethodJSON: IInvokable;
begin begin
Result := MakeInvokable(function(Args: TArray<IValue>): IValue Result :=
begin MakeInvokable(function(Args: TArray<IValue>): IValue
Result := TValueWrapper.Create(DumpAsJSONString(Args[0].GetValue.AsObject, [])); begin
end) Result := TValueWrapper.Create(DumpAsJSONString(Args[0].GetValue.AsObject, []));
end);
end; end;
procedure RegisterWSFunctions(WSProcessor: TWebStencilsProcessor);
procedure TMVCWebStencilsViewEngine.OnGetValue(Sender: TObject; const AObjectName, APropName: string; var AReplaceText: string; var AHandled: Boolean);
var
lValue: TValue;
begin
AHandled := False;
if ViewModel.TryGetValue(AObjectName, lValue) then
begin
AReplaceText := GetTValueVarAsString(lValue, AObjectName, TWebStencilsProcessor(Sender));
AHandled := True;
end
else
begin
AReplaceText := '';
AHandled := True;
end;
end;
procedure TMVCWebStencilsViewEngine.RegisterWSFunctions(WSProcessor: TWebStencilsProcessor);
begin begin
if gFunctionInitialized then Exit; if gFunctionInitialized then Exit;
TMonitor.Enter(gWSLock); TMonitor.Enter(gWSLock);
@ -155,7 +178,6 @@ begin
if gFunctionInitialized then Exit; if gFunctionInitialized then Exit;
gFunctionInitialized := True; gFunctionInitialized := True;
TBindingMethodsFactory.RegisterMethod( TBindingMethodsFactory.RegisterMethod(
TMethodDescription.Create( TMethodDescription.Create(
MakeInvokable(function(Args: TArray<IValue>): IValue MakeInvokable(function(Args: TArray<IValue>): IValue
@ -168,6 +190,18 @@ begin
end) as IInvokable, end) as IInvokable,
'json', 'json', '', True, 'Serialize an object to JSON', nil)); 'json', 'json', '', True, 'Serialize an object to JSON', nil));
TBindingMethodsFactory.RegisterMethod(
TMethodDescription.Create(
MakeInvokable(function(Args: TArray<IValue>): IValue
begin
if Length(Args) <> 1 then
begin
raise EWebStencilsException.Create('Expected 1 parameter, got ' + Length(Args).ToString);
end;
Result := TValueWrapper.Create(TMVCWebStencilsViewEngine.GetTValueVarAsString(Args[0].GetValue, '', nil));
end),
'ValueOf', 'ValueOf', '', True, 'ValueOf returns the inner value of a nullable as string - the non-nullable types are returned as-is', nil));
finally finally
TMonitor.Exit(gWSLock); TMonitor.Exit(gWSLock);
end; end;
@ -191,9 +225,10 @@ begin
begin begin
TMVCWebStencilsConfiguration.OnProcessorConfiguration(lWebStencilsProcessor); TMVCWebStencilsConfiguration.OnProcessorConfiguration(lWebStencilsProcessor);
end; end;
//lWebStencilsProcessor.OnFile := Self.OnFile; {12.2, any filename starting with ..\ is not read correctly by the parser. Is it a feature? } lWebStencilsProcessor.OnValue := OnGetValue;
lWebStencilsProcessor.InputFileName := lViewFileName; lWebStencilsProcessor.InputFileName := lViewFileName;
lWebStencilsProcessor.PathTemplate := Config[TMVCConfigKey.ViewPath]; lWebStencilsProcessor.PathTemplate := Config[TMVCConfigKey.ViewPath];
lWebStencilsProcessor.WebRequest := WebContext.Request.RawWebRequest;
if Assigned(ViewModel) then if Assigned(ViewModel) then
begin begin
for lPair in ViewModel do for lPair in ViewModel do
@ -202,6 +237,10 @@ begin
lWebStencilsProcessor.AddVar(lPair.Key, ViewModel[lPair.Key].AsObject, False); lWebStencilsProcessor.AddVar(lPair.Key, ViewModel[lPair.Key].AsObject, False);
end; end;
end; end;
if Assigned(WebContext.LoggedUser) then
begin
lWebStencilsProcessor.UserRoles := WebContext.LoggedUser.Roles.ToString;
end;
if Assigned(FBeforeRenderCallback) then if Assigned(FBeforeRenderCallback) then
begin begin
FBeforeRenderCallback(lWebStencilsProcessor); FBeforeRenderCallback(lWebStencilsProcessor);
@ -219,6 +258,159 @@ begin
end; end;
end; end;
class function TMVCWebStencilsViewEngine.GetTValueVarAsString(const Value: TValue; const VarName: string; const Processor: TWebStencilsProcessor): String;
var
lIsObject: Boolean;
lAsObject: TObject;
lNullableInt32: NullableInt32;
lNullableUInt32: NullableUInt32;
lNullableInt16: NullableInt16;
lNullableUInt16: NullableUInt16;
lNullableInt64: NullableInt64;
lNullableUInt64: NullableUInt64;
lNullableCurrency: NullableCurrency;
lNullableBoolean: NullableBoolean;
lNullableTDate: NullableTDate;
lNullableTTime: NullableTTime;
lNullableTDateTime: NullableTDateTime;
begin
if Value.IsEmpty then
begin
Exit('');
end;
lIsObject := False;
lAsObject := nil;
if Value.IsObject then
begin
lIsObject := True;
lAsObject := Value.AsObject;
end;
if lIsObject then
begin
if lAsObject is TField then
Result := TField(Value.AsObject).AsString
else if lAsObject is TJsonBaseObject then
Result := TJsonBaseObject(lAsObject).ToJSON()
else
Result := lAsObject.ToString;
end
else
begin
if Value.TypeInfo.Kind = tkRecord then
begin
Result := '';
if Value.TypeInfo = TypeInfo(NullableInt32) then
begin
lNullableInt32 := Value.AsType<NullableInt32>;
if lNullableInt32.HasValue then
Result := lNullableInt32.Value.ToString
end
else if Value.TypeInfo = TypeInfo(NullableUInt32) then
begin
lNullableUInt32 := Value.AsType<NullableUInt32>;
if lNullableUInt32.HasValue then
Result := lNullableUInt32.Value.ToString
end
else if Value.TypeInfo = TypeInfo(NullableInt16) then
begin
lNullableInt16 := Value.AsType<NullableInt16>;
if lNullableInt16.HasValue then
Result := lNullableInt16.Value.ToString
end
else if Value.TypeInfo = TypeInfo(NullableUInt16) then
begin
lNullableUInt16 := Value.AsType<NullableUInt16>;
if lNullableUInt16.HasValue then
Result := lNullableUInt16.Value.ToString
end
else if Value.TypeInfo = TypeInfo(NullableInt64) then
begin
lNullableInt64 := Value.AsType<NullableInt64>;
if lNullableInt64.HasValue then
Result := lNullableInt64.Value.ToString
end
else if Value.TypeInfo = TypeInfo(NullableUInt64) then
begin
lNullableUInt64 := Value.AsType<NullableUInt64>;
if lNullableUInt64.HasValue then
Result := lNullableUInt64.Value.ToString
end
else if Value.TypeInfo = TypeInfo(NullableString) then
begin
Result := Value.AsType<NullableString>.ValueOrDefault;
end
else if Value.TypeInfo = TypeInfo(NullableCurrency) then
begin
lNullableCurrency := Value.AsType<NullableCurrency>;
if lNullableCurrency.HasValue then
Result := FloatToStr(lNullableCurrency.Value);
//Result := FloatToStr(lNullableCurrency.Value, fLocaleFormatSettings);
end
else if Value.TypeInfo = TypeInfo(NullableBoolean) then
begin
lNullableBoolean := Value.AsType<NullableBoolean>;
if lNullableBoolean.HasValue then
Result := BoolToStr(lNullableBoolean.Value, True);
end
else if Value.TypeInfo = TypeInfo(NullableTDate) then
begin
lNullableTDate := Value.AsType<NullableTDate>;
if lNullableTDate.HasValue then
Result := DateToISO8601(lNullableTDate.Value);
end
else if Value.TypeInfo = TypeInfo(NullableTTime) then
begin
lNullableTTime := Value.AsType<NullableTTime>;
if lNullableTTime.HasValue then
Result := DateToISO8601(lNullableTTime.Value);
end
else if Value.TypeInfo = TypeInfo(NullableTDateTime) then
begin
lNullableTDateTime := Value.AsType<NullableTDateTime>;
if lNullableTDateTime.HasValue then
Result := DateToISO8601(lNullableTDateTime.Value);
end
else
begin
raise EWebStencilsException.Create('Unsupported type for variable "' + VarName + '"');
end;
end
else
begin
case Value.Kind of
tkInteger: Result := Value.AsInteger.ToString;
tkInt64: Result := Value.AsInt64.ToString;
tkString, tkUString, tkWString, tkLString: Result := Value.AsString;
tkWChar, tkChar: Result := Value.AsType<Char>;
tkFloat: begin
if Value.TypeInfo.Name = 'TDate' then
begin
//Result := DateToStr(Value.AsExtended, fLocaleFormatSettings);
Result := DateToStr(Value.AsExtended);
end
else if Value.TypeInfo.Name = 'TDateTime' then
begin
//Result := DateTimeToStr(Value.AsExtended, fLocaleFormatSettings);
Result := DateTimeToStr(Value.AsExtended);
end
else
begin
//Result := FloatToStr(Value.AsExtended, fLocaleFormatSettings);
Result := FloatToStr(Value.AsExtended);
end;
end;
tkEnumeration: Result := Value.ToString.ToLower;
else
raise EWebStencilsException.Create('Unsupported type for variable "' + VarName + '"');
end;
end;
end;
end;
initialization initialization
gWSLock := TObject.Create; gWSLock := TObject.Create;