mORMot and Open Source friends
Check-in [930d542030]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:{6210} several enhancements to the MVC Blog sample
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 930d542030b47473e18f070d15635b261d507940
User & Date: ab 2021-01-21 22:21:29
Context
2021-01-21
22:22
{6211} fixed a border-case of TSQLHttpServer URI redirection check-in: f9ecd0f1b5 user: ab tags: trunk
22:21
{6210} several enhancements to the MVC Blog sample check-in: 930d542030 user: ab tags: trunk
20:52
{6209} introducing rsoNoTableURI in TSQLRestServerOptions check-in: dd79b59abe user: ab tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to SQLite3/Samples/30 - MVC Server/MVCModel.pas.

92
93
94
95
96
97
98

99
100
101
102
103
104
105
106
107
108

109
110
111
112
113
114
115
116
117
118
119
120


121
122
123
124
125
126
127
...
152
153
154
155
156
157
158


159
160
161
162
163
164
165
...
229
230
231
232
233
234
235


236
237
238
239
240
241
242
...
255
256
257
258
259
260
261
262












263
264
265
266
267
268
269
270
271
...
433
434
435
436
437
438
439

























440
441
442
443
444
445

446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
...
497
498
499
500
501
502
503




504
505
506
507


508
509
510


511
512
513
514
515
516
517
...
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
...
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
...
627
628
629
630
631
632
633

634
635
636
637
638
639
640
    Lookup: array of record
      Ident: RawUTF8;
      Occurence: integer;
    end;
    OrderID: TIntegerDynArray;
    procedure Init(aRest: TSQLRest);
    function Get(tagID: integer): RawUTF8;

    procedure SaveOccurence(aRest: TSQLRest);
    procedure SortTagsByIdent(var Tags: TIntegerDynArray);
    function GetAsDocVariantArray: Variant;
  end;

  TSQLArticle = class(TSQLContent)
  private
    fAbstract: RawUTF8;
    fPublishedMonth: Integer;
    fTags: TIntegerDynArray;

  public
    class function CurrentPublishedMonth: Integer;
    class procedure InitializeTable(Server: TSQLRestServer; const FieldName: RawUTF8;
      Options: TSQLInitializeTableOptions); override;
    procedure SetPublishedMonth(FromTime: TTimeLog);
    // note: caller should call Tags.SaveOccurence() to update the DB
    procedure TagsAddOrdered(aTagID: Integer; var aTags: TSQLTags);
  published
    property PublishedMonth: Integer read fPublishedMonth write fPublishedMonth;
    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;


  end;

  TSQLArticleSearch = class(TSQLRecordFTS4Porter)
  private
    fContent: RawUTF8;
    fTitle: RawUTF8;
    fAbstract: RawUTF8;
................................................................................


function CreateModel: TSQLModel;

procedure DotClearFlatImport(Rest: TSQLRest; const aFlatFile: RawUTF8;
  var aTagsLookup: TSQLTags; const aDotClearRoot: RawUTF8;
  const aStaticFolder: TFileName);




implementation

uses
  SynCrtSock; // for DotClearFlatImport() below
  
................................................................................

class procedure TSQLArticle.InitializeTable(Server: TSQLRestServer;
  const FieldName: RawUTF8; Options: TSQLInitializeTableOptions);
begin
  inherited;
  if (FieldName='') or (FieldName='PublishedMonth') then
    Server.CreateSQLIndex(TSQLArticle,'PublishedMonth',false);


end;

procedure TSQLArticle.SetPublishedMonth(FromTime: TTimeLog);
begin
  fPublishedMonth := TTimeLogBits(FromTime).Year*12+TTimeLogBits(FromTime).Month-1;
end;

................................................................................

function TSQLTags.Get(tagID: integer): RawUTF8;
begin
  if (tagID>0) and (tagID<=Length(Lookup)) then
    result := Lookup[tagID-1].Ident else
    result := '';
end;













function TSQLTags.GetAsDocVariantArray: Variant;
var i,ndx: Integer;
begin
  TDocVariant.NewFast(result);
  with Lock.ProtectMethod do
  for i := 0 to length(OrderID)-1 do begin
    ndx := OrderID[i]-1;
    with Lookup[ndx] do
      if Occurence>0 then
