• R/O
  • SSH
  • HTTPS

mantisbtmonitor: Commit


Commit MetaInfo

Revision60 (tree)
Time2021-10-28 02:54:11
Authorderekwildstar

Log Message

Removidas units antigas, que eram usadas no modo não-scrap
Criado o procedure LoadCommentsCLDS
Ajustado o procedure GenerateSimpleComments para que ele faça referência os novos nomes de campos. Além disso foi removida a gambiarra para ajustar o encoding do texto do comentário
Adicionada uma documentação inicial de código no formato PasDoc
Alterados os records TPreviousStatus e TPreviousResponsible
Aplicado o encoding UTF8 em todos os TStringStream criados
Aplicado o encoding UTF8 em todos os HTMLDocument criados
Alterado ParseCommentsAndAttachments para que ele contenha um argumento var e não out. Este procedure precisa deste parâmetro como var porque ele altera a variável passada nele. O parâmetro out descartava o conteúdo preexistente no parâmetro
Alterada a função GetPreviousResponsible
Alterada a função GetPreviousStatus
Corrigido o método GetAsResponsibleChange
Corrigido o método GetAsStatusChange
Documentação rtf atualizada

Change Summary

Incremental Difference

--- trunk/client/prj/MantisBTMonitor.dproj (revision 59)
+++ trunk/client/prj/MantisBTMonitor.dproj (revision 60)
@@ -114,9 +114,9 @@
114114 <AppDPIAwarenessMode>PerMonitor</AppDPIAwarenessMode>
115115 <VerInfo_MinorVer>0</VerInfo_MinorVer>
116116 <VerInfo_Release>0</VerInfo_Release>
117- <VerInfo_Build>662</VerInfo_Build>
117+ <VerInfo_Build>677</VerInfo_Build>
118118 <VerInfo_Locale>1033</VerInfo_Locale>
119- <VerInfo_Keys>CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.662;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName)</VerInfo_Keys>
119+ <VerInfo_Keys>CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.677;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName)</VerInfo_Keys>
120120 <Debugger_RunParams>/desenvolvimento</Debugger_RunParams>
121121 <VerInfo_AutoGenVersion>false</VerInfo_AutoGenVersion>
122122 <VerInfo_AutoIncVersion>true</VerInfo_AutoIncVersion>
@@ -152,9 +152,7 @@
152152 <DCCReference Include="..\src\UFormTask.pas">
153153 <Form>FormTask</Form>
154154 </DCCReference>
155- <DCCReference Include="..\src\lib\UMNWSWrapperFunctions.pas"/>
156155 <DCCReference Include="..\src\lib\UFunctions.pas"/>
157- <DCCReference Include="..\src\lib\mnws.pas"/>
158156 <DCCReference Include="..\src\UFormViewNote.pas">
159157 <Form>FormViewNote</Form>
160158 </DCCReference>
--- trunk/client/src/lib/UFunctions.pas (revision 59)
+++ trunk/client/src/lib/UFunctions.pas (revision 60)
@@ -42,13 +42,13 @@
4242 procedure RenderProjectNameAsMenuHeader(ATexto: String; ACanvas: TCanvas; ARect: TRect; AStatusColor: TColor);
4343 procedure PopulateImageListWithStatusColors(const AImageList: TPngImageList);
4444 procedure RenderPriorityDescriptions(ACanvas: TCanvas; ARect: TRect);
45+procedure LoadCommentsCLDS(AComments: TComments; AClientDataSet: TClientDataSet);
4546
4647 implementation
4748
4849 uses
49- SysUtils, ShellApi, EncdDecd, UMNWSWrapperFunctions, Forms,
50- Controls, UConfigurations, IdGlobalProtocols, RegularExpressionsCore,
51- NetEncoding;
50+ SysUtils, ShellApi, EncdDecd, Forms, Controls, UConfigurations,
51+ IdGlobalProtocols, RegularExpressionsCore, NetEncoding;
5252
5353 function RegExMatch(ASubject, APattern: String; AGroup: Byte; out AMatch: String): Boolean;
5454 begin
@@ -612,7 +612,7 @@
612612 Comments := '';
613613 while not AClientDataSet.Eof do
614614 begin
615- if AClientDataSet.FieldByName('private').AsInteger = 1 then
615+ if AClientDataSet.FieldByName('IsPrivate').AsBoolean then
616616 Color := '#808080'
617617 else
618618 Color := '#008000';
@@ -619,9 +619,9 @@
619619
620620 Comments := Comments + Format(COMMENT,[Color
621621 ,FormatFloat('00000000',AClientDataSet.FieldByName('id').Asinteger)
622- ,AClientDataSet.FieldByName('Reporter').AsString
623- ,FormatDateTime('dd/mm/yyyy "às" hh:nn:ss',AClientDataSet.FieldByName('Date').AsDateTime)
624- ,UTF8ToString(RawByteString(DecodeString(AClientDataSet.FieldByName('Note').AsString)))]);
622+ ,AClientDataSet.FieldByName('Creator').AsString
623+ ,FormatDateTime('dd/mm/yyyy "às" hh:nn:ss',AClientDataSet.FieldByName('Created').AsDateTime)
624+ ,AClientDataSet.FieldByName('Text').AsString]); // UTF8ToString(RawByteString(DecodeString(AClientDataSet.FieldByName('Text').AsString)))
625625 AClientDataSet.Next;
626626 end;
627627 LoadWebBrowserHTML(AWebBrowser,Comments);
@@ -901,4 +901,37 @@
901901 end;
902902 end;
903903
904+procedure LoadCommentsCLDS(AComments: TComments; AClientDataSet: TClientDataSet);
905+begin
906+ AClientDataSet.DisableControls;
907+ try
908+ if AClientDataSet.Active then
909+ AClientDataSet.EmptyDataSet
910+ else
911+ AClientDataSet.CreateDataSet;
912+
913+ AClientDataSet.ReadOnly := False;
914+ try
915+ for var Comment: TComment in AComments do
916+ begin
917+ AClientDataSet.Append;
918+ AClientDataSet.FieldByName('Id').AsInteger := Comment.Id;
919+ AClientDataSet.FieldByName('Created').AsDateTime := Comment.Created;
920+ AClientDataSet.FieldByName('LastUpdate').AsDateTime := Comment.LastUpdate;
921+ AClientDataSet.FieldByName('Creator').AsString := Comment.Creator;
922+ AClientDataSet.FieldByName('IsPrivate').AsBoolean := Comment.IsPrivate;
923+ AClientDataSet.FieldByName('Text').AsString := Comment.Text;
924+ AClientDataSet.Post;
925+ end;
926+ finally
927+ AClientDataSet.ReadOnly := True;
928+ end;
929+
930+ AClientDataSet.First;
931+ finally
932+ AClientDataSet.EnableControls;
933+ end;
934+end;
935+
936+
904937 end.
--- trunk/client/src/lib/UScrapFunctions.pas (revision 59)
+++ trunk/client/src/lib/UScrapFunctions.pas (revision 60)
@@ -132,19 +132,30 @@
132132 end;
133133
134134 THistory = array of THistoryEntry;
135-
135+ //: Record que representa o estado anterior de uma tarefa
136+ //: @Member(Date Data e hora na qual houve a mudança de estado)
137+ //: @Member(Actor Nome do usuário que realizou a mudança de estado)
138+ //: @Member(Status Estado anterior)
136139 TPreviousStatus = record
137140 Date: TDateTime;
138- ActorName: String;
141+ Actor: String;
139142 Status: TStatus;
140143 end;
141-
144+ //: Record que representa o responsável anterior de uma tarefa
145+ //: @Member(Date Data e hora na qual houve a mudança de estado)
146+ //: @Member(Actor Nome do usuário que realizou a mudança de estado)
147+ //: @Member(Responsible Nome do responsável anterior)
142148 TPreviousResponsible = record
143149 Date: TDateTime;
144- ActorName: String;
145- Name: String;
150+ Actor: String;
151+ Responsible: String;
146152 end;
147-
153+ //: Record que representa uma tarefa, seus comentários e seu histórico de
154+ //: mudanças
155+ //: @Member(PreviousStatus Obtém o estado anterior da tarefa atual)
156+ //: @SeeAlso(TPreviousStatus)
157+ //: @Member(PreviousResponsible)
158+ //: @SeeAlso(TPreviousResponsible)
148159 TTask = record
149160 private
150161 FId: Cardinal;
@@ -214,6 +225,8 @@
214225 var
215226 RBS: RawByteString;
216227 begin
228+ Result := AText;
229+ Exit;
217230 // RBS é um RawByteString, que é uma string que não tenta interpretar os bytes
218231 // da string original. Atualmente existem codificações que usam de 1 a 4 bytes
219232 // para representar um único caractere. Ao usar RawByteString dois bytes que
@@ -286,7 +299,7 @@
286299 Req.HttpOpenRequestParams.IgnoreInvalidCertificates := True;
287300 Req.HttpSendRequestParams.IgnoreInvalidCertificateCA := True;
288301
289- Res.Content := TStringStream.Create('');
302+ Res.Content := TStringStream.Create('',TEncoding.UTF8);
290303 try
291304 Request(Req,Res);
292305
@@ -343,7 +356,7 @@
343356 Req.HttpOpenRequestParams.IgnoreInvalidCertificates := True;
344357 Req.HttpSendRequestParams.IgnoreInvalidCertificateCA := True;
345358
346- Res.Content := TStringStream.Create('');
359+ Res.Content := TStringStream.Create('',TEncoding.UTF8);
347360 try
348361 Request(Req,Res);
349362
@@ -354,6 +367,7 @@
354367 // modificar o DOM, logo, ao usar esta propriedade o texto parseado pode
355368 // ser diferente daquilo que ele seria ao não usar esta propriedade
356369 HTMLDocument.DesignMode := 'On';
370+ HTMLDocument.charset := 'utf-8';
357371 (HTMLDocument as IHTMLDocument2Disp).Write(TStringStream(Res.Content).DataString);
358372 HTMLDocument.Close;
359373
@@ -433,7 +447,7 @@
433447 Req.HttpOpenRequestParams.Headers.Add('Content-Type: application/x-www-form-urlencoded');
434448 Req.HttpSendRequestParams.IgnoreInvalidCertificateCA := True;
435449
436- Res.Content := TStringStream.Create('');
450+ Res.Content := TStringStream.Create('',TEncoding.UTF8);
437451 try
438452 Request(Req,Res);
439453
@@ -497,7 +511,7 @@
497511 Req.HttpSendRequestParams.IgnoreInvalidCertificateCA := True;
498512
499513 Req.InternetConnectParams.ServerName := PChar(Configurations.BaseUrl + '/mantis/config/MantisConfigs.php');
500- Res.Content := TStringStream.Create('');
514+ Res.Content := TStringStream.Create('',TEncoding.UTF8);
501515 try
502516 Request(Req,Res);
503517
@@ -675,7 +689,7 @@
675689 // mesmo. Para isso, use IHTMLDOMNode
676690
677691 // AUnparsedComment é o <td> que contém o comentário propriamente dito
678-procedure ParseCommentsAndAttachments(AUnparsedComment: IHTMLElement; out AComment: TComment);
692+procedure ParseCommentsAndAttachments(AUnparsedComment: IHTMLElement; var AComment: TComment);
679693 var
680694 i: SmallInt;
681695 HasAttachments: Boolean;
@@ -857,6 +871,7 @@
857871 // modificar o DOM, logo, ao usar esta propriedade o texto parseado pode
858872 // ser diferente daquilo que ele seria ao não usar esta propriedade
859873 (HTMLDocument as IHTMLDocument2).DesignMode := 'On';
874+ (HTMLDocument as IHTMLDocument2).charset := 'utf-8';
860875 (HTMLDocument as IHTMLDocument2Disp).Write(ADocument);
861876 (HTMLDocument as IHTMLDocument2).Close;
862877
@@ -1165,9 +1180,10 @@
11651180 Req.HttpOpenRequestParams.IgnoreInvalidCertificates := True;
11661181 Req.HttpSendRequestParams.IgnoreInvalidCertificateCA := True;
11671182
1168- Res.Content := TStringStream.Create('');
1183+ Res.Content := TStringStream.Create('',TEncoding.UTF8);
11691184 try
11701185 Request(Req,Res);
1186+
11711187 Result := ParseTaskDetails(TStringStream(Res.Content).DataString,ATask);
11721188 finally
11731189 Res.Content.Free;
@@ -1194,6 +1210,7 @@
11941210 // modificar o DOM, logo, ao usar esta propriedade o texto parseado pode
11951211 // ser diferente daquilo que ele seria ao não usar esta propriedade
11961212 (HTMLDocument as IHTMLDocument2).DesignMode := 'On';
1213+ (HTMLDocument as IHTMLDocument2).charset := 'utf-8';
11971214 (HTMLDocument as IHTMLDocument2Disp).Write(ADocument);
11981215 (HTMLDocument as IHTMLDocument2).Close;
11991216
@@ -1292,7 +1309,7 @@
12921309 Req.HttpOpenRequestParams.IgnoreInvalidCertificates := True;
12931310 Req.HttpSendRequestParams.IgnoreInvalidCertificateCA := True;
12941311
1295- Res.Content := TStringStream.Create('');
1312+ Res.Content := TStringStream.Create('',TEncoding.UTF8);
12961313 try
12971314 Request(Req,Res);
12981315
@@ -1335,7 +1352,7 @@
13351352 Req.HttpOpenRequestParams.IgnoreInvalidCertificates := True;
13361353 Req.HttpSendRequestParams.IgnoreInvalidCertificateCA := True;
13371354
1338- Res.Content := TStringStream.Create('');
1355+ Res.Content := TStringStream.Create('',TEncoding.UTF8);
13391356 try
13401357 Request(Req,Res);
13411358
@@ -1364,29 +1381,52 @@
13641381 { TTask }
13651382
13661383 function TTask.GetPreviousResponsible: TPreviousResponsible;
1384+var
1385+ FirstIteration: Boolean;
13671386 begin
1368- Result := '';
1369- // Varrendo o histórico de trás pra frente, ao encontrar a primeira mudança de
1370- // responsável, que é na verdade a última, o responsável anterior é aquele que
1371- // se encontra à esquerda de "=>" (FromResponsible)
1387+ Result := Default(TPreviousResponsible);
1388+ FirstIteration := True;
1389+ // Varrendo o histórico de trás pra frente, ao encontrar a segunda mudança de
1390+ // responsável, o responsável anterior é aquele que se encontra à direita de
1391+ // "=>" (ToResponsible).
13721392 for var i: Cardinal := High(History) downto 0 do
13731393 if History[i].Action = HISTORY_ACTION_RESPONSIBLE then
13741394 begin
1375- Result := History[i].AsResponsibleChange.FromResponsible;
1395+ if FirstIteration then
1396+ begin
1397+ FirstIteration := False;
1398+ Continue;
1399+ end;
1400+
1401+ Result.Date := History[i].Moment;
1402+ Result.Actor := History[i].User;
1403+ Result.Responsible := History[i].AsResponsibleChange.ToResponsible;
13761404 Break;
13771405 end;
13781406 end;
13791407
13801408 function TTask.GetPreviousStatus: TPreviousStatus;
1409+var
1410+ FirstIteration: Boolean;
13811411 begin
1382- Result := Configurations.MantisConfigs.DefaultStatus;
1383- // Varrendo o histórico de trás pra frente, ao encontrar a primeira mudança de
1384- // status, que é na verdade a última, o status anterior é aquele que se
1385- // encontra à esquerda de "=>" (FromStatus)
1412+ Result := Default(TPreviousStatus);
1413+ Result.Status := Configurations.MantisConfigs.DefaultStatus;
1414+ FirstIteration := True;
1415+ // Varrendo o histórico de trás pra frente, ao encontrar a segunda mudança de
1416+ // status, o status anterior é aquele que se encontra à direita de "=>"
1417+ // (ToStatus)
13861418 for var i: Cardinal := High(History) downto 0 do
13871419 if History[i].Action = HISTORY_ACTION_STATE then
13881420 begin
1389- Result := History[i].AsStatusChange.FromStatus;
1421+ if FirstIteration then
1422+ begin
1423+ FirstIteration := False;
1424+ Continue;
1425+ end;
1426+
1427+ Result.Date := History[i].Moment;
1428+ Result.Actor := History[i].User;
1429+ Result.Status := History[i].AsStatusChange.ToStatus;
13901430 Break;
13911431 end;
13921432 end;
@@ -1479,7 +1519,7 @@
14791519 var SeparatorEnd: Byte := SeparatorBegin + HISTORY_CHANGE_SEPARATORLENGTH;
14801520
14811521 Result.FromResponsible := Trim(Copy(FData,1,Pred(SeparatorBegin)));
1482- Result.ToResponsible := Trim(Copy(FData,1,Succ(SeparatorEnd)));
1522+ Result.ToResponsible := Trim(Copy(FData,SeparatorEnd,Length(FData)));
14831523 end;
14841524 end;
14851525
@@ -1493,7 +1533,7 @@
14931533 var SeparatorEnd: Byte := SeparatorBegin + HISTORY_CHANGE_SEPARATORLENGTH;
14941534
14951535 Result.FromStatus := Configurations.MantisConfigs.StatusByName[Trim(Copy(FData,1,Pred(SeparatorBegin)))];
1496- Result.ToStatus := Configurations.MantisConfigs.StatusByName[Trim(Copy(FData,1,Succ(SeparatorEnd)))];
1536+ Result.ToStatus := Configurations.MantisConfigs.StatusByName[Trim(Copy(FData,SeparatorEnd,Length(FData)))];
14971537 end;
14981538 end;
14991539
--- trunk/client/src/UDamoPrincipal.pas (revision 59)
+++ trunk/client/src/UDamoPrincipal.pas (revision 60)
@@ -102,7 +102,7 @@
102102
103103 uses
104104 UFormSplash, UFormGeneralConfiguration, Forms, UConfigurations,
105- ActnMenus, UFormTask, ActiveX, UMNWSWrapperFunctions, UFunctions;
105+ ActnMenus, UFormTask, ActiveX, UFunctions;
106106
107107 const
108108 NIN_BALLOONUSERCLICK = (WM_USER + 5);
--- trunk/client/src/UDamoTask.pas (revision 59)
+++ trunk/client/src/UDamoTask.pas (revision 60)
@@ -41,10 +41,9 @@
4141 DASOAttachments: TDataSource;
4242 CLDSComments: TClientDataSet;
4343 CLDSCommentsId: TIntegerField;
44- CLDSCommentsReporter: TStringField;
45- CLDSCommentsDate: TDateTimeField;
46- CLDSCommentsPrivate: TSmallintField;
47- CLDSCommentsNote: TMemoField;
44+ CLDSCommentsCreator: TStringField;
45+ CLDSCommentsCreated: TDateTimeField;
46+ CLDSCommentsText: TMemoField;
4847 ACTNDownloadAttachment: TAction;
4948 ACTNDownloadAndOpenAttachment: TAction;
5049 ACTNUploadAttachment: TAction;
@@ -58,6 +57,8 @@
5857 CLDSHistoryAction: TStringField;
5958 CLDSHistoryData: TStringField;
6059 DASOHistory: TDataSource;
60+ CLDSCommentsLastUpdate: TDateTimeField;
61+ CLDSCommentsIsPrivate: TBooleanField;
6162 procedure ACTNOpenWithMantisExecute(Sender: TObject);
6263 procedure ACTNNoteReplyExecute(Sender: TObject);
6364 procedure ACTNNoteFullScreenExecute(Sender: TObject);
--- trunk/client/src/UFormTask.pas (revision 59)
+++ trunk/client/src/UFormTask.pas (revision 60)
@@ -94,7 +94,7 @@
9494 { Private declarations }
9595 FDamoTask: TDamoTask;
9696 FAttachmentsAndRelatedTasksLoaded: Boolean;
97-// FCommentsLoaded: Boolean;
97+ FCommentsLoaded: Boolean;
9898 FAdditionalInfoLoaded: Boolean;
9999 FStepsToReproduceLoaded: Boolean;
100100 FHistoryLoaded: Boolean;
@@ -163,6 +163,7 @@
163163 FDamoTask.CLDSHistory.SaveToFile(DatDirectory + 'history.cds');
164164 FDamoTask.CLDSRelatedTasks.SaveToFile(DatDirectory + 'relatedtasks.cds');
165165 FDamoTask.CLDSAttachments.SaveToFile(DatDirectory + 'attachments.cds');
166+ FDamoTask.CLDSComments.SaveToFile(DatDirectory + 'comments.cds');
166167 end;
167168 {$WARN SYMBOL_PLATFORM ON}
168169 end;
@@ -170,16 +171,16 @@
170171 procedure TFormTask.CLDSCommentsAfterScroll(DataSet: TDataSet);
171172 begin
172173 inherited;
173- LABENoteAuthor.Caption := FDamoTask.CLDSCommentsReporter.AsString;
174- LABENoteDate.Caption := FormatDateTime('dd/mm/yyyy "às" hh:nn:ss',FDamoTask.CLDSCommentsDate.AsDateTime);
174+ LABENoteAuthor.Caption := FDamoTask.CLDSCommentsCreator.AsString;
175+ LABENoteDate.Caption := FormatDateTime('dd/mm/yyyy "às" hh:nn:ss',FDamoTask.CLDSCommentsCreated.AsDateTime);
175176 LABENoteNumber.Caption := '(' + FormatFloat('00000000',FDamoTask.CLDSCommentsid.AsInteger) + ')';
176- LoadWebBrowserHTML(WEBRNote,FDamoTask.CLDSCommentsNote.AsString{,[sfDeactivateLinks,sfDeactivateOtherTags,sfActivateAllowedTags,sfMakeSpacesAndTabsPreserved,sfMakeReturnsPreserved,sfActivateLinks],True});
177+ LoadWebBrowserHTML(WEBRNote,FDamoTask.CLDSCommentsText.AsString{,[sfDeactivateLinks,sfDeactivateOtherTags,sfActivateAllowedTags,sfMakeSpacesAndTabsPreserved,sfMakeReturnsPreserved,sfActivateLinks],True});
177178 PNBBNoteReply.Tag := FDamoTask.CLDSCommentsId.AsInteger;
178179 PNBBNoteFullScreen.Tag := FDamoTask.CLDSCommentsId.AsInteger;
179180 PNBBNoteEdit.Tag := FDamoTask.CLDSCommentsId.AsInteger;
180181 LABENoteCount.Caption := Format('Anotação %u de %u',[Abs(FDamoTask.CLDSComments.RecordCount - FDamoTask.CLDSComments.RecNo + 1),FDamoTask.CLDSComments.RecordCount]);
181182
182- if FDamoTask.CLDSCommentsPrivate.AsInteger = 1 then
183+ if FDamoTask.CLDSCommentsIsPrivate.AsBoolean then
183184 PANE.Color := clGray
184185 else
185186 PANE.Color := clGreen;
@@ -235,15 +236,17 @@
235236
236237 procedure TFormTask.LoadComments;
237238 begin
238-// if not FCommentsLoaded then
239-// begin
240-// acho que isso carregava os comentários no CLDS, mas nao sei se vou usar isso ainda
239+ if not FCommentsLoaded then
240+ begin
241+ LoadCommentsCLDS(FDamoTask.Task.Comments, FDamoTask.CLDSComments);
241242 // FCommentsLoaded := GetComments(FIssueInfo.Id,Configurations.UserId,CLDSComments);
242243 // mude para scrap
243244 // if FCommentsLoaded then
244245 // acho que isso gera os comentários numa unica pagina html
245246 GenerateSimpleComments(WEBRNotesSimple,FDamoTask.CLDSComments);
246-// end;
247+
248+ FCommentsLoaded := True;
249+ end;
247250 end;
248251
249252 procedure TFormTask.LoadDescription;
@@ -325,6 +328,7 @@
325328 FStepsToReproduceLoaded := False;
326329 FAttachmentsAndRelatedTasksLoaded := False;
327330 FHistoryLoaded := False;
331+ FCommentsLoaded := False;
328332
329333 if AModal then
330334 Result := ShowModal
--- trunk/client/src/UFormViewNote.pas (revision 59)
+++ trunk/client/src/UFormViewNote.pas (revision 60)
@@ -46,7 +46,7 @@
4646 implementation
4747
4848 uses
49- UMNWSWrapperFunctions, UConfigurations, UFunctions;
49+ UConfigurations, UFunctions;
5050
5151 {$R *.dfm}
5252
Show on old repository browser