support for delphi 11.1
This commit is contained in:
@@ -14,51 +14,42 @@ object MainLogView: TMainLogView
|
||||
OnCreate = FormCreate
|
||||
OnKeyDown = FormKeyDown
|
||||
OnShow = FormShow
|
||||
LCLVersion = '2.0.8.0'
|
||||
object Splitter2: TSplitter
|
||||
Cursor = crVSplit
|
||||
Left = 0
|
||||
Height = 4
|
||||
Top = 635
|
||||
Width = 860
|
||||
Align = alBottom
|
||||
ResizeAnchor = akBottom
|
||||
end
|
||||
LCLVersion = '2.0.11.0'
|
||||
object Splitter3: TSplitter
|
||||
Left = 837
|
||||
Height = 635
|
||||
Height = 583
|
||||
Top = 0
|
||||
Width = 4
|
||||
Visible = False
|
||||
end
|
||||
object Splitter1: TSplitter
|
||||
Left = 829
|
||||
Height = 635
|
||||
Height = 583
|
||||
Top = 0
|
||||
Width = 4
|
||||
Visible = False
|
||||
end
|
||||
object Splitter4: TSplitter
|
||||
Left = 833
|
||||
Height = 635
|
||||
Height = 583
|
||||
Top = 0
|
||||
Width = 4
|
||||
Visible = False
|
||||
end
|
||||
object PanelLeft: TPanel
|
||||
Left = 257
|
||||
Height = 635
|
||||
Height = 583
|
||||
Top = 0
|
||||
Width = 150
|
||||
Align = alLeft
|
||||
ClientHeight = 635
|
||||
ClientHeight = 583
|
||||
ClientWidth = 150
|
||||
Constraints.MinWidth = 150
|
||||
TabOrder = 0
|
||||
object ImageLogo: TImage
|
||||
Left = 8
|
||||
Height = 32
|
||||
Top = 591
|
||||
Top = 539
|
||||
Width = 137
|
||||
Anchors = [akLeft, akRight, akBottom]
|
||||
Center = True
|
||||
@@ -421,9 +412,27 @@ object MainLogView: TMainLogView
|
||||
TopIndex = -1
|
||||
end
|
||||
end
|
||||
object Splitter2: TSplitter
|
||||
Cursor = crVSplit
|
||||
Left = 0
|
||||
Height = 4
|
||||
Top = 583
|
||||
Width = 860
|
||||
Align = alBottom
|
||||
ResizeAnchor = akBottom
|
||||
end
|
||||
object PanelBottom: TPanel
|
||||
Left = 0
|
||||
Height = 52
|
||||
Top = 587
|
||||
Width = 860
|
||||
Align = alBottom
|
||||
TabOrder = 9
|
||||
OnResize = PanelBottomResize
|
||||
end
|
||||
object List: TDrawGrid
|
||||
Left = 841
|
||||
Height = 635
|
||||
Height = 583
|
||||
Top = 0
|
||||
Width = 19
|
||||
Align = alClient
|
||||
@@ -447,7 +456,7 @@ object MainLogView: TMainLogView
|
||||
end
|
||||
object ProfileList: TDrawGrid
|
||||
Left = 407
|
||||
Height = 635
|
||||
Height = 583
|
||||
Top = 0
|
||||
Width = 274
|
||||
Align = alLeft
|
||||
@@ -468,17 +477,17 @@ object MainLogView: TMainLogView
|
||||
end
|
||||
object PanelThread: TPanel
|
||||
Left = 681
|
||||
Height = 635
|
||||
Height = 583
|
||||
Top = 0
|
||||
Width = 148
|
||||
Align = alLeft
|
||||
ClientHeight = 635
|
||||
ClientHeight = 583
|
||||
ClientWidth = 148
|
||||
TabOrder = 3
|
||||
Visible = False
|
||||
object ThreadListBox: TCheckListBox
|
||||
Left = 1
|
||||
Height = 593
|
||||
Height = 541
|
||||
Top = 1
|
||||
Width = 146
|
||||
Align = alClient
|
||||
@@ -492,7 +501,7 @@ object MainLogView: TMainLogView
|
||||
object pnlThreadBottom: TPanel
|
||||
Left = 1
|
||||
Height = 40
|
||||
Top = 594
|
||||
Top = 542
|
||||
Width = 146
|
||||
Align = alBottom
|
||||
ClientHeight = 40
|
||||
@@ -512,11 +521,11 @@ object MainLogView: TMainLogView
|
||||
end
|
||||
object PanelBrowse: TPanel
|
||||
Left = 0
|
||||
Height = 635
|
||||
Height = 583
|
||||
Top = 0
|
||||
Width = 257
|
||||
Align = alLeft
|
||||
ClientHeight = 635
|
||||
ClientHeight = 583
|
||||
ClientWidth = 257
|
||||
Constraints.MinWidth = 80
|
||||
TabOrder = 4
|
||||
|
@@ -57,6 +57,7 @@ type
|
||||
fInts: TIntegerDynArray;
|
||||
fCreateTime: TCreateTime;
|
||||
fData: TSQLRawBlob;
|
||||
fFP: double;
|
||||
published
|
||||
property Name: RawUTF8 read fName write fName stored AS_UNIQUE;
|
||||
property Age: integer read fAge write fAge;
|
||||
@@ -65,6 +66,7 @@ type
|
||||
property Ints: TIntegerDynArray index 1 read fInts write fInts;
|
||||
property Data: TSQLRawBlob read fData write fData;
|
||||
property CreateTime: TCreateTime read fCreateTime write fCreateTime;
|
||||
property FP: double read fFP write fFP;
|
||||
end;
|
||||
|
||||
TTestORM = class(TSynTestCase)
|
||||
@@ -183,8 +185,9 @@ begin
|
||||
Check(serverTime<>0);
|
||||
CheckSame(Now,serverTime,0.5);
|
||||
if System.Pos('MongoDB',Owner.CustomVersions)=0 then
|
||||
Owner.CustomVersions := Owner.CustomVersions+'Using '+
|
||||
string(fClient.ServerBuildInfoText);
|
||||
Owner.CustomVersions := format('%sUsing %s'#13#10'Running on %s'#13#10+
|
||||
'Compiled with mORMot '+SYNOPSE_FRAMEWORK_VERSION,
|
||||
[Owner.CustomVersions,fClient.ServerBuildInfoText,OSVersionText]);
|
||||
fExpectedCount := COLL_COUNT;
|
||||
end;
|
||||
|
||||
@@ -452,6 +455,7 @@ begin
|
||||
R.Value := _ObjFast(['num',i]);
|
||||
R.Ints := nil;
|
||||
R.DynArray(1).Add(i);
|
||||
R.FP := i*7.3445;
|
||||
Check(fClient.BatchAdd(R,True)>=0);
|
||||
end;
|
||||
finally
|
||||
@@ -473,6 +477,7 @@ begin
|
||||
Check(Length(R.Ints)=1);
|
||||
Check(R.Ints[0]=aID);
|
||||
Check(R.CreateTime>=fStartTimeStamp);
|
||||
CheckSame(R.FP,aID*7.3445);
|
||||
end;
|
||||
|
||||
procedure TTestORM.Retrieve;
|
||||
|
@@ -19,12 +19,14 @@ type
|
||||
fTitle: RawUTF8;
|
||||
fLanguage: RawUTF8;
|
||||
fAbout: RawUTF8;
|
||||
fLink: RawUTF8;
|
||||
published
|
||||
property Title: RawUTF8 index 80 read fTitle write fTitle;
|
||||
property Language: RawUTF8 index 3 read fLanguage write fLanguage;
|
||||
property Description: RawUTF8 index 120 read fDescription write fDescription;
|
||||
property Copyright: RawUTF8 index 80 read fCopyright write fCopyright;
|
||||
property About: RawUTF8 read fAbout write fAbout;
|
||||
property Link: RawUTF8 index 60 read fLink write fLink;
|
||||
end;
|
||||
|
||||
TSQLRecordTimeStamped = class(TSQLRecord)
|
||||
@@ -46,6 +48,7 @@ type
|
||||
fHashedPassword: RawUTF8;
|
||||
fLogonName: RawUTF8;
|
||||
public
|
||||
function ComputeHash(const PlainPassword: RawUTF8): RawUTF8; virtual;
|
||||
procedure SetPlainPassword(const PlainPassword: RawUTF8);
|
||||
function CheckPlainPassword(const PlainPassword: RawUTF8): boolean;
|
||||
function Name: RawUTF8;
|
||||
@@ -96,6 +99,7 @@ type
|
||||
OrderID: TIntegerDynArray;
|
||||
procedure Init(aRest: TSQLRest);
|
||||
function Get(tagID: integer): RawUTF8;
|
||||
function GetIDFromIdent(const Ident: RawUTF8): integer;
|
||||
procedure SaveOccurence(aRest: TSQLRest);
|
||||
procedure SortTagsByIdent(var Tags: TIntegerDynArray);
|
||||
function GetAsDocVariantArray: Variant;
|
||||
@@ -106,6 +110,7 @@ type
|
||||
fAbstract: RawUTF8;
|
||||
fPublishedMonth: Integer;
|
||||
fTags: TIntegerDynArray;
|
||||
fLegacyHash: Int64;
|
||||
public
|
||||
class function CurrentPublishedMonth: Integer;
|
||||
class procedure InitializeTable(Server: TSQLRestServer; const FieldName: RawUTF8;
|
||||
@@ -118,6 +123,8 @@ type
|
||||
property Abstract: RawUTF8 read fAbstract write fAbstract;
|
||||
// "index 1" below to allow writing e.g. aArticle.DynArray(1).Delete(aIndex)
|
||||
property Tags: TIntegerDynArray index 1 read fTags write fTags;
|
||||
// xxhash32 of legacy post_url
|
||||
property LegacyHash: Int64 read fLegacyHash write fLegacyHash;
|
||||
end;
|
||||
|
||||
TSQLArticleSearch = class(TSQLRecordFTS4Porter)
|
||||
@@ -157,6 +164,8 @@ procedure DotClearFlatImport(Rest: TSQLRest; const aFlatFile: RawUTF8;
|
||||
var aTagsLookup: TSQLTags; const aDotClearRoot: RawUTF8;
|
||||
const aStaticFolder: TFileName);
|
||||
|
||||
function ComputeLegacyHash(url: PUTF8Char): cardinal;
|
||||
|
||||
|
||||
implementation
|
||||
|
||||
@@ -176,12 +185,16 @@ end;
|
||||
|
||||
{ TSQLSomeone }
|
||||
|
||||
const
|
||||
SALT = 'mORMot';
|
||||
function TSQLSomeone.ComputeHash(const PlainPassword: RawUTF8): RawUTF8;
|
||||
var dig: THash256;
|
||||
begin
|
||||
PBKDF2_SHA3(SHA3_224,PlainPassword,LogonName+'@mORMot',30,@dig);
|
||||
BinToHexLower(@dig,28,result);
|
||||
end;
|
||||
|
||||
function TSQLSomeone.CheckPlainPassword(const PlainPassword: RawUTF8): boolean;
|
||||
begin
|
||||
result := fHashedPassword=SHA256(SALT+LogonName+PlainPassword);
|
||||
result := fHashedPassword=ComputeHash(PlainPassword);
|
||||
end;
|
||||
|
||||
function TSQLSomeone.Name: RawUTF8;
|
||||
@@ -191,7 +204,7 @@ end;
|
||||
|
||||
procedure TSQLSomeone.SetPlainPassword(const PlainPassword: RawUTF8);
|
||||
begin
|
||||
fHashedPassword := SHA256(SALT+LogonName+PlainPassword);
|
||||
fHashedPassword := ComputeHash(PlainPassword);
|
||||
end;
|
||||
|
||||
|
||||
@@ -233,6 +246,8 @@ begin
|
||||
inherited;
|
||||
if (FieldName='') or (FieldName='PublishedMonth') then
|
||||
Server.CreateSQLIndex(TSQLArticle,'PublishedMonth',false);
|
||||
if (FieldName='') or (FieldName='LegacyHash') then
|
||||
Server.CreateSQLIndex(TSQLArticle,'LegacyHash',false);
|
||||
end;
|
||||
|
||||
procedure TSQLArticle.SetPublishedMonth(FromTime: TTimeLog);
|
||||
@@ -260,8 +275,20 @@ begin
|
||||
result := '';
|
||||
end;
|
||||
|
||||
function TSQLTags.GetIDFromIdent(const Ident: RawUTF8): integer;
|
||||
var i: PtrInt;
|
||||
begin
|
||||
if Ident<>'' then
|
||||
for i := 0 to length(Lookup)-1 do
|
||||
if IdemPropNameU(Lookup[i].Ident,Ident) then begin
|
||||
result := i+1;
|
||||
exit;
|
||||
end;
|
||||
result := 0;
|
||||
end;
|
||||
|
||||
function TSQLTags.GetAsDocVariantArray: Variant;
|
||||
var i,ndx: Integer;
|
||||
var i,ndx: PtrInt;
|
||||
begin
|
||||
TDocVariant.NewFast(result);
|
||||
with Lock.ProtectMethod do
|
||||
@@ -438,21 +465,46 @@ begin
|
||||
until P=nil;
|
||||
end;
|
||||
|
||||
function HttpGet(const aURI: SockString; outHeaders: PSockString=nil;
|
||||
forceNotSocket: boolean=false; outStatus: PInteger=nil): SockString;
|
||||
begin
|
||||
result := '';
|
||||
if outStatus<>nil then
|
||||
outStatus^ := 404;
|
||||
end;
|
||||
|
||||
function ComputeLegacyHash(url: PUTF8Char): cardinal;
|
||||
var c: ansichar;
|
||||
begin
|
||||
result := 0;
|
||||
if url<>nil then
|
||||
repeat
|
||||
case url^ of
|
||||
#0: exit;
|
||||
'a'..'z', 'A'..'Z', '0'..'9': begin
|
||||
c := upcase(url^);
|
||||
result := crc32c(result, @c, 1);
|
||||
end;
|
||||
end;
|
||||
inc(url);
|
||||
until false;
|
||||
end;
|
||||
|
||||
procedure DotClearFlatImport(Rest: TSQLRest; const aFlatFile: RawUTF8;
|
||||
var aTagsLookup: TSQLTags; const aDotClearRoot: RawUTF8;
|
||||
const aStaticFolder: TFileName);
|
||||
var T,tagTable,postTable: TDotClearTable;
|
||||
data,urls: TRawUTF8List;
|
||||
data: TRawUTF8List;
|
||||
urls: TIntegerDynArray;
|
||||
info: TSQLBlogInfo;
|
||||
article: TSQLArticle;
|
||||
comment: TSQLComment;
|
||||
tag: TSQLTag;
|
||||
tags: TRawUTF8DynArray;
|
||||
tags, notfound: TRawUTF8DynArray;
|
||||
tagID: TIDDynArray;
|
||||
tagsCount: integer;
|
||||
batch: TSQLRestBatch;
|
||||
PublicFolder: TFileName;
|
||||
notfound: TRawUTF8DynArray;
|
||||
r,ndx,post_url,meta_id,meta_type,tag_post_id,postID,post_id: integer;
|
||||
|
||||
function FixLinks(P: PUTF8Char): RawUTF8;
|
||||
@@ -501,13 +553,20 @@ var T,tagTable,postTable: TDotClearTable;
|
||||
continue;
|
||||
AddNoJSONEscape(B,H-B);
|
||||
P := H;
|
||||
if IdemPChar(P,'HTTP://BLOG.SYNOPSE.INFO/') then
|
||||
inc(P,24)
|
||||
else if IdemPChar(P,'HTTPS://BLOG.SYNOPSE.INFO/') then
|
||||
inc(P,25);
|
||||
if IdemPChar(P,'HTTP://SYNOPSE.INFO') then begin
|
||||
AddShort('https://synopse.info');
|
||||
inc(P,19);
|
||||
end else if P^='/' then begin
|
||||
if P[1]='?' then
|
||||
inc(P);
|
||||
if IdemPChar(P+1,'POST/') then begin
|
||||
GetUrl(P+6);
|
||||
i := urls.IndexOf(urlnoparam);
|
||||
i := IntegerScanIndex(pointer(urls),length(urls),
|
||||
ComputeLegacyHash(pointer(urlnoparam)));
|
||||
if i>=0 then begin
|
||||
AddShort('articleView?id=');
|
||||
Add(i+1);
|
||||
@@ -582,7 +641,6 @@ begin
|
||||
end;
|
||||
auto1 := TAutoFree.Several([
|
||||
@data,TDotClearTable.Parse(aFlatFile),
|
||||
@urls,TRawUTF8ListHashed.Create,
|
||||
@batch,TSQLRestBatch.Create(Rest,TSQLTag,5000)]);
|
||||
auto2 := TSQLRecord.AutoFree([ // avoid several try..finally
|
||||
@info,TSQLBlogInfo, @article,TSQLArticle, @comment,TSQLComment, @tag,TSQLTag]);
|
||||
@@ -614,7 +672,7 @@ begin
|
||||
post_url := postTable.FieldIndexExisting('post_url');
|
||||
if postTable.Step(true) then
|
||||
repeat
|
||||
urls.Add(postTable.FieldBuffer(post_url));
|
||||
AddInteger(urls,ComputeLegacyHash(postTable.FieldBuffer(post_url)));
|
||||
until not postTable.Step;
|
||||
article.Author := TSQLAuthor(1);
|
||||
article.AuthorName := 'synopse';
|
||||
@@ -631,6 +689,7 @@ begin
|
||||
article.ModifiedAt := Iso8601ToTimeLog(postTable.GetU(r,'post_upddt'));
|
||||
article.SetPublishedMonth(article.CreatedAt);
|
||||
postID := postTable.GetAsInteger(r,post_id);
|
||||
article.LegacyHash := ComputeLegacyHash(postTable.Get(r,post_url));
|
||||
article.Tags := nil;
|
||||
if tagTable.Step(true) then
|
||||
repeat
|
||||
|
@@ -38,17 +38,25 @@ var aModel: TSQLModel;
|
||||
aApplication: TBlogApplication;
|
||||
aHTTPServer: TSQLHttpServer;
|
||||
begin
|
||||
//with TSQLLog.Family do Level := LOG_VERBOSE;
|
||||
with TSQLLog.Family do begin
|
||||
Level := LOG_VERBOSE;
|
||||
PerThreadLog := ptIdentifiedInOnFile;
|
||||
RotateFileCount := 10;
|
||||
RotateFileSizeKB := 20 shl 10;
|
||||
FileExistsAction := acAppend; // as expected by rotation
|
||||
end;
|
||||
aModel := CreateModel;
|
||||
try
|
||||
aServer := TSQLRestServerDB.Create(aModel,ChangeFileExt(ExeVersion.ProgramFileName,'.db'));
|
||||
try
|
||||
aServer.DB.Synchronous := smNormal;
|
||||
aServer.DB.LockingMode := lmExclusive;
|
||||
aServer.Options := aServer.Options+[rsoNoTableURI];
|
||||
aServer.CreateMissingTables;
|
||||
aApplication := TBlogApplication.Create;
|
||||
try
|
||||
aApplication.Start(aServer);
|
||||
aServer.ServiceMethodRegisterPublishedMethods('', aApplication);
|
||||
aHTTPServer := TSQLHttpServer.Create('8092',aServer
|
||||
{$ifndef ONLYUSEHTTPSOCKET},'+',useHttpApiRegisteringURI{$endif});
|
||||
try
|
||||
|
@@ -12,6 +12,7 @@ uses
|
||||
SynCommons,
|
||||
SynLog,
|
||||
SynTests,
|
||||
SynCrtSock,
|
||||
mORMot,
|
||||
mORMotMVC,
|
||||
MVCModel;
|
||||
@@ -31,8 +32,9 @@ type
|
||||
out Comments: TObjectList);
|
||||
procedure AuthorView(
|
||||
var ID: TID; out Author: TSQLAuthor; out Articles: variant);
|
||||
function Login(
|
||||
const LogonName,PlainPassword: RawUTF8): TMVCAction;
|
||||
procedure LoginView;
|
||||
function Login(const LogonName,PlainPassword,
|
||||
NewPlainPassword1,NewPlainPassword2: RawUTF8): TMVCAction;
|
||||
function Logout: TMVCAction;
|
||||
function ArticleComment(ID: TID; const Title,Comment: RawUTF8): TMVCAction;
|
||||
function ArticleMatch(const Match: RawUTF8): TMVCAction;
|
||||
@@ -71,7 +73,13 @@ type
|
||||
procedure TagToText(const Value: variant; out result: variant);
|
||||
public
|
||||
procedure Start(aServer: TSQLRestServer); reintroduce;
|
||||
published
|
||||
// low-level blog/post blog/tag blog/rss endpoints
|
||||
procedure Post(Ctxt: TSQLRestServerURIContext);
|
||||
procedure Tag(Ctxt: TSQLRestServerURIContext);
|
||||
procedure Rss(Ctxt: TSQLRestServerURIContext);
|
||||
public
|
||||
// IBlogApplication implemented methods
|
||||
procedure Default(var Scope: variant);
|
||||
procedure ArticleView(ID: TID;
|
||||
var WithComments: boolean; Direction: integer; var Scope: variant;
|
||||
@@ -79,7 +87,9 @@ type
|
||||
out Comments: TObjectList);
|
||||
procedure AuthorView(
|
||||
var ID: TID; out Author: TSQLAuthor; out Articles: variant);
|
||||
function Login(const LogonName,PlainPassword: RawUTF8): TMVCAction;
|
||||
procedure LoginView;
|
||||
function Login(const LogonName,PlainPassword,
|
||||
NewPlainPassword1,NewPlainPassword2: RawUTF8): TMVCAction;
|
||||
function Logout: TMVCAction;
|
||||
function ArticleComment(ID: TID; const Title,Comment: RawUTF8): TMVCAction;
|
||||
function ArticleMatch(const Match: RawUTF8): TMVCAction;
|
||||
@@ -130,6 +140,10 @@ begin
|
||||
finally
|
||||
Free;
|
||||
end;
|
||||
_Safe(fBlogMainInfo)^.AddValue('engine',RawUTF8ToVariant(
|
||||
'Website powered by mORMot MVC '+SYNOPSE_FRAMEWORK_VERSION+
|
||||
', compiled with '+GetDelphiCompilerVersion+
|
||||
', running on '+RawUTF8(ToText(OSVersion32))+'.'));
|
||||
end;
|
||||
|
||||
procedure TBlogApplication.MonthToText(const Value: variant;
|
||||
@@ -172,29 +186,28 @@ begin
|
||||
auto := TSQLRecord.AutoFree([ // avoid several try..finally
|
||||
@info,TSQLBlogInfo, @article,TSQLArticle, @comment,TSQLComment, @tag,TSQLTag]);
|
||||
if not RestModel.Retrieve('',info) then begin // retrieve first item
|
||||
tmp := StringFromFile('/home/ab/Downloads/2020-06-16-a8003957c2ae6bde5be6ea279c9c9ce4-backup.txt');
|
||||
tmp := StringFromFile(ExeVersion.ProgramFilePath+'2021-01-20-16-37-default-backup.txt');
|
||||
info.Language := 'en';
|
||||
if tmp<>'' then begin
|
||||
info.Title := 'Synopse Blog';
|
||||
info.Description := 'Articles, announcements, news, updates and more '+
|
||||
'about our Open Source projects';
|
||||
'about Synopse Open Source projects';
|
||||
info.About := 'Latest information about Synopse Open Source librairies, '+
|
||||
'mainly the mORMot ORM/SOA/MVC framework, and SynPDF.';
|
||||
info.Link := 'https://blog.synopse.info';
|
||||
end else begin
|
||||
info.Title := 'mORMot BLOG';
|
||||
info.Description := 'Sample Blog Web Application using Synopse mORMot MVC';
|
||||
info.About := TSynTestCase.RandomTextParagraph(10,'!');
|
||||
info.Link := 'http://localhost:8092';
|
||||
end;
|
||||
info.About := info.About+#13#10'Website powered by mORMot MVC '+
|
||||
SYNOPSE_FRAMEWORK_VERSION+', compiled with '+GetDelphiCompilerVersion+
|
||||
', running on '+ToText(OSVersion32)+'.';
|
||||
info.Copyright := '©'+ToUTF8(CurrentYear)+'<a href=https://synopse.info>Synopse Informatique</a>';
|
||||
RestModel.Add(info,true);
|
||||
end;
|
||||
if RestModel.TableHasRows(TSQLArticle) then
|
||||
exit;
|
||||
if tmp<>'' then begin
|
||||
DotClearFlatImport(RestModel,tmp,fTagsLookup,'http://blog.synopse.info',
|
||||
DotClearFlatImport(RestModel,tmp,fTagsLookup,'https://blog.synopse.info',
|
||||
(TMVCRunOnRestServer(fMainRunner).Views as TMVCViewsMustache).ViewStaticFolder);
|
||||
exit;
|
||||
end;
|
||||
@@ -271,20 +284,24 @@ begin
|
||||
fDefaultData.AddNewProp('tags',fTagsLookup.GetAsDocVariantArray,info);
|
||||
end;
|
||||
|
||||
procedure TBlogApplication.FlushAnyCache;
|
||||
begin
|
||||
inherited FlushAnyCache; // call fMainRunner.NotifyContentChanged
|
||||
fDefaultData.Clear;
|
||||
end;
|
||||
|
||||
|
||||
{ TBlogApplication - Commands }
|
||||
|
||||
const
|
||||
ARTICLE_FIELDS = 'RowID,Title,Tags,Abstract,ContentHtml,Author,AuthorName,CreatedAt';
|
||||
ARTICLE_DEFAULT_LIMIT = ' limit 20';
|
||||
ARTICLE_DEFAULT_ORDER: RawUTF8 = 'order by RowID desc'+ARTICLE_DEFAULT_LIMIT;
|
||||
|
||||
procedure TBlogApplication.FlushAnyCache;
|
||||
begin
|
||||
inherited FlushAnyCache; // call fMainRunner.NotifyContentChanged
|
||||
fDefaultData.Clear;
|
||||
// get last 20 articles
|
||||
fDefaultData.SetValue('Articles',
|
||||
RestModel.RetrieveDocVariantArray(TSQLArticle,'',
|
||||
ARTICLE_DEFAULT_ORDER,[],ARTICLE_FIELDS,nil,@fDefaultLastID));
|
||||
end;
|
||||
|
||||
|
||||
{ TBlogApplication - Commands }
|
||||
|
||||
procedure TBlogApplication.Default(var Scope: variant);
|
||||
var scop: PDocVariantData;
|
||||
lastID: TID;
|
||||
@@ -320,14 +337,12 @@ begin
|
||||
whereClause := whereClause+' and IntegerDynArrayContains(Tags,?)';
|
||||
end;
|
||||
SetVariantNull(Scope);
|
||||
if (lastID=0) and (tag=0) then begin // use simple cache if no parameters
|
||||
if not fDefaultData.AddExistingProp('Articles',Scope) then begin
|
||||
articles := RestModel.RetrieveDocVariantArray(TSQLArticle,'',
|
||||
ARTICLE_DEFAULT_ORDER,[],ARTICLE_FIELDS,nil,@fDefaultLastID);
|
||||
fDefaultData.AddNewProp('Articles',articles,Scope);
|
||||
end;
|
||||
if (lastID=0) and (tag=0) then begin
|
||||
// use simple cache if no parameters
|
||||
fDefaultData.AddExistingProp('Articles',Scope); // set by FlushAnyCache
|
||||
lastID := fDefaultLastID;
|
||||
end else begin // use more complex request using lastID + tag parameters
|
||||
end else begin
|
||||
// use more complex request using lastID + tag parameters
|
||||
articles := RestModel.RetrieveDocVariantArray(TSQLArticle,'',
|
||||
whereClause+ARTICLE_DEFAULT_ORDER,[lastID,tag],ARTICLE_FIELDS,nil,@lastID);
|
||||
scope := _ObjFast(['Articles',articles]);
|
||||
@@ -370,12 +385,31 @@ begin
|
||||
raise EMVCApplication.CreateGotoError(HTTP_NOTFOUND);
|
||||
end;
|
||||
|
||||
function TBlogApplication.Login(const LogonName, PlainPassword: RawUTF8): TMVCAction;
|
||||
procedure TBlogApplication.LoginView;
|
||||
begin
|
||||
end;
|
||||
|
||||
function TBlogApplication.Login(const LogonName, PlainPassword,
|
||||
NewPlainPassword1, NewPlainPassword2: RawUTF8): TMVCAction;
|
||||
var Author: TSQLAuthor;
|
||||
SessionInfo: TCookieData;
|
||||
newpwd: RawUTF8;
|
||||
begin
|
||||
if CurrentSession.CheckAndRetrieve<>0 then begin
|
||||
GotoError(result,HTTP_BADREQUEST);
|
||||
if LogonName='' then begin
|
||||
GotoView(result,'LoginView',[]);
|
||||
exit;
|
||||
end;
|
||||
newpwd := Trim(NewPlainPassword1);
|
||||
if newpwd<>'' then begin
|
||||
if (newpwd<>NewPlainPassword2) or
|
||||
(newpwd=PlainPassword) or
|
||||
(CurrentSession.CheckAndRetrieve(@SessionInfo,TypeInfo(TCookieData))=0) or
|
||||
(SessionInfo.AuthorName<>LogonName) then begin
|
||||
GotoError(result,HTTP_NOTACCEPTABLE);
|
||||
exit;
|
||||
end;
|
||||
end else if CurrentSession.CheckAndRetrieve<>0 then begin
|
||||
GotoError(result,'Already Logged In',HTTP_BADREQUEST);
|
||||
exit;
|
||||
end;
|
||||
Author := TSQLAuthor.Create(RestModel,'LogonName=?',[LogonName]);
|
||||
@@ -385,6 +419,10 @@ begin
|
||||
SessionInfo.AuthorID := Author.ID;
|
||||
SessionInfo.AuthorRights := Author.Rights;
|
||||
CurrentSession.Initialize(@SessionInfo,TypeInfo(TCookieData));
|
||||
if newpwd<>'' then begin
|
||||
Author.SetPlainPassword(newpwd);
|
||||
RestModel.Update(Author,'HashedPassword');
|
||||
end;
|
||||
GotoDefault(result);
|
||||
end else
|
||||
GotoError(result,sErrorInvalidLogin);
|
||||
@@ -481,6 +519,83 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TBlogApplication.Post(Ctxt: TSQLRestServerURIContext);
|
||||
var hash, id: Int64;
|
||||
begin
|
||||
hash := ComputeLegacyHash(pointer(UrlDecode(Ctxt.URIAfterRoot,5,-1)));
|
||||
id := RestModel.OneFieldValueInt64(TSQLArticle,'ID',
|
||||
FormatUTF8('LegacyHash=:(%):', [hash]));
|
||||
Ctxt.Redirect(FormatUTF8('/%/articleview?id=%',[RestModel.Model.Root,id]));
|
||||
end;
|
||||
|
||||
procedure TBlogApplication.Tag(Ctxt: TSQLRestServerURIContext);
|
||||
var
|
||||
id: integer;
|
||||
begin
|
||||
id := fTagsLookup.GetIDFromIdent(copy(Ctxt.UriAfterRoot, 5, 100));
|
||||
Ctxt.Redirect(FormatUTF8('/%/default?scope={tag:%}',[RestModel.Model.Root,id]));
|
||||
end;
|
||||
|
||||
function Esc(const Msg: RawUTF8): RawUTF8;
|
||||
var i: integer;
|
||||
ins: RawUTF8;
|
||||
begin
|
||||
// fast enough for our purpose to compute some RSS cache
|
||||
result := Msg;
|
||||
for i := length(Msg) downto 1 do begin
|
||||
case Msg[i] of
|
||||
'"': ins := '"';
|
||||
'&': ins := '&';
|
||||
'<': ins := '<';
|
||||
'>': ins := '>';
|
||||
else Continue;
|
||||
end;
|
||||
result[i] := ';';
|
||||
insert(ins,result,i);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TBlogApplication.Rss(Ctxt: TSQLRestServerURIContext);
|
||||
function ComputeRss: variant;
|
||||
var xml, lng, link: RawUTF8;
|
||||
art: integer;
|
||||
begin
|
||||
with _Safe(fBlogMainInfo)^ do
|
||||
begin
|
||||
link := U['Link'];
|
||||
if (link<>'') and (link[length(link)]='/') then
|
||||
SetLength(link,length(link)-1);
|
||||
lng := U['Language'];
|
||||
if lng='' then
|
||||
lng := 'en_US';
|
||||
FormatUTF8('<?xml version="1.0" encoding="UTF-8"?><rss version="2.0">'+
|
||||
'<channel><title>%</title>'+
|
||||
'<link>%</link><description>%</description>'+
|
||||
'<lastBuildDate>%</lastBuildDate><language>%</language>',
|
||||
[Esc(U['Title']),link,Esc(U['Description']),
|
||||
DateTimeToHTTPDate(NowUTC,'+0000'),lng],xml);
|
||||
end;
|
||||
with _Safe(fDefaultData.GetValue('Articles'))^ do
|
||||
for art := 0 to Count-1 do
|
||||
with _Safe(Values[art])^ do
|
||||
xml := FormatUTF8('%'#13'<item><title>%</title>'+
|
||||
'<link>%/articleview?id=%</link><pubDate>%</pubDate><category>blog</category>'+
|
||||
'<dc:creator>%</dc:creator><description><![CDATA[%]]></description>'+
|
||||
'<content:encoded><![CDATA[%]]></content:encoded></item>',
|
||||
[xml,Esc(U['Title']),link,I['ID'],
|
||||
DateTimeToHTTPDate(TimeLogToDateTime(I['CreatedAt']),'+0000'),
|
||||
Esc(U['AuthorName']),U['Abstract'],U['Content']]);
|
||||
RawUTF8ToVariant(xml+'</channel></rss>',result);
|
||||
end;
|
||||
var
|
||||
rss: variant;
|
||||
begin
|
||||
if not fDefaultData.ExistsOrLock('rss',rss) then
|
||||
fDefaultData.ReplaceAndUnlock('rss',ComputeRss,rss);
|
||||
Ctxt.Returns(ToUTF8(rss),HTTP_SUCCESS,
|
||||
HEADER_CONTENT_TYPE+'application/rss+xml; charset=UTF-8',{handle304=}true);
|
||||
end;
|
||||
|
||||
initialization
|
||||
{$ifndef DELPHI2010}
|
||||
// manual definition mandatory only if Delphi 2010 RTTI is not available
|
||||
|
@@ -1,51 +1,51 @@
|
||||
{{>header}}
|
||||
{{>masthead}}
|
||||
<div class="blog-header">
|
||||
<h1 class="blog-title">{{article.title}}</h1>
|
||||
<div class="lead blog-description">
|
||||
Written by <a href="authorView?id={{article.Author}}">{{article.AuthorName}}</a> ({{author.FirstName}} {{author.FamilyName}}) on {{TimeLogToText article.CreatedAt}}<br />
|
||||
{{#article.tags}}<a href="default?scope={tag:{{.}}}" class="label label-info">{{TagToText .}}</a> {{/article.tags}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-8 blog-main">
|
||||
{{#article}}
|
||||
{{#ContentHtml}}{{{abstract}}}{{/ContentHtml}}{{^ContentHtml}}{{{WikiToHtml abstract}}}{{/ContentHtml}}
|
||||
<hr>
|
||||
{{#ContentHtml}}{{{content}}}{{/ContentHtml}}{{^ContentHtml}}{{{WikiToHtml content}}}{{/ContentHtml}}
|
||||
{{/article}}
|
||||
<hr>
|
||||
<ul class="pager">
|
||||
<li class="previous"><a href="ArticleView?id={{Article.ID}}&withComments={{withComments}}&direction=1">← Previous</a></li>
|
||||
<li class="next"><a href="ArticleView?id={{Article.ID}}&withComments={{withComments}}&direction=2">Next →</a></li>
|
||||
</ul>
|
||||
<a name="comments"></a>
|
||||
{{#WithComments}}
|
||||
{{#Comments}}
|
||||
<blockquote>
|
||||
<p><strong>{{Title}}</strong></p>
|
||||
<p>{{#ContentHtml}}{{{Content}}}{{/ContentHtml}}{{^ContentHtml}}{{{WikiToHtml Content}}}{{/ContentHtml}}</p>
|
||||
<footer>Commented on {{TimeLogToText CreatedAt}} by <a href="authorView?id={{Author}}">{{AuthorName}}</a></<footer>
|
||||
</blockquote>
|
||||
<hr>
|
||||
{{/Comments}}
|
||||
{{^Comments}}<blockquote>No comment yet.</blockquote>{{/Comments}}
|
||||
<p><a href="ArticleView?id={{Article.ID}}#comments" class="btn btn-primary btn-sm">Hide Comments</a></p>
|
||||
{{#main.session.AuthorRights.Comment}}
|
||||
<a name="addComment"></a>
|
||||
<form class="form-horizontal" action="ArticleComment#addComment" method="post">
|
||||
<div class="form-group">
|
||||
{{#Scope}}<div class="alert alert-danger">{{CommentError}}</div>{{/Scope}}
|
||||
<input type="hidden" name="id" value={{Article.ID}}>
|
||||
<input type="text" class="form-control" name="title" placeholder="Title" value="{{Scope.CommentTitle}}">
|
||||
<textarea class="form-control" name="comment" rows="7" placeholder="Enter a new comment here">{{Scope.CommentContent}}</textarea>
|
||||
<button type="submit" class="btn btn-primary btn-sm">Add Comment</a>
|
||||
</div>
|
||||
</form>
|
||||
{{/main.session.AuthorRights.Comment}}
|
||||
</p>
|
||||
{{/WithComments}}
|
||||
{{^WithComments}}
|
||||
<p><a href="ArticleView?id={{Article.ID}}&withComments=true#comments" class="btn btn-primary btn-sm">Show Comments</a></p>
|
||||
{{/WithComments}}
|
||||
<div class="blog-header">
|
||||
<h1 class="blog-title">{{article.title}}</h1>
|
||||
<div class="lead blog-description">
|
||||
Written by <a href="authorView?id={{article.Author}}">{{article.AuthorName}}</a> ({{author.FirstName}} {{author.FamilyName}}) on {{TimeLogToText article.CreatedAt}}<br />
|
||||
{{#article.tags}}<a href="default?scope={tag:{{.}}}" class="label label-info">{{TagToText .}}</a> {{/article.tags}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-8 blog-main">
|
||||
{{#article}}
|
||||
{{#ContentHtml}}{{{abstract}}}{{/ContentHtml}}{{^ContentHtml}}{{{WikiToHtml abstract}}}{{/ContentHtml}}
|
||||
<hr>
|
||||
{{#ContentHtml}}{{{content}}}{{/ContentHtml}}{{^ContentHtml}}{{{WikiToHtml content}}}{{/ContentHtml}}
|
||||
{{/article}}
|
||||
<hr>
|
||||
<ul class="pager">
|
||||
<li class="previous"><a href="ArticleView?id={{Article.ID}}&withComments={{withComments}}&direction=1">← Previous</a></li>
|
||||
<li class="next"><a href="ArticleView?id={{Article.ID}}&withComments={{withComments}}&direction=2">Next →</a></li>
|
||||
</ul>
|
||||
<a name="comments"></a>
|
||||
{{#WithComments}}
|
||||
{{#Comments}}
|
||||
<blockquote>
|
||||
<p><strong>{{Title}}</strong></p>
|
||||
<p>{{#ContentHtml}}{{{Content}}}{{/ContentHtml}}{{^ContentHtml}}{{{WikiToHtml Content}}}{{/ContentHtml}}</p>
|
||||
<footer>Commented on {{TimeLogToText CreatedAt}} by <a href="authorView?id={{Author}}">{{AuthorName}}</a></<footer>
|
||||
</blockquote>
|
||||
<hr>
|
||||
{{/Comments}}
|
||||
{{^Comments}}<blockquote>No comment yet.</blockquote>{{/Comments}}
|
||||
<p><a href="ArticleView?id={{Article.ID}}#comments" class="btn btn-primary btn-sm">Hide Comments</a></p>
|
||||
{{#main.session.AuthorRights.Comment}}
|
||||
<a name="addComment"></a>
|
||||
<form class="form-horizontal" action="ArticleComment#addComment" method="post">
|
||||
<div class="form-group">
|
||||
{{#Scope}}<div class="alert alert-danger">{{CommentError}}</div>{{/Scope}}
|
||||
<input type="hidden" name="id" value={{Article.ID}}>
|
||||
<input type="text" class="form-control" name="title" placeholder="Title" value="{{Scope.CommentTitle}}">
|
||||
<textarea class="form-control" name="comment" rows="7" placeholder="Enter a new comment here">{{Scope.CommentContent}}</textarea>
|
||||
<button type="submit" class="btn btn-primary btn-sm">Add Comment</a>
|
||||
</div>
|
||||
</form>
|
||||
{{/main.session.AuthorRights.Comment}}
|
||||
</p>
|
||||
{{/WithComments}}
|
||||
{{^WithComments}}
|
||||
<p><a href="ArticleView?id={{Article.ID}}&withComments=true#comments" class="btn btn-primary btn-sm">Show Comments</a></p>
|
||||
{{/WithComments}}
|
||||
{{>footer}}
|
@@ -1,15 +1,25 @@
|
||||
{{>header}}
|
||||
{{>masthead}}
|
||||
<div class="blog-header">
|
||||
<h1 class="blog-title">User {{Author.LogonName}}</h1>
|
||||
<div class="lead blog-description">{{Author.FirstName}} {{Author.FamilyName}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-8 panel">
|
||||
<div class="panel-heading">Information about <strong>{{Author.LogonName}}</strong></div>
|
||||
<div class="panel-body">
|
||||
{{{TSQLAuthor.HtmlTable Author}}}
|
||||
</div>
|
||||
<div class="blog-header">
|
||||
<h1 class="blog-title">User {{Author.LogonName}}</h1>
|
||||
<div class="lead blog-description">{{Author.FirstName}} {{Author.FamilyName}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-8 panel">
|
||||
<div class="panel-heading">Information about <strong>{{Author.LogonName}}</strong></div>
|
||||
<div class="panel-body">
|
||||
{{{TSQLAuthor.HtmlTable Author}}}
|
||||
{{#main.session}}
|
||||
<form class="navbar-form" action="login" method="post">
|
||||
<span class="navbar-text">Change <strong>{{AuthorName}}</strong> Password:</span>
|
||||
<input type="text" class="form-control" name="LogonName" placeholder="Username">
|
||||
<input type="password" class="form-control" name="PlainPassword" placeholder="Old Password">
|
||||
<input type="password" class="form-control" name="NewPlainPassword1" placeholder="New Password">
|
||||
<input type="password" class="form-control" name="NewPlainPassword2" placeholder="Repeat New Password">
|
||||
<button class="btn" type="submit">Change</button>
|
||||
</form>
|
||||
{{/main.session}}
|
||||
</div>
|
||||
{{>articlerow}}
|
||||
{{>footer}}
|
@@ -22,8 +22,23 @@
|
||||
</div><!-- container -->
|
||||
<div class="blog-footer">
|
||||
<p>Proudly using the Open Source <a href="http://mormot.net">mORMot ORM/SOA/MVC Framework</a>.<br>
|
||||
{{{main.blog.engine}}}<br>
|
||||
<small>{{{main.blog.copyright}}} - page generated in [[GENERATION_TIME_TAG]]</small></p>
|
||||
<p><a href="#">Back to top</a></p>
|
||||
<ul class="pager">
|
||||
<li><a href="#">Back to top</a></li>
|
||||
<li><a href=rss>RSS feed</a></li>
|
||||
<li><a href=https://synopse.info/forum/viewtopic.php?id=25>Synopse</a></li>
|
||||
<li><a href=https://synopse.info/forum>Support Forum</a></li>
|
||||
<li><a href=https://github.com/synopse>On Github</a></li>
|
||||
</ul>
|
||||
{{#main.session}}
|
||||
<p>
|
||||
<form class="navbar-form" action="logout">
|
||||
<span class="navbar-text">Signed in as <a href=AuthorView?id={{AuthorID}}>{{AuthorName}}</a></span>
|
||||
<button class="btn" type="submit">Logout</button>
|
||||
</form>
|
||||
</p>
|
||||
{{/main.session}}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@@ -3,44 +3,23 @@
|
||||
<div class="blog-masthead">
|
||||
<div class="navbar navbar-default navbar-fixed-top">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button class="navbar-toggle" type="button" data-toggle="collapse" data-target="#navbar-main">
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<div class="navbar-left">
|
||||
<a class="navbar-brand" href="default">{{main.blog.title}}</a>
|
||||
</div>
|
||||
<div class="navbar-form navbar-right">
|
||||
<form action="articleMatch" method="post">
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" name="match" placeholder="Search Expression">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<center>
|
||||
<div class="navbar-collapse collapse" id="navbar-main">
|
||||
{{#main.session}}
|
||||
<div class="navbar-right">
|
||||
<form class="navbar-form" action="logout">
|
||||
<span class="navbar-text">Signed in as <a href=AuthorView?id={{AuthorID}}>{{AuthorName}}</a></span>
|
||||
<button class="btn" type="submit">Logout</button>
|
||||
</form>
|
||||
</div>
|
||||
{{/main.session}}
|
||||
{{^main.session}}
|
||||
<form class="navbar-form navbar-right" action="login" method="post">
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" name="LogonName" placeholder="Username">
|
||||
<input type="password" class="form-control" name="PlainPassword" placeholder="Password">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-default">Sign In</button>
|
||||
</form>
|
||||
{{/main.session}}
|
||||
</div>
|
||||
</center>
|
||||
<div class="navbar-header">
|
||||
<button class="navbar-toggle" type="button" data-toggle="collapse" data-target="#navbar-main">
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<div class="navbar-left">
|
||||
<a class="navbar-brand" href="default">{{main.blog.title}}</a>
|
||||
</div>
|
||||
<div class="navbar-form navbar-right">
|
||||
<form action="articleMatch" method="post">
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" name="match" placeholder="Search Expression">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -8,6 +8,7 @@ uses
|
||||
SysUtils,
|
||||
Classes,
|
||||
SynCommons,
|
||||
SynTable,
|
||||
mORMot,
|
||||
mORMotHttpClient,
|
||||
Project31ChatCallbackInterface in 'Project31ChatCallbackInterface.pas';
|
||||
|
@@ -3,7 +3,7 @@ unit ECCProcess;
|
||||
(*
|
||||
This file is part of Synopse framework.
|
||||
|
||||
Synopse framework. Copyright (C) 2020 Arnaud Bouchez
|
||||
Synopse framework. Copyright (C) 2022 Arnaud Bouchez
|
||||
Synopse Informatique - https://synopse.info
|
||||
|
||||
*** BEGIN LICENSE BLOCK *****
|
||||
@@ -22,7 +22,7 @@ unit ECCProcess;
|
||||
|
||||
The Initial Developer of the Original Code is Arnaud Bouchez.
|
||||
|
||||
Portions created by the Initial Developer are Copyright (C) 2020
|
||||
Portions created by the Initial Developer are Copyright (C) 2022
|
||||
the Initial Developer. All Rights Reserved.
|
||||
|
||||
Contributor(s):
|
||||
|
@@ -28,7 +28,7 @@ uses
|
||||
BaseUnix,
|
||||
{$endif}
|
||||
{$ifdef LINUXNOTBSD}
|
||||
SynSystemd,
|
||||
SynFPCLinux,
|
||||
mORMotService,
|
||||
{$endif}
|
||||
mORMotHttpServer; // HTTP server for RESTful server
|
||||
@@ -163,12 +163,12 @@ const
|
||||
UNIX_SOCK_PATH = '/tmp/rest-bench.socket';
|
||||
|
||||
{$ifdef LINUX}
|
||||
/// killa process after X second without GEt requests
|
||||
/// killa process after X second without Get requests
|
||||
function inactivityWatchdog(p: pointer): ptrint;
|
||||
var currentRC: TSynMonitorCount64;
|
||||
begin
|
||||
repeat
|
||||
sleep(10000); /// once per 10 second
|
||||
sleep(10000); // once per 10 second
|
||||
if aRestServer = nil then // not initialized
|
||||
continue;
|
||||
currentRC := aRestServer.Stats.Read;
|
||||
@@ -182,6 +182,7 @@ begin
|
||||
Result := 0;
|
||||
end;
|
||||
{$endif}
|
||||
|
||||
begin
|
||||
// set logging abilities
|
||||
SQLite3Log.Family.Level := LOG_VERBOSE;
|
||||
@@ -190,10 +191,10 @@ begin
|
||||
SQLite3Log.Family.NoFile := true; // do not create log files for benchmark
|
||||
{$ifdef UNIX}
|
||||
{$ifdef LINUXNOTBSD}
|
||||
if SynSystemd.ProcessIsStartedBySystemd then begin
|
||||
if ProcessIsStartedBySystemd then begin
|
||||
SQLite3Log.Family.EchoToConsole := SQLite3Log.Family.Level;
|
||||
SQLite3Log.Family.EchoToConsoleUseJournal := true;
|
||||
if sd.listen_fds(0) = 1 then
|
||||
if ExternalLibraries.sd_listen_fds(0) = 1 then
|
||||
url := '' // force to use socket passed by systemd
|
||||
else
|
||||
url := '8888';
|
||||
|
@@ -43,6 +43,15 @@
|
||||
<OtherUnitFiles Value="../..;../../.."/>
|
||||
<UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
|
||||
</SearchPaths>
|
||||
<CodeGeneration>
|
||||
<SmartLinkUnit Value="True"/>
|
||||
</CodeGeneration>
|
||||
<Linking>
|
||||
<Debugging>
|
||||
<UseExternalDbgSyms Value="True"/>
|
||||
</Debugging>
|
||||
<LinkSmart Value="True"/>
|
||||
</Linking>
|
||||
</CompilerOptions>
|
||||
<Debugging>
|
||||
<Exceptions Count="1">
|
||||
|
@@ -7,7 +7,7 @@ unit SynJSONTreeView;
|
||||
{
|
||||
This file is part of Synopse framework.
|
||||
|
||||
Synopse framework. Copyright (C) 2020 Arnaud Bouchez
|
||||
Synopse framework. Copyright (C) 2022 Arnaud Bouchez
|
||||
Synopse Informatique - https://synopse.info
|
||||
|
||||
*** BEGIN LICENSE BLOCK *****
|
||||
@@ -26,7 +26,7 @@ unit SynJSONTreeView;
|
||||
|
||||
The Initial Developer of the Original Code is Arnaud Bouchez.
|
||||
|
||||
Portions created by the Initial Developer are Copyright (C) 2020
|
||||
Portions created by the Initial Developer are Copyright (C) 2022
|
||||
the Initial Developer. All Rights Reserved.
|
||||
|
||||
Contributor(s):
|
||||
|
@@ -6,7 +6,7 @@ unit SynRestMidasVCL;
|
||||
{
|
||||
This file is part of Synopse framework.
|
||||
|
||||
Synopse framework. Copyright (C) 2020 Arnaud Bouchez
|
||||
Synopse framework. Copyright (C) 2022 Arnaud Bouchez
|
||||
Synopse Informatique - https://synopse.info
|
||||
|
||||
*** BEGIN LICENSE BLOCK *****
|
||||
@@ -25,7 +25,7 @@ unit SynRestMidasVCL;
|
||||
|
||||
The Initial Developer of the Original Code is Arnaud Bouchez.
|
||||
|
||||
Portions created by the Initial Developer are Copyright (C) 2020
|
||||
Portions created by the Initial Developer are Copyright (C) 2022
|
||||
the Initial Developer. All Rights Reserved.
|
||||
|
||||
Contributor(s):
|
||||
|
@@ -6,7 +6,7 @@ unit SynRestVCL;
|
||||
{
|
||||
This file is part of Synopse framework.
|
||||
|
||||
Synopse framework. Copyright (C) 2020 Arnaud Bouchez
|
||||
Synopse framework. Copyright (C) 2022 Arnaud Bouchez
|
||||
Synopse Informatique - https://synopse.info
|
||||
|
||||
*** BEGIN LICENSE BLOCK *****
|
||||
@@ -25,7 +25,7 @@ unit SynRestVCL;
|
||||
|
||||
The Initial Developer of the Original Code is Arnaud Bouchez.
|
||||
|
||||
Portions created by the Initial Developer are Copyright (C) 2020
|
||||
Portions created by the Initial Developer are Copyright (C) 2022
|
||||
the Initial Developer. All Rights Reserved.
|
||||
|
||||
Contributor(s):
|
||||
|
@@ -7,7 +7,7 @@ unit mORMotRESTFPCInterfaces;
|
||||
This unit has been generated by a mORMot 1.18.2797 server.
|
||||
Any manual modification of this file may be lost after regeneration.
|
||||
|
||||
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
|
||||
Synopse mORMot framework. Copyright (C) 2022 Arnaud Bouchez
|
||||
Synopse Informatique - https://synopse.info
|
||||
|
||||
This unit is released under a MPL/GPL/LGPL tri-license,
|
||||
|
@@ -1,10 +1,57 @@
|
||||
{$IF (CompilerVersion <= 25)}
|
||||
type
|
||||
//other name of constants in XE4
|
||||
TStyledSettingHelper = record helper for TStyledSetting
|
||||
const Family = TStyledSetting.ssFamily;
|
||||
const Size = TStyledSetting.ssSize;
|
||||
const Style = TStyledSetting.ssStyle;
|
||||
const FontColor = TStyledSetting.ssFontColor;
|
||||
const Other = TStyledSetting.ssOther;
|
||||
end;
|
||||
|
||||
TTextAlignHelper = record helper for TTextAlign
|
||||
const Center = TTextAlign.taCenter;
|
||||
const Leading = TTextAlign.taLeading;
|
||||
const Trailing = TTextAlign.taTrailing;
|
||||
end;
|
||||
|
||||
TFmxFormBorderStyleHelper = record helper for TFmxFormBorderStyle
|
||||
const None = TFmxFormBorderStyle.bsNone;
|
||||
const Single = TFmxFormBorderStyle.bsSingle;
|
||||
const Sizeable = TFmxFormBorderStyle.bsSizeable;
|
||||
const ToolWindow = TFmxFormBorderStyle.bsToolWindow;
|
||||
const SizeToolWin = TFmxFormBorderStyle.bsSizeToolWin;
|
||||
end;
|
||||
|
||||
TFormPositionHelper = record helper for TFormPosition
|
||||
//(poDesigned, poDefault, poDefaultPosOnly, poDefaultSizeOnly,
|
||||
//poScreenCenter, poDesktopCenter, poMainFormCenter, poOwnerFormCenter);
|
||||
const OwnerFormCenter = TFormPosition.poOwnerFormCenter;
|
||||
const ScreenCenter = TFormPosition.poScreenCenter;
|
||||
end;
|
||||
|
||||
TBrushKindHelper = record helper for TBrushKind
|
||||
//(bkNone, bkSolid, bkGradient, bkBitmap, bkResource);
|
||||
const None = TBrushKind.bkNone;
|
||||
end;
|
||||
|
||||
TAlignLayoutHelper = record helper for TAlignLayout
|
||||
//(alNone, alTop, alLeft, alRight, alBottom, alMostTop, alMostBottom, alMostLeft, alMostRight, alClient,
|
||||
//alContents, alCenter, alVertCenter, alHorzCenter, alHorizontal, alVertical, alScale, alFit, alFitLeft, alFitRight);
|
||||
const Top = TAlignLayout.alTop;
|
||||
end;
|
||||
|
||||
{$IFEND}
|
||||
|
||||
var
|
||||
_ScreenDPI_X : Single = 0;
|
||||
|
||||
function ScalingByScreenDPI_N( F:TForm = NIL ):Single;
|
||||
var
|
||||
p : TPointF;
|
||||
{$IF (CompilerVersion >= 28)}
|
||||
M : TDeviceDisplayMetrics;
|
||||
{$IFEND}
|
||||
i : integer;
|
||||
h : THandle;
|
||||
begin
|
||||
@@ -29,6 +76,7 @@ begin
|
||||
end;
|
||||
{$ENDIF}
|
||||
|
||||
{$IF (CompilerVersion >= 28)} //TDeviceDisplayMetrics is available since XE8
|
||||
if TPlatformServices.Current.SupportsPlatformService( IFMXDeviceMetricsService ) then
|
||||
begin
|
||||
M := (TPlatformServices.Current.GetPlatformService(
|
||||
@@ -38,6 +86,7 @@ begin
|
||||
{$IFDEF MSWINDOWS}96{$ENDIF}
|
||||
;
|
||||
end;
|
||||
{$IFEND}
|
||||
end;
|
||||
|
||||
function ScalingByScreenDPI( F:TForm = NIL ):TPointF;
|
||||
|
@@ -6,7 +6,7 @@ unit SynTaskDialog;
|
||||
{
|
||||
This file is part of Synopse framework.
|
||||
|
||||
Synopse framework. Copyright (C) 2020 Arnaud Bouchez
|
||||
Synopse framework. Copyright (C) 2022 Arnaud Bouchez
|
||||
Synopse Informatique - https://synopse.info
|
||||
|
||||
*** BEGIN LICENSE BLOCK *****
|
||||
@@ -25,7 +25,7 @@ unit SynTaskDialog;
|
||||
|
||||
The Initial Developer of the Original Code is Arnaud Bouchez.
|
||||
|
||||
Portions created by the Initial Developer are Copyright (C) 2020
|
||||
Portions created by the Initial Developer are Copyright (C) 2022
|
||||
the Initial Developer. All Rights Reserved.
|
||||
|
||||
Contributor(s):
|
||||
@@ -139,8 +139,12 @@ uses
|
||||
{$endif}
|
||||
{$IFDEF FMX}
|
||||
System.UITypes, System.Types, System.UIConsts,
|
||||
FMX.Menus, FMX.Types, FMX.Layouts, FMX.ComboEdit,
|
||||
FMX.Graphics, FMX.Forms, FMX.Controls, FMX.StdCtrls, FMX.ExtCtrls,
|
||||
FMX.Menus, FMX.Types, FMX.Layouts,
|
||||
{$IF (CompilerVersion >= 26.0)}// Delphi XE5 UP
|
||||
FMX.ComboEdit,
|
||||
FMX.Graphics,
|
||||
{$IFEND}
|
||||
FMX.Forms, FMX.Controls, FMX.StdCtrls, FMX.ExtCtrls,
|
||||
FMX.ListBox, FMX.Edit, FMX.Objects, FMX.Platform,
|
||||
{$IFDEF MSWINDOWS}
|
||||
FMX.Platform.Win
|
||||
|
Reference in New Issue
Block a user