................................................................................
      exit;
    inc(P);
    T := TDotClearTable.Create(P);
    result.AddObject(T.Name,T);
    //FileFromString(T.GetODSDocument,TFileName(T.Name)+'.ods');
  until P=nil;
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;

    info: TSQLBlogInfo;
    article: TSQLArticle;
    comment: TSQLComment;
    tag: TSQLTag;
    tags: 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;
  var B,H: PUTF8Char;
      url,urlnoparam: RawUTF8;
      i,urlLen,status: integer;
      pic: RawByteString;
................................................................................
        if IdemPChar(P,'SRC="') then begin
          tag := src;
          inc(H,5);
        end else
          continue;
        AddNoJSONEscape(B,H-B);
        P := H;




        if IdemPChar(P,'HTTP://SYNOPSE.INFO') then begin
          AddShort('https://synopse.info');
          inc(P,19);
        end else if P^='/' then begin


          if IdemPChar(P+1,'POST/') then begin
            GetUrl(P+6);
            i := urls.IndexOf(urlnoparam);


            if i>=0 then begin
              AddShort('articleView?id=');
              Add(i+1);
              inc(P,urlLen+6);
            end else
              AddString(aDotClearRoot);
          end else
................................................................................
    EnsureDirectoryExists(PublicFolder);
    HTTP_DEFAULT_RESOLVETIMEOUT := 1000; // don't wait forever
    HTTP_DEFAULT_CONNECTTIMEOUT := 1000;
    HTTP_DEFAULT_RECEIVETIMEOUT := 2000;
  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]);
  T := data.GetObjectFrom('setting');
  Rest.Retrieve('',info);
  info.Copyright := VariantToUTF8(T.GetValue('setting_id','copyright_notice','setting_value'));
  if info.ID=0 then
................................................................................
  T.SortFields(tag_post_id,true,nil,sftInteger);
  postTable := data.GetObjectFrom('post');
  postTable.SortFields('post_creadt',true,nil,sftDateTime);
  post_id := postTable.FieldIndexExisting('post_id');
  post_url := postTable.FieldIndexExisting('post_url');
  if postTable.Step(true) then
    repeat
      urls.Add(postTable.FieldBuffer(post_url));
    until not postTable.Step;
  article.Author := TSQLAuthor(1);
  article.AuthorName := 'synopse';
  article.ContentHtml := true;
  for r := 1 to postTable.RowCount do begin
    article.Title := postTable.GetU(r,'post_title');
    article.Abstract := FixLinks(postTable.Get(r,'post_excerpt_xhtml'));
................................................................................
      article.Abstract := article.Content;
      article.Content := '';
    end;
    article.CreatedAt := Iso8601ToTimeLog(postTable.GetU(r,'post_creadt'));
    article.ModifiedAt := Iso8601ToTimeLog(postTable.GetU(r,'post_upddt'));
    article.SetPublishedMonth(article.CreatedAt);
    postID := postTable.GetAsInteger(r,post_id);

    article.Tags := nil;
    if tagTable.Step(true) then
      repeat
        if tagTable.FieldAsInteger(tag_post_id)=postID then begin
          ndx := FastFindPUTF8CharSorted(
            pointer(tags),high(tags),tagTable.FieldBuffer(meta_id),@StrIComp);
          if ndx>=0 then






>










>












>
>







 







>
>







 







>
>







 








>
>
>
>
>
>
>
>
>
>
>
>

|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>





|
>




|




<







 







>
>
>
>




>
>


<
>
>







 







<







 







|







 







>







92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
...
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
...
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
...
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
...
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500

501
502
503
504
505
506
507
...
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560

561
562
563
564
565
566
567
568
569
...
630
631
632
633
634
635
636

637
638
639
640
641
642
643
...
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
...
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
    Lookup: array of record
      Ident: RawUTF8;
      Occurence: integer;
    end;
    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;
  end;

  TSQLArticle = class(TSQLContent)
  private
    fAbstract: RawUTF8;
    fPublishedMonth: Integer;
    fTags: TIntegerDynArray;
    fLegacyHash: Int64;
  public
    class function CurrentPublishedMonth: Integer;
    class procedure InitializeTable(Server: TSQLRestServer; const FieldName: RawUTF8;
      Options: TSQLInitializeTableOptions); override;
    procedure SetPublishedMonth(FromTime: TTimeLog);
    // note: caller should call Tags.SaveOccurence() to update the DB
    procedure TagsAddOrdered(aTagID: Integer; var aTags: TSQLTags);
  published
    property PublishedMonth: Integer read fPublishedMonth write fPublishedMonth;
    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)
  private
    fContent: RawUTF8;
    fTitle: RawUTF8;
    fAbstract: RawUTF8;
