Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Comment: | {404} sample "30 - MVC Server" will now handle tags (categories) for articles
|
---|---|
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
70f7a1a8231a2c0b06a8e5af5b3112d9 |
User & Date: | User 2014-10-25 16:41:06 |
2014-10-25
| ||
17:25 | {405} allows static linking of SQLite3.o for FPC under Windows and Linux - thanks Alfred for the patch! check-in: 96a3f4c8b2 user: User tags: trunk | |
16:41 |
{404} sample "30 - MVC Server" will now handle tags (categories) for articles
| |
16:36 | {403} small enhancements to mORMotMVC.pas - for instance, RegisterExpressionHelpers() methods can now be chained in a fluent interface check-in: 3499783d21 user: User tags: trunk | |
Changes to SQLite3/Samples/30 - MVC Server/MVCModel.pas.
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 .. 90 91 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 ... 183 184 185 186 187 188 189 190 191 |
public class procedure InitializeTable(Server: TSQLRestServer; const FieldName: RawUTF8; Options: TSQLInitializeTableOptions); override; published property Rights: TSQLAuthorRights read fRights write fRights; end; TSQLCategory = class(TSQLRecord) private fIdent: RawUTF8; published property Ident: RawUTF8 read fIdent write fIdent; end; TSQLContent = class(TSQLRecordTimeStamped) private fContent: RawUTF8; fTitle: RawUTF8; fAuthor: TSQLAuthor; fAuthorName: RawUTF8; published ................................................................................ property AuthorName: RawUTF8 index 50 read fAuthorName write fAuthorName; end; TSQLArticle = class(TSQLContent) private fAbstract: RawUTF8; fPublishedMonth: Integer; public class function CurrentPublishedMonth: Integer; class procedure InitializeTable(Server: TSQLRestServer; const FieldName: RawUTF8; Options: TSQLInitializeTableOptions); override; published property PublishedMonth: Integer read fPublishedMonth write fPublishedMonth; property Abstract: RawUTF8 index 1024 read fAbstract write fAbstract; end; TSQLComment = class(TSQLContent) private fArticle: TSQLArticle; published property Article: TSQLArticle read fArticle write fArticle; end; function CreateModel: TSQLModel; implementation function CreateModel: TSQLModel; begin result := TSQLModel.Create([TSQLBlogInfo,TSQLCategory,TSQLAuthor, TSQLArticle,TSQLComment],'blog'); TSQLArticle.AddFilterOrValidate('Title',TSynFilterTrim.Create); TSQLArticle.AddFilterOrValidate('Title',TSynValidateText.Create); TSQLArticle.AddFilterOrValidate('Content',TSynFilterTrim.Create); TSQLArticle.AddFilterOrValidate('Content',TSynValidateText.Create); end; ................................................................................ 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; end. |
< < < < < < < > > > > > > > > > > > > > > > | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
66 67 68 69 70 71 72 73 74 75 76 77 78 79 .. 83 84 85 86 87 88 89 90 91 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 132 133 134 135 136 137 138 ... 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 |
public class procedure InitializeTable(Server: TSQLRestServer; const FieldName: RawUTF8; Options: TSQLInitializeTableOptions); override; published property Rights: TSQLAuthorRights read fRights write fRights; end; TSQLContent = class(TSQLRecordTimeStamped) private fContent: RawUTF8; fTitle: RawUTF8; fAuthor: TSQLAuthor; fAuthorName: RawUTF8; published ................................................................................ property AuthorName: RawUTF8 index 50 read fAuthorName write fAuthorName; 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 TagsAddOrdered(aTagID: Integer; const AlphabeticalOrder: TCardinalDynArray); published property PublishedMonth: Integer read fPublishedMonth write fPublishedMonth; property Abstract: RawUTF8 index 1024 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; TSQLComment = class(TSQLContent) private fArticle: TSQLArticle; published property Article: TSQLArticle read fArticle write fArticle; end; TSQLTag = class(TSQLRecord) private fIdent: RawUTF8; fCreatedAt: TCreateTime; public class function ComputeTagIdentPerIDArray(aRest: TSQLRest; out AlphabeticalOrder: TCardinalDynArray): TRawUTF8DynArray; published property Ident: RawUTF8 read fIdent write fIdent; property CreatedAt: TCreateTime read fCreatedAt write fCreatedAt; end; function CreateModel: TSQLModel; implementation function CreateModel: TSQLModel; begin result := TSQLModel.Create([TSQLBlogInfo,TSQLAuthor, TSQLTag,TSQLArticle,TSQLComment],'blog'); TSQLArticle.AddFilterOrValidate('Title',TSynFilterTrim.Create); TSQLArticle.AddFilterOrValidate('Title',TSynValidateText.Create); TSQLArticle.AddFilterOrValidate('Content',TSynFilterTrim.Create); TSQLArticle.AddFilterOrValidate('Content',TSynValidateText.Create); end; ................................................................................ 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.TagsAddOrdered(aTagID: Integer; const AlphabeticalOrder: TCardinalDynArray); var sets: TByteDynArray; i,n,max: integer; begin // add tag ID per alphabetic order - a bit complicated but works max := length(AlphabeticalOrder); if (aTagID=0) or (aTagID>max) then exit; n := length(fTags); if n=0 then begin SetLength(fTags,1); fTags[0] := aTagID; exit; end; SetLength(sets,(max shr 3)+1); for i := 0 to n-1 do SetBit(sets[0],fTags[i]-1); if GetBit(sets[0],aTagID-1) then exit; // duplicated aTagID SetBit(sets[0],aTagID-1); SetLength(fTags,n+1); n := 0; for i := 0 to max-1 do if GetBit(sets[0],AlphabeticalOrder[i]) then begin fTags[n] := AlphabeticalOrder[i]+1; inc(n); end; assert(n=length(fTags)); end; { TSQLTag } class function TSQLTag.ComputeTagIdentPerIDArray(aRest: TSQLRest; out AlphabeticalOrder: TCardinalDynArray): TRawUTF8DynArray; var tag: TSQLTag; max: integer; begin result := nil; tag := TSQLTag.CreateAndFillPrepare(aRest,'','ID,Ident'); try if tag.FillTable.RowCount=0 then exit; max := 0; while tag.FillOne do if tag.ID>max then max := tag.ID; SetLength(result,max); tag.FillRewind; while tag.FillOne do result[tag.ID-1] := tag.Ident; SetLength(AlphabeticalOrder,max); FillIncreasing(pointer(AlphabeticalOrder),0,max); QuickSortIndexedPUTF8Char(pointer(result),max,AlphabeticalOrder); finally tag.Free; end; end; end. |
Changes to SQLite3/Samples/30 - MVC Server/MVCViewModel.pas.
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 .. 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 .. 89 90 91 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 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 ... 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 ... 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 ... 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 |
// ! blog/main/articlecommit -> article edition commit (ID=0 for new) IBlogApplication = interface(IMVCApplication) procedure ArticleView( ID: integer; var WithComments: boolean; Direction: integer; out Article: TSQLArticle; out Author: variant; out Comments: TObjectList); procedure AuthorView( var ID: integer; out Author: TSQLAuthor; out Articles: RawJSON); function Login( const LogonName,PlainPassword: RawUTF8): TMVCAction; function Logout: TMVCAction; procedure ArticleEdit(var ID: integer; const Title,Content: RawUTF8; const ValidationError: variant; out Article: TSQLArticle); function ArticleCommit( ................................................................................ AuthorRights: TSQLAuthorRights; end; /// implements the ViewModel/Controller of this BLOG web site TBlogApplication = class(TMVCApplication,IBlogApplication) protected fBlogMainInfo: variant; fDefaultData: ILockedDocVariant; fDefaultLastID: integer; procedure ComputeMinimalData; virtual; procedure FlushAnyCache; override; function GetViewInfo(MethodIndex: integer): variant; override; function GetLoggedAuthorID(Rights: TSQLAuthorRights): integer; procedure MonthToText(const Value: variant; out result: variant); public constructor Create(aServer: TSQLRestServer); reintroduce; public procedure Default(var Scope: variant); procedure ArticleView(ID: integer; var WithComments: boolean; Direction: integer; out Article: TSQLArticle; out Author: variant; out Comments: TObjectList); procedure AuthorView( var ID: integer; out Author: TSQLAuthor; out Articles: RawJSON); function Login(const LogonName,PlainPassword: RawUTF8): TMVCAction; function Logout: TMVCAction; procedure ArticleEdit(var ID: integer; const Title,Content: RawUTF8; const ValidationError: variant; out Article: TSQLArticle); function ArticleCommit(ID: integer; const Title,Content: RawUTF8): TMVCAction; end; ................................................................................ { TBlogApplication } constructor TBlogApplication.Create(aServer: TSQLRestServer); begin fDefaultData := TLockedDocVariant.Create; inherited Create(aServer,TypeInfo(IBlogApplication)); ComputeMinimalData; // publish IBlogApplication using Mustache Views (TMVCRunOnRestServer default) fMainRunner := TMVCRunOnRestServer.Create(Self). SetCache('Default',cacheRootIfNoSession,15). SetCache('ArticleView',cacheWithParametersIfNoSession,60). SetCache('AuthorView',cacheWithParametersIgnoringSession,60); (TMVCRunOnRestServer(fMainRunner).Views as TMVCViewsMustache). RegisterExpressionHelpers(['MonthToText'],[MonthToText]); end; procedure TBlogApplication.MonthToText(const Value: variant; out result: variant); const MONTHS: array[0..11] of RawUTF8 = ( 'January','February','March','April','May','June','July','August', 'September','October','November','December'); var month: integer; text: RawUTF8; begin if VariantToInteger(Value,month) and (month>0) then text := MONTHS[month mod 12]+' '+UInt32ToUTF8(month div 12); RawUTF8ToVariant(text,result); end; procedure TBlogApplication.ComputeMinimalData; var info: TSQLBlogInfo; article: TSQLArticle; comment: TSQLComment; n: integer; res: TIntegerDynArray; begin info := TSQLBlogInfo.Create; try if not RestModel.Retrieve('',info) then begin // retrieve first item info.Title := 'mORMot BLOG'; info.Language := 'en'; info.Description := 'Sample Blog Web Application using Synopse mORMot MVC'; info.Copyright := '©2014 <a href=http://synopse.info>Synopse Informatique</a>'; info.About := TSynTestCase.RandomTextParagraph(30,'!'); RestModel.Add(info,true); end; fBlogMainInfo := info.GetSimpleFieldsAsDocVariant(false); finally info.Free; end; if not RestModel.TableHasRows(TSQLArticle) then begin RestModel.BatchStart(TSQLArticle,1000); article := TSQLArticle.Create; try article.Author := TSQLAuthor(1); article.AuthorName := 'synopse'; for n := 1 to 100 do begin article.PublishedMonth := 2014*12+(n div 10); article.Title := TSynTestCase.RandomTextParagraph(5,' '); article.Abstract := TSynTestCase.RandomTextParagraph(30,'!'); article.Content := TSynTestCase.RandomTextParagraph(200,'.','http://synopse.info'); RestModel.BatchAdd(article,true); end; if RestModel.BatchSend(res)=HTML_SUCCESS then begin comment := TSQLComment.Create; try comment.Author := article.Author; comment.AuthorName := article.AuthorName; RestModel.BatchStart(TSQLComment,1000); for n := 1 to 200 do begin comment.Article := Pointer(res[random(length(res))]); comment.Title := TSynTestCase.RandomTextParagraph(5,' '); comment.Content := TSynTestCase.RandomTextParagraph(30,'.','http://mormot.net'); RestModel.BatchAdd(Comment,true); end; RestModel.BatchSend(res) finally comment.Free; end; end; finally article.Free; end; end; end; function TBlogApplication.GetLoggedAuthorID(Rights: TSQLAuthorRights): integer; var SessionInfo: TCookieData; begin ................................................................................ fDefaultData.Clear; end; { TBlogApplication - Commands } const ARTICLE_FIELDS = 'ID,Title,Abstract,Author,AuthorName,CreatedAt'; procedure TBlogApplication.Default(var Scope: variant); var lastID: integer; begin if VariantToInteger(Scope,lastID) and (lastID>0) then begin _ObjAddProps(['articles',RestModel.RetrieveDocVariantArray( TSQLArticle,'','ID<? order by ID desc limit 20',[lastID],ARTICLE_FIELDS, nil,@lastID)],Scope); if lastID>1 then _ObjAddProps(['lastID',lastID],Scope); end else begin if not fDefaultData.AddExistingProp('Articles',Scope) then fDefaultData.AddNewProp('Articles',RestModel.RetrieveDocVariantArray( TSQLArticle,'','order by ID desc limit 20',[],ARTICLE_FIELDS, nil,@fDefaultLastID),Scope); _ObjAddProps(['lastID',fDefaultLastID],Scope); end; if not fDefaultData.AddExistingProp('Archives',Scope) then fDefaultData.AddNewProp('Archives',RestModel.RetrieveDocVariantArray( TSQLArticle,'','group by PublishedMonth order by PublishedMonth desc limit 12',[], 'distinct(PublishedMonth),max(ID)+1 as FirstID'),Scope); end; procedure TBlogApplication.ArticleView( ................................................................................ Comments := RestModel.RetrieveList(TSQLComment,'Article=?',[Article.ID]); end; end else raise EMVCApplication.CreateGotoError(HTML_NOTFOUND); end; procedure TBlogApplication.AuthorView(var ID: integer; out Author: TSQLAuthor; out Articles: RawJSON); begin RestModel.Retrieve(ID,Author); Author.HashedPassword := ''; // no need to publish it if Author.ID<>0 then Articles := RestModel.RetrieveListJSON( TSQLArticle,'Author=? order by id desc limit 50',[ID],ARTICLE_FIELDS) else raise EMVCApplication.CreateGotoError(HTML_NOTFOUND); end; function TBlogApplication.Login(const LogonName, PlainPassword: RawUTF8): TMVCAction; var Author: TSQLAuthor; SessionInfo: TCookieData; begin ................................................................................ RestModel.Update(Article); finally Article.Free; end; end; {$ifndef ISDELPHI2010} initialization TTextWriter.RegisterCustomJSONSerializerFromTextSimpleType(TypeInfo(TSQLAuthorRights)); TTextWriter.RegisterCustomJSONSerializerFromText(TypeInfo(TCookieData), 'AuthorName RawUTF8 AuthorID cardinal AuthorRights TSQLAuthorRights'); {$endif} end. |
| > > > > | > > > > > > > > | > < | > > > > > > > > > > > | > > > | > > > > > > > | | < < > > | | | | | | | | < < < < < < > > > > > > > > > | > > > | | < < | | | | | | | | | | < < | < | | > | > < < < | > | | | > > > > > > < > | > > > > > > | < > > | | | < |
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 .. 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 .. 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 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 ... 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 ... 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 ... 382 383 384 385 386 387 388 389 390 391 392 393 394 395 |
// ! blog/main/articlecommit -> article edition commit (ID=0 for new) IBlogApplication = interface(IMVCApplication) procedure ArticleView( ID: integer; var WithComments: boolean; Direction: integer; out Article: TSQLArticle; out Author: variant; out Comments: TObjectList); procedure AuthorView( var ID: integer; out Author: TSQLAuthor; out Articles: variant); function Login( const LogonName,PlainPassword: RawUTF8): TMVCAction; function Logout: TMVCAction; procedure ArticleEdit(var ID: integer; const Title,Content: RawUTF8; const ValidationError: variant; out Article: TSQLArticle); function ArticleCommit( ................................................................................ AuthorRights: TSQLAuthorRights; end; /// implements the ViewModel/Controller of this BLOG web site TBlogApplication = class(TMVCApplication,IBlogApplication) protected fBlogMainInfo: variant; fTagsLookupLock: IAutoLocker; fTagsLookup: TRawUTF8DynArray; fTagsLookupOrder: TCardinalDynArray; fDefaultData: ILockedDocVariant; fDefaultLastID: integer; procedure ComputeMinimalData; virtual; procedure FlushAnyCache; override; function GetViewInfo(MethodIndex: integer): variant; override; function GetLoggedAuthorID(Rights: TSQLAuthorRights): integer; procedure MonthToText(const Value: variant; out result: variant); procedure TagToText(const Value: variant; out result: variant); public constructor Create(aServer: TSQLRestServer); reintroduce; public procedure Default(var Scope: variant); procedure ArticleView(ID: integer; var WithComments: boolean; Direction: integer; out Article: TSQLArticle; out Author: variant; out Comments: TObjectList); procedure AuthorView( var ID: integer; out Author: TSQLAuthor; out Articles: variant); function Login(const LogonName,PlainPassword: RawUTF8): TMVCAction; function Logout: TMVCAction; procedure ArticleEdit(var ID: integer; const Title,Content: RawUTF8; const ValidationError: variant; out Article: TSQLArticle); function ArticleCommit(ID: integer; const Title,Content: RawUTF8): TMVCAction; end; ................................................................................ { TBlogApplication } constructor TBlogApplication.Create(aServer: TSQLRestServer); begin fDefaultData := TLockedDocVariant.Create; inherited Create(aServer,TypeInfo(IBlogApplication)); ComputeMinimalData; with TSQLBlogInfo.Create(RestModel,'') do try fBlogMainInfo := GetSimpleFieldsAsDocVariant(false); finally Free; end; fTagsLookupLock := TAutoLocker.Create; fTagsLookup := TSQLTag.ComputeTagIdentPerIDArray(RestModel,fTagsLookupOrder); // publish IBlogApplication using Mustache Views (TMVCRunOnRestServer default) fMainRunner := TMVCRunOnRestServer.Create(Self). SetCache('Default',cacheRootIfNoSession,15). SetCache('ArticleView',cacheWithParametersIfNoSession,60). SetCache('AuthorView',cacheWithParametersIgnoringSession,60); (TMVCRunOnRestServer(fMainRunner).Views as TMVCViewsMustache). RegisterExpressionHelpers(['MonthToText'],[MonthToText]). RegisterExpressionHelpers(['TagToText'],[TagToText]); end; procedure TBlogApplication.MonthToText(const Value: variant; out result: variant); const MONTHS: array[0..11] of RawUTF8 = ( 'January','February','March','April','May','June','July','August', 'September','October','November','December'); var month: integer; begin if VariantToInteger(Value,month) and (month>0) then RawUTF8ToVariant(MONTHS[month mod 12]+' '+UInt32ToUTF8(month div 12),result) else SetVariantNull(result); end; procedure TBlogApplication.TagToText(const Value: variant; out result: variant); var tag: integer; begin fTagsLookupLock.Enter; try if VariantToInteger(Value,tag) and (tag>0) and (tag<=length(fTagsLookup)) then RawUTF8ToVariant(fTagsLookup[tag-1],result) else SetVariantNull(result); finally fTagsLookupLock.Leave; end; end; const // just try with 200000 - and let your WordPress blog engine start to cry... FAKEDATA_ARTICLESCOUNT = 200; procedure TBlogApplication.ComputeMinimalData; var info: TSQLBlogInfo; article: TSQLArticle; comment: TSQLComment; tag: TSQLTag; batch: TSQLRestBatch; n,t: integer; articles,tags,comments: TIntegerDynArray; begin TSQLRecord.AutoFree([ // avoid several try..finally @info,TSQLBlogInfo, @article,TSQLArticle, @comment,TSQLComment, @tag,TSQLTag]); if not RestModel.Retrieve('',info) then begin // retrieve first item info.Title := 'mORMot BLOG'; info.Language := 'en'; info.Description := 'Sample Blog Web Application using Synopse mORMot MVC'; info.Copyright := '©2014 <a href=http://synopse.info>Synopse Informatique</a>'; info.About := TSynTestCase.RandomTextParagraph(30,'!'); RestModel.Add(info,true); end; if not RestModel.TableHasRows(TSQLArticle) then begin batch := TSQLRestBatch.Create(RestModel,TSQLTag,100); try for n := 1 to 32 do begin tag.Ident := 'Tag'+UInt32ToUtf8(n); tag.ID := n*2; // force ID to test TSQLTag.ComputeTagIdentPerIDArray batch.Add(tag,true,true); end; RestModel.BatchSend(batch,tags); fTagsLookup := TSQLTag.ComputeTagIdentPerIDArray(RestModel,fTagsLookupOrder); batch.Reset(TSQLArticle,20000); article.Author := TSQLAuthor(1); article.AuthorName := 'synopse'; for n := 1 to FAKEDATA_ARTICLESCOUNT do begin article.PublishedMonth := 2014*12+(n div 10); article.Title := TSynTestCase.RandomTextParagraph(5,' '); article.Abstract := TSynTestCase.RandomTextParagraph(30,'!'); article.Content := TSynTestCase.RandomTextParagraph(200,'.','http://synopse.info'); article.Tags := nil; for t := 1 to Random(6) do article.TagsAddOrdered(tags[random(length(tags))],fTagsLookupOrder); batch.Add(article,true); end; if RestModel.BatchSend(batch,articles)=HTML_SUCCESS then begin comment.Author := article.Author; comment.AuthorName := article.AuthorName; batch.Reset(TSQLComment,20000); for n := 1 to FAKEDATA_ARTICLESCOUNT*2 do begin comment.Article := Pointer(articles[random(length(articles))]); comment.Title := TSynTestCase.RandomTextParagraph(5,' '); comment.Content := TSynTestCase.RandomTextParagraph(30,'.','http://mormot.net'); batch.Add(Comment,true); end; RestModel.BatchSend(batch,comments) end; finally batch.Free; end; end; end; function TBlogApplication.GetLoggedAuthorID(Rights: TSQLAuthorRights): integer; var SessionInfo: TCookieData; begin ................................................................................ fDefaultData.Clear; end; { TBlogApplication - Commands } const ARTICLE_FIELDS = 'ID,Title,Tags,Abstract,Author,AuthorName,CreatedAt'; ARTICLE_DEFAULT_ORDER: RawUTF8 = 'order by ID desc limit 20'; procedure TBlogApplication.Default(var Scope: variant); var lastID,tag: integer; whereClause: RawUTF8; begin lastID := 0; tag := 0; with DocVariantDataSafe(Scope)^ do begin if GetAsInteger('lastID',lastID) then whereClause := 'ID<?' else whereClause := 'ID>?'; // will search ID>0 so always true if GetAsInteger('tag',tag) then // uses custom function to search in BLOB whereClause := whereClause+' and IntegerDynArrayContains(Tags,?)'; end; if (lastID=0) and (tag=0) then begin // use simple cache if no parameters SetVariantNull(Scope); if not fDefaultData.AddExistingProp('Articles',Scope) then fDefaultData.AddNewProp('Articles',RestModel.RetrieveDocVariantArray( TSQLArticle,'',pointer(ARTICLE_DEFAULT_ORDER),[], ARTICLE_FIELDS,nil,@fDefaultLastID),Scope); lastID := fDefaultLastID; end else // use more complex request using lastID + tag parameters scope := _ObjFast(['Articles',RestModel.RetrieveDocVariantArray( TSQLArticle,'',Pointer(whereClause+ARTICLE_DEFAULT_ORDER),[lastID,tag], ARTICLE_FIELDS,nil,@lastID)]); if lastID>1 then _ObjAddProps(['lastID',lastID],Scope); if tag>0 then _ObjAddProps(['tag',tag],Scope); if not fDefaultData.AddExistingProp('Archives',Scope) then fDefaultData.AddNewProp('Archives',RestModel.RetrieveDocVariantArray( TSQLArticle,'','group by PublishedMonth order by PublishedMonth desc limit 12',[], 'distinct(PublishedMonth),max(ID)+1 as FirstID'),Scope); end; procedure TBlogApplication.ArticleView( ................................................................................ Comments := RestModel.RetrieveList(TSQLComment,'Article=?',[Article.ID]); end; end else raise EMVCApplication.CreateGotoError(HTML_NOTFOUND); end; procedure TBlogApplication.AuthorView(var ID: integer; out Author: TSQLAuthor; out Articles: variant); begin RestModel.Retrieve(ID,Author); Author.HashedPassword := ''; // no need to publish it if Author.ID<>0 then Articles := RestModel.RetrieveDocVariantArray( TSQLArticle,'','Author=? order by id desc limit 50',[ID],ARTICLE_FIELDS) else raise EMVCApplication.CreateGotoError(HTML_NOTFOUND); end; function TBlogApplication.Login(const LogonName, PlainPassword: RawUTF8): TMVCAction; var Author: TSQLAuthor; SessionInfo: TCookieData; begin ................................................................................ RestModel.Update(Article); finally Article.Free; end; end; {$ifndef ISDELPHI2010} initialization TTextWriter.RegisterCustomJSONSerializerFromTextSimpleType(TypeInfo(TSQLAuthorRights)); TTextWriter.RegisterCustomJSONSerializerFromText(TypeInfo(TCookieData), 'AuthorName RawUTF8 AuthorID cardinal AuthorRights TSQLAuthorRights'); {$endif} end. |
Changes to SQLite3/Samples/30 - MVC Server/Views/ArticleView.html.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
{{>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}} </div> </div> <div class="blog-main"> {{{WikiToHtml article.abstract}}} <hr> {{{WikiToHtml article.content}}} <hr> |
| > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
{{>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="blog-main"> {{{WikiToHtml article.abstract}}} <hr> {{{WikiToHtml article.content}}} <hr> |
Changes to SQLite3/Samples/30 - MVC Server/Views/Default.html.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<p class="lead blog-description">{{main.blog.description}}</p>
</div>
<div class="row">
<div class="col-sm-8 blog-main">
{{#Scope}}
{{>articlerow}}
{{#lastID}}
<p><a href="default?scope={{.}}" class="btn btn-primary btn-sm">Previous Articles</a></p>
{{/lastID}}
</div>
<div class="col-sm-3 col-sm-offset-1 blog-sidebar">
<div class="sidebar-module sidebar-module-inset">
<h4>About</h4>
{{{WikiToHtml main.blog.about}}}
</div>
<div class="sidebar-module">
<h4>Archives</h4>
<ol class="list-unstyled">
{{#Archives}}
<li><a href="default?scope={{FirstID}}">{{MonthToText PublishedMonth}}</a></li>
{{/Archives}}
</ol>
</div>
</div>
</div>
{{/Scope}}
{{>footer}}
|
| | |
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<p class="lead blog-description">{{main.blog.description}}</p> </div> <div class="row"> <div class="col-sm-8 blog-main"> {{#Scope}} {{>articlerow}} {{#lastID}} <p><a href="default?scope={lastID:{{.}},tag:{{tag}}}" class="btn btn-primary btn-sm">Previous Articles</a></p> {{/lastID}} </div> <div class="col-sm-3 col-sm-offset-1 blog-sidebar"> <div class="sidebar-module sidebar-module-inset"> <h4>About</h4> {{{WikiToHtml main.blog.about}}} </div> <div class="sidebar-module"> <h4>Archives</h4> <ol class="list-unstyled"> {{#Archives}} <li><a href="default?scope={lastID:{{FirstID}}}">{{MonthToText PublishedMonth}}</a></li> {{/Archives}} </ol> </div> </div> </div> {{/Scope}} {{>footer}} |
Changes to SQLite3/Samples/30 - MVC Server/Views/articlerow.partial.
1 2 3 4 5 6 7 |
{{#articles}} <div class="blog-post"> <h2 class="blog-post-title"><a href=articleView?id={{id}}>{{Title}}</a></h2> <p class="blog-post-meta">{{TimeLogToText CreatedAt}} by <a href="authorView?id={{Author}}">{{AuthorName}}</a></p> {{{WikiToHtml Abstract}}} </div> {{/articles}} |
> | > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
{{#articles}} <div class="blog-post"> <h2 class="blog-post-title"><a href=articleView?id={{id}}>{{Title}}</a></h2> <p class="blog-post-meta"> {{TimeLogToText CreatedAt}} by <a href="authorView?id={{Author}}">{{AuthorName}}</a><br /> {{#Tags}}<a href="default?scope={tag:{{.}}}" class="label label-info">{{TagToText .}}</a> {{/Tags}} </p> {{{WikiToHtml Abstract}}} </div> {{/articles}} {{^articles}} <div class="blog-post"> <p>There is no more article corresponding to this research criteria.</p> </div> {{/articles}} |
Changes to synopseCommit.inc.
1 |
'1.18.403'
|
| |
1 |
'1.18.404'
|