................................................................................


function CreateModel: TSQLModel;

procedure DotClearFlatImport(Rest: TSQLRest; const aFlatFile: RawUTF8;
  var aTagsLookup: TSQLTags; const aDotClearRoot: RawUTF8;
  const aStaticFolder: TFileName);

function ComputeLegacyHash(url: PUTF8Char): cardinal;


implementation

uses
  SynCrtSock; // for DotClearFlatImport() below
  
................................................................................

class procedure TSQLArticle.InitializeTable(Server: TSQLRestServer;
  const FieldName: RawUTF8; Options: TSQLInitializeTableOptions);
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);
begin
  fPublishedMonth := TTimeLogBits(FromTime).Year*12+TTimeLogBits(FromTime).Month-1;
end;

................................................................................

function TSQLTags.Get(tagID: integer): RawUTF8;
begin
  if (tagID>0) and (tagID<=Length(Lookup)) then
    result := Lookup[tagID-1].Ident else
    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: PtrInt;
begin
  TDocVariant.NewFast(result);
  with Lock.ProtectMethod do
  for i := 0 to length(OrderID)-1 do begin
    ndx := OrderID[i]-1;
    with Lookup[ndx] do
      if Occurence>0 then
................................................................................
      exit;
    inc(P);
    T := TDotClearTable.Create(P);
    result.AddObject(T.Name,T);
    //FileFromString(T.GetODSDocument,TFileName(T.Name)+'.ods');
  until P=nil;
end;

{function HttpGet2(const aURI: SockString; outHeaders: PSockString=nil;
  forceNotSocket: boolean=false; outStatus: PInteger=nil): SockString; overload;
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: TRawUTF8List;
    urls: TIntegerDynArray;
    info: TSQLBlogInfo;
    article: TSQLArticle;
    comment: TSQLComment;
    tag: TSQLTag;
    tags, notfound: TRawUTF8DynArray;
    tagID: TIDDynArray;
    tagsCount: integer;
    batch: TSQLRestBatch;
    PublicFolder: TFileName;

    r,ndx,post_url,meta_id,meta_type,tag_post_id,postID,post_id: integer;

  function FixLinks(P: PUTF8Char): RawUTF8;
  var B,H: PUTF8Char;
      url,urlnoparam: RawUTF8;
      i,urlLen,status: integer;
      pic: RawByteString;
................................................................................
        if IdemPChar(P,'SRC="') then begin
          tag := src;
          inc(H,5);
        end else
          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 := IntegerScanIndex(pointer(urls),length(urls),
              ComputeLegacyHash(pointer(urlnoparam)));
            if i>=0 then begin
              AddShort('articleView?id=');
              Add(i+1);
              inc(P,urlLen+6);
            end else
              AddString(aDotClearRoot);
          end else
................................................................................
    EnsureDirectoryExists(PublicFolder);
    HTTP_DEFAULT_RESOLVETIMEOUT := 1000; // don't wait forever
    HTTP_DEFAULT_CONNECTTIMEOUT := 1000;
    HTTP_DEFAULT_RECEIVETIMEOUT := 2000;
  end;
  auto1 := TAutoFree.Several([
    @data,TDotClearTable.Parse(aFlatFile),

    @batch,TSQLRestBatch.Create(Rest,TSQLTag,5000)]);
  auto2 := TSQLRecord.AutoFree([ // avoid several try..finally
    @info,TSQLBlogInfo, @article,TSQLArticle, @comment,TSQLComment, @tag,TSQLTag]);
  T := data.GetObjectFrom('setting');
  Rest.Retrieve('',info);
  info.Copyright := VariantToUTF8(T.GetValue('setting_id','copyright_notice','setting_value'));
  if info.ID=0 then
................................................................................
  T.SortFields(tag_post_id,true,nil,sftInteger);
  postTable := data.GetObjectFrom('post');
  postTable.SortFields('post_creadt',true,nil,sftDateTime);
  post_id := postTable.FieldIndexExisting('post_id');
  post_url := postTable.FieldIndexExisting('post_url');
  if postTable.Step(true) then
    repeat
      AddInteger(urls,ComputeLegacyHash(postTable.FieldBuffer(post_url)));
    until not postTable.Step;
  article.Author := TSQLAuthor(1);
  article.AuthorName := 'synopse';
  article.ContentHtml := true;
  for r := 1 to postTable.RowCount do begin
    article.Title := postTable.GetU(r,'post_title');
    article.Abstract := FixLinks(postTable.Get(r,'post_excerpt_xhtml'));
................................................................................
      article.Abstract := article.Content;
      article.Content := '';
    end;
    article.CreatedAt := Iso8601ToTimeLog(postTable.GetU(r,'post_creadt'));
    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
        if tagTable.FieldAsInteger(tag_post_id)=postID then begin
          ndx := FastFindPUTF8CharSorted(
            pointer(tags),high(tags),tagTable.FieldBuffer(meta_id),@StrIComp);
          if ndx>=0 then

Changes to SQLite3/Samples/30 - MVC Server/MVCServer.dpr.

41
42
43
44
45
46
47

48
49
50
51

52
53
54
55
56
57
58
  //with TSQLLog.Family do Level := LOG_VERBOSE;
  aModel := CreateModel;
  try
    aServer := TSQLRestServerDB.Create(aModel,ChangeFileExt(ExeVersion.ProgramFileName,'.db'));
    try
      aServer.DB.Synchronous := smNormal;
      aServer.DB.LockingMode := lmExclusive;

      aServer.CreateMissingTables;
      aApplication := TBlogApplication.Create;
      try
        aApplication.Start(aServer);

        aHTTPServer := TSQLHttpServer.Create('8092',aServer
          {$ifndef ONLYUSEHTTPSOCKET},'+',useHttpApiRegisteringURI{$endif});
        try
          aHTTPServer.RootRedirectToURI('blog/default'); // redirect / to blog/default
          aServer.RootRedirectGet := 'blog/default';  // redirect blog to blog/default
          writeln('"MVC Blog Server" launched on port 8092 using ',aHttpServer.HttpServer.ClassName);
          writeln(#10'You can check http://localhost:8092/blog/mvc-info for information');






>




>







41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
  //with TSQLLog.Family do Level := LOG_VERBOSE;
  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
          aHTTPServer.RootRedirectToURI('blog/default'); // redirect / to blog/default
          aServer.RootRedirectGet := 'blog/default';  // redirect blog to blog/default
          writeln('"MVC Blog Server" launched on port 8092 using ',aHttpServer.HttpServer.ClassName);
          writeln(#10'You can check http://localhost:8092/blog/mvc-info for information');

Changes to SQLite3/Samples/30 - MVC Server/MVCViewModel.pas.

8
9
10
11
12
13
14

15
16
17
18
19
20
21
..
67
68
69
70
71
72
73



74

75
76
77
78
79
80
81
...
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
...
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
...
476
477
478
479
480
481
482


















483
484
485
486
487
488
489
490
491
uses
  SysUtils,
  Contnrs,
  Variants,
  SynCommons,
  SynLog,
  SynTests,

  mORMot,
  mORMotMVC,
  MVCModel;

type
  /// defines the main ViewModel/Controller commands of the BLOG web site
  // - typical URI are:
................................................................................
    procedure FlushAnyCache; override;
    procedure GetViewInfo(MethodIndex: integer; out info: variant); override;
    function GetLoggedAuthorID(Right: TSQLAuthorRight; ContentToFillAuthor: TSQLContent): TID;
    procedure MonthToText(const Value: variant; out result: variant);
    procedure TagToText(const Value: variant; out result: variant);
  public
    procedure Start(aServer: TSQLRestServer); reintroduce;



  public

    procedure Default(var Scope: variant);
    procedure ArticleView(ID: TID;
      var WithComments: boolean; Direction: integer; var Scope: variant;
      out Article: TSQLArticle; out Author: variant;
      out Comments: TObjectList);
    procedure AuthorView(
      var ID: TID; out Author: TSQLAuthor; out Articles: variant);
................................................................................
    articles,tags,comments: TIDDynArray;
    tmp: RawUTF8;
    auto: IAutoFree; // mandatory only for FPC
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');
    info.Language := 'en';
    if tmp<>'' then begin
      info.Title := 'Synopse Blog';
      info.Description := 'Articles, announcements, news, updates and more '+
        'about our Open Source projects';
      info.About := 'Latest information about Synopse Open Source librairies, '+
        'mainly the mORMot ORM/SOA/MVC framework, and SynPDF.';
................................................................................
      ', running on '+RawUTF8(ToText(OSVersion32))+'.';
    info.Copyright := '&copy;'+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',
      (TMVCRunOnRestServer(fMainRunner).Views as TMVCViewsMustache).ViewStaticFolder);
    exit;
  end;  
  SetLength(tags,32);
  for n := 1 to length(tags) do begin
    tag.Ident := 'Tag'+UInt32ToUtf8(n);
    tag.IDValue := n*2; // force test TSQLTags layout
................................................................................
        if RestModel.Add(Article,true)<>0 then
          GotoView(result,'ArticleView',['ID',Article.ID],HTTP_SUCCESS) else
          GotoError(result,sErrorWriting);
      end else
        RestModel.Update(Article);
  end;
end;



















initialization
  {$ifndef DELPHI2010}
  // manual definition mandatory only if Delphi 2010 RTTI is not available
  TTextWriter.RegisterCustomJSONSerializerFromTextSimpleType(TypeInfo(TSQLAuthorRights));
  TTextWriter.RegisterCustomJSONSerializerFromText(TypeInfo(TCookieData),
    'AuthorName RawUTF8 AuthorID cardinal AuthorRights TSQLAuthorRights');
  {$endif}
end.






>







 







>
>
>

>







 







|







 







|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>









8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
..
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
...
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
...
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
...
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
uses
  SysUtils,
  Contnrs,
  Variants,
  SynCommons,
  SynLog,
  SynTests,
  SynCrtSock,
  mORMot,
  mORMotMVC,
  MVCModel;

type
  /// defines the main ViewModel/Controller commands of the BLOG web site
  // - typical URI are:
................................................................................
    procedure FlushAnyCache; override;
    procedure GetViewInfo(MethodIndex: integer; out info: variant); override;
    function GetLoggedAuthorID(Right: TSQLAuthorRight; ContentToFillAuthor: TSQLContent): TID;
    procedure MonthToText(const Value: variant; out result: variant);
    procedure TagToText(const Value: variant; out result: variant);
  public
    procedure Start(aServer: TSQLRestServer); reintroduce;
  published
    procedure Post(Ctxt: TSQLRestServerURIContext);
    procedure Tag(Ctxt: TSQLRestServerURIContext);
  public
    // IBlogApplication implemented methods
    procedure Default(var Scope: variant);
    procedure ArticleView(ID: TID;
      var WithComments: boolean; Direction: integer; var Scope: variant;
      out Article: TSQLArticle; out Author: variant;
      out Comments: TObjectList);
    procedure AuthorView(
      var ID: TID; out Author: TSQLAuthor; out Articles: variant);
................................................................................
    articles,tags,comments: TIDDynArray;
    tmp: RawUTF8;
    auto: IAutoFree; // mandatory only for FPC
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(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';
      info.About := 'Latest information about Synopse Open Source librairies, '+
        'mainly the mORMot ORM/SOA/MVC framework, and SynPDF.';
................................................................................
      ', running on '+RawUTF8(ToText(OSVersion32))+'.';
    info.Copyright := '&copy;'+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,'https://blog.synopse.info',
      (TMVCRunOnRestServer(fMainRunner).Views as TMVCViewsMustache).ViewStaticFolder);
    exit;
  end;  
  SetLength(tags,32);
  for n := 1 to length(tags) do begin
    tag.Ident := 'Tag'+UInt32ToUtf8(n);
    tag.IDValue := n*2; // force test TSQLTags layout
................................................................................
        if RestModel.Add(Article,true)<>0 then
          GotoView(result,'ArticleView',['ID',Article.ID],HTTP_SUCCESS) else
          GotoError(result,sErrorWriting);
      end else
        RestModel.Update(Article);
  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;


initialization
  {$ifndef DELPHI2010}
  // manual definition mandatory only if Delphi 2010 RTTI is not available
  TTextWriter.RegisterCustomJSONSerializerFromTextSimpleType(TypeInfo(TSQLAuthorRights));
  TTextWriter.RegisterCustomJSONSerializerFromText(TypeInfo(TCookieData),
    'AuthorName RawUTF8 AuthorID cardinal AuthorRights TSQLAuthorRights');
  {$endif}
end.

Changes to SynopseCommit.inc.

1
'1.18.6209'
|
1
'1.18.6210'