• R/O
  • SSH
  • HTTPS

mantisbtmonitor: Commit


Commit MetaInfo

Revision70 (tree)
Time2021-12-18 07:12:52
Authorderekwildstar

Log Message

Folhas de estilo atualizadas
Consideração de comentários sem texto (comentários de anexos)
Função ReplaceSpecialElementsFromComment criada para lidar com a substituição de tags especiais incluídos em comentários
Lidando com a abertura da tela de tarefas quando há um id de comentário informado
Tela de gerenciamento de notas concluída com a lista de uploads e drag and drop habilitado
Na tela de tarefa o método DoNavigationStarting está sendo compartilhado por todos os TEdgeBrowser no evento OnNavigationStarting

Change Summary

Incremental Difference

--- trunk/client/prj/MantisBTMonitor.dproj (revision 69)
+++ trunk/client/prj/MantisBTMonitor.dproj (revision 70)
@@ -117,7 +117,7 @@
117117 <VerInfo_MinorVer>0</VerInfo_MinorVer>
118118 <VerInfo_Release>0</VerInfo_Release>
119119 <VerInfo_Locale>1033</VerInfo_Locale>
120- <VerInfo_Keys>CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.419;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName)</VerInfo_Keys>
120+ <VerInfo_Keys>CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.493;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName)</VerInfo_Keys>
121121 <Debugger_RunParams>/desenvolvimento</Debugger_RunParams>
122122 <VerInfo_AutoGenVersion>false</VerInfo_AutoGenVersion>
123123 <VerInfo_AutoIncVersion>true</VerInfo_AutoIncVersion>
@@ -124,7 +124,7 @@
124124 <DCC_DebugInformation>2</DCC_DebugInformation>
125125 <DCC_SymbolReferenceInfo>2</DCC_SymbolReferenceInfo>
126126 <DCC_DebugInfoInExe>true</DCC_DebugInfoInExe>
127- <VerInfo_Build>419</VerInfo_Build>
127+ <VerInfo_Build>493</VerInfo_Build>
128128 <DCC_MapFile>3</DCC_MapFile>
129129 </PropertyGroup>
130130 <ItemGroup>
--- trunk/client/src/lib/UFunctions.pas (revision 69)
+++ trunk/client/src/lib/UFunctions.pas (revision 70)
@@ -829,11 +829,17 @@
829829 ' </div>'#13#10 +
830830 ' </div>'#13#10;
831831
832+ const HEADER1_TEMPLATE =
833+ ' <div class="widget-header widget-header-small" id="%u">(%s) <b>%s</b> escreveu em <b>%s</b></div>';
834+
835+ const HEADER2_TEMPLATE =
836+ ' <div class="widget-header widget-header-small"><b>%s</b> anexou este(s) arquivo(s) em <b>%s</b></div>';
837+
832838 const COMMENT_TEMPLATE =
833839 '<tr class="bugnote">'#13#10 +
834840 ' <td class="bugnote-note bugnote-%s">'#13#10 +
835841 ' <div class="widget-box widget-color-blue2 mbtmheader">'#13#10 +
836- ' <div class="widget-header widget-header-small">(%s) <b>%s</b> escreveu em <b>%s</b></div>'#13#10 +
842+ '%s'#13#10 + // cabeçalho
837843 ' </div>'#13#10 +
838844 '%s' + // ferramentas
839845 '%s' + // comentários
@@ -852,12 +858,13 @@
852858
853859 var Tools: String;
854860 var Attachments: String;
861+ var Header: String;
855862
856863 Result := '';
857864
858865 for var Comment: TComment in AComments do
859866 begin
860- // -- Ferramentas ------------------------------------------------------
867+ // -- Ferramentas --------------------------------------------------------
861868 Tools := '';
862869 Attachments := '';
863870
@@ -879,8 +886,8 @@
879886
880887 Tools := Format(TOOLS_TEMPLATE,[Tools]);
881888 end;
882- // ---------------------------------------------------------------------
883- // -- Anexos -----------------------------------------------------------
889+ // -----------------------------------------------------------------------
890+ // -- Anexos -------------------------------------------------------------
884891 if Length(Comment.Attachments) > 0 then
885892 begin
886893 var AttachmentItem: String;
@@ -895,16 +902,24 @@
895902
896903 Attachments := Attachments + Format(ATTACHMENTS_TEMPLATE,[AttachmentItem]);
897904 end;
898- // ---------------------------------------------------------------------
899- // -- Linha de comentário final ----------------------------------------
905+ // -----------------------------------------------------------------------
906+ // -- Header -------------------------------------------------------------
907+ if Comment.AttachmentsOnly then
908+ Header := Format(HEADER2_TEMPLATE,[Comment.Creator
909+ ,FormatDateTime('dd/mm/yyyy "às" hh:nn:ss',Comment.Created)])
910+ else
911+ Header := Format(HEADER1_TEMPLATE,[Comment.Id
912+ ,FormatFloat('00000000',Comment.Id)
913+ ,Comment.Creator
914+ ,FormatDateTime('dd/mm/yyyy "às" hh:nn:ss',Comment.Created)]);
915+ // -----------------------------------------------------------------------
916+ // -- Linha de comentário final ------------------------------------------
900917 Result := Result + Format(COMMENT_TEMPLATE,[IfThen(Comment.IsPrivate,'private','public')
901- ,FormatFloat('00000000',Comment.Id)
902- ,Comment.Creator
903- ,FormatDateTime('dd/mm/yyyy "às" hh:nn:ss',Comment.Created)
918+ ,Header
904919 ,Tools
905920 ,Comment.Text
906921 ,Attachments]);
907- // ---------------------------------------------------------------------
922+ // -----------------------------------------------------------------------
908923 end;
909924 end;
910925 end;
--- trunk/client/src/lib/UInterposersAndHelpers.pas (revision 69)
+++ trunk/client/src/lib/UInterposersAndHelpers.pas (revision 70)
@@ -136,7 +136,7 @@
136136 var
137137 FileInfo: SHFILEINFO;
138138 begin
139- SHGetFileInfo(PChar(GetFileName(AId)), 0, FileInfo, SizeOf(FileInfo), SHGFI_TYPENAME);
139+ SHGetFileInfo(PChar(GetFileName(AId)), 0, FileInfo, SizeOf(SHFILEINFO), SHGFI_TYPENAME);
140140 Result := FileInfo.szTypeName;
141141 end;
142142
@@ -149,7 +149,7 @@
149149 var
150150 FileInfo: SHFILEINFO;
151151 begin
152- SHGetFileInfo(PChar(GetFileName(AId)), 0, FileInfo, SizeOf(FileInfo), SHGFI_DISPLAYNAME);
152+ SHGetFileInfo(PChar(GetFileName(AId)), 0, FileInfo, SizeOf(SHFILEINFO), SHGFI_DISPLAYNAME);
153153 Result := FileInfo.szDisplayName;
154154 end;
155155
@@ -157,7 +157,7 @@
157157 var
158158 FileInfo: SHFILEINFO;
159159 begin
160- SHGetFileInfo(PChar(GetFileName(AId)), 0, FileInfo, SizeOf(FileInfo), SHGFI_ICON or SHGFI_SMALLICON);
160+ SHGetFileInfo(PChar(GetFileName(AId)), 0, FileInfo, SizeOf(SHFILEINFO), SHGFI_ICON or SHGFI_SMALLICON);
161161 Result := FileInfo.hIcon;
162162 end;
163163
@@ -180,21 +180,6 @@
180180 inherited;
181181 end;
182182
183-//procedure TListBox.WMDropFiles(var AMessage: TWMDropFiles);
184-//begin
185-// inherited;
186-//
187-// with TFileCatcher.Create(AMessage.Drop) do
188-// try
189-// for var i: Cardinal := 0 to Pred(FileCount) do
190-// Items.Add(GetFile(i));
191-// finally
192-// Free;
193-// end;
194-//
195-// AMessage.Result := 0;
196-//end;
197-
198183 procedure TListView.WMDropFiles(var AMessage: TWMDropFiles);
199184 begin
200185 inherited;
--- trunk/client/src/lib/UScrapFunctions.pas (revision 69)
+++ trunk/client/src/lib/UScrapFunctions.pas (revision 70)
@@ -567,9 +567,51 @@
567567 end;
568568 end;
569569
570+function ReplaceSpecialElementsFromComment(AElementWithComment: IHTMLElement; const AComment: TComment): String;
571+var
572+ HTMLElementCollection: IHTMLElementCollection4;
573+begin
574+ // Substituindo cada <atta> por um link para download do anexo referenciado
575+ HTMLElementCollection := (AElementWithComment as IHTMLElement2).getElementsByTagName('atta') as IHTMLElementCollection4;
570576
571-// AUnparsedComment é o <td> que contém o comentário propriamente dito
572-procedure ParseCommentsAndAttachments(AUnparsedComment: IHTMLElement; var AComment: TComment);
577+ if HTMLElementCollection.length > 0 then
578+ begin
579+ var HTMLAnchorElement: IHTMLAnchorElement;
580+ var FileName: String;
581+ var FileNick: String;
582+ var Attachment: TAttachment;
583+ // Aqui o loop precisa ser de trás pra frente porque ao substituir um dos
584+ // elementos a quantidade de elementos circulados diminui e caso o loop
585+ // fosse na ordem direta, estaríamos acessando um índice de elemento que não
586+ // condiz com a realidade, havendo o salto de itens e um AV no último item.
587+ // Ao reverter o loop, é garantido que o índice anterior seja sempre o
588+ // elemento anterior
589+ for var i: Word := Pred(HTMLElementCollection.length) downto 0 do
590+ begin
591+ // Obtém o nome real do arquivo a partir do primeiro <atna> existente
592+ // dentro de <atta>
593+ FileName := (((HTMLElementCollection.item(i) as IHTMLElement2).getElementsByTagName('atna') as IHTMLElementCollection4).item(0) as IHTMLElement).innerText;
594+ // Usa o nome real do arquivo para buscar dentre os anexos do comentário o
595+ // anexo que está sendo referenciado
596+ Attachment := AComment.AttachmentByFileName[FileName];
597+ // Caso haja uma anexo, prossegue com sua substituição
598+ if Attachment.Id > 0 then
599+ begin
600+ FileNick := (((HTMLElementCollection.item(i) as IHTMLElement2).getElementsByTagName('atnn') as IHTMLElementCollection4).item(0) as IHTMLElement).innerText;
601+
602+ HTMLAnchorElement := (AElementWithComment.document as IHTMLDocument2).createElement('a') as IHTMLAnchorElement;
603+ HTMLAnchorElement.href := '/ViewAttachment?id=' + Attachment.Id.ToString + '&fn=' + FileName;
604+ (HTMLAnchorElement as IHTMLElement).innerText := FileNick;
605+
606+ (HTMLElementCollection.item(i) as IHTMLDOMNode).replaceNode(HTMLAnchorElement as IHTMLDomNode);
607+ end;
608+ end;
609+ end;
610+ //-///////////////////////////////////////////////////////////////////////////
611+ Result := Trim(AElementWithComment.innerHTML);
612+end;
613+// AElementWithComment é o <td> que contém o comentário propriamente dito
614+procedure ParseCommentsAndAttachments(AElementWithComment: IHTMLElement; var AComment: TComment);
573615 var
574616 HTMLCollection: IHTMLElementCollection;
575617 begin
@@ -576,7 +618,7 @@
576618 // Obtém uma coleção de todos os <a> contidos no comentário, incluindo aqueles
577619 // que estão dentro de outros tags e não são filhos imediatos de
578620 // AUnparsedComment
579- HTMLCollection := (AUnparsedComment as IHTMLElement2).getElementsByTagName('a');
621+ HTMLCollection := (AElementWithComment as IHTMLElement2).getElementsByTagName('a');
580622 // Caso haja <a> precisamos verificar cada um deles para saber se eles se
581623 // referem a anexos e processar quando necessário
582624 if HTMLCollection.length > 0 then
@@ -601,7 +643,7 @@
601643 // AUnparsedComment de uma só vez, usando um loop só para excluir elementos
602644 // que não precisam figurar no comentário final. O loop abaixo também exclui
603645 // <br> do fim do comentário, os quais são desnecessários
604- HTMLCollection := AUnparsedComment.children as IHTMLElementCollection;
646+ HTMLCollection := AElementWithComment.children as IHTMLElementCollection;
605647
606648 if HTMLCollection.length > 0 then
607649 begin
@@ -617,7 +659,7 @@
617659 // também precisam ser excluídos, pois eles não são necessários no fim do
618660 // comentário
619661 if (LowerCase(HTMLElement.tagName) = 'div') or (LowerCase(HTMLElement.tagName) = 'br') then
620- (AUnparsedComment as IHTMLDOMNode).removeChild(HTMLElement as IHTMLDOMNode)
662+ (AElementWithComment as IHTMLDOMNode).removeChild(HTMLElement as IHTMLDOMNode)
621663 // Caso o elemento for <a>, preciasamos fazer algumas verificações antes
622664 // de excluir, pois apenas <a> referentes a anexos precisam ser excluídos
623665 // e há um caso especial onde precisamos excluir um nó de texto que existe
@@ -631,15 +673,17 @@
631673 // que contém o tamanho do anexo, logo, nos livramos dele antes de
632674 // excluir o <a> da vez
633675 if Attachment.FileSize > 0 then
634- (AUnparsedComment as IHTMLDOMNode).removeChild((HTMLElement as IHTMLDOMNode).nextSibling);
676+ (AElementWithComment as IHTMLDOMNode).removeChild((HTMLElement as IHTMLDOMNode).nextSibling);
635677
636- (AUnparsedComment as IHTMLDOMNode).removeChild(HTMLElement as IHTMLDOMNode);
678+ (AElementWithComment as IHTMLDOMNode).removeChild(HTMLElement as IHTMLDOMNode);
637679 end;
638680 end;
639681 end;
640682 end;
641- // Neste ponto só deve existir o texto do comentário
642- AComment.Text := Trim(AUnparsedComment.innerHTML);
683+ // Neste ponto só deve existir o texto do comentário e dentro dele podem
684+ // existir elementos especiais que precisam tratados, por exemplo, elementos
685+ // <atta> que precisam ser substituídos por links para anexos. A função abaixo
686+ AComment.Text := ReplaceSpecialElementsFromComment(AElementWithComment,AComment);
643687 end;
644688
645689 {
@@ -1033,9 +1077,7 @@
10331077 var Comment: TComment;
10341078 var FormsCollection: IHTMLElementCollection;
10351079 var HTMLFormElement: IHTMLFormElement;
1036- // Leia mais adiante porque existem estas variáveis comentadas
1037-// var LinksCollection: IHTMLElementCollection;
1038-// var HTMLLinkElement: IHTMLLinkElement;
1080+ var AnchorsCollection: IHTMLElementCollection;
10391081
10401082 for var i: Word := 0 to Pred(HTMLElementCollection.length) do
10411083 begin
@@ -1107,9 +1149,7 @@
11071149 // que é muito mais útil do que esta funcionalidade que eu estou
11081150 // ignorando
11091151
1110- // Quando não se acha nenhum <form>, ainda pode haver um botão de
1111- // exclusão em um <a>, dentro de CommentDetails, quando trata-se de um
1112- // comentário sem texto, mas com anexos
1152+ // Quando não se acha nenhum <form>, ainda pode haver
11131153 // else
11141154 // begin
11151155 // LinksCollection := (CommentDetails as IHTMLElement2).getElementsByTagName('a');
@@ -1125,7 +1165,28 @@
11251165 // end;
11261166 // end;
11271167
1168+ // Algumas linhas de comentário no Mantis não são comentários
1169+ // propriamente ditos, porque não possuem texto. Estas linhas contém
1170+ // apenas arquivos anexados e que aparecem dentro do fluxo de
1171+ // comentários apenas para que se identifique uma cronologia de quando
1172+ // tais arquivos foram anexados. Linhas de comentário que possuem um
1173+ // botão de exclusão em um <a>, dentro de CommentDetails, tratam-se de
1174+ // comentários sem texto, mas com anexos
1175+ AnchorsCollection := (CommentDetails as IHTMLElement2).getElementsByTagName('a');
11281176
1177+ if AnchorsCollection.length > 0 then
1178+ for var j: Byte := 0 to Pred(AnchorsCollection.length) do
1179+ if Pos('bug_file_delete.php',String(((AnchorsCollection as IHTMLElementCollection4).item(j) as IHTMLAnchorElement).href)) > 0 then
1180+ begin
1181+ // Ao achar bug_file_delete.php no href do link ele
1182+ // provavelmente será o único presente e isso já é suficiente
1183+ // para indicar que este é um comentário "somente anexo", sendo
1184+ // assim basta configurar a propriedade e sair imediatamente do
1185+ // loop
1186+ Comment.AttachmentsOnly := True;
1187+ Break;
1188+ end;
1189+
11291190 // Obtendo a referência a segunda célula da linha, a qual contém o
11301191 // comentário propriamente dito e também referências arquivos
11311192 // anexados juntamente com o comentário, por isso é necessário
@@ -1135,7 +1196,7 @@
11351196
11361197 // Um comentário tem anexos quando há em seu texto <div> ou <a> com
11371198 // href apontando para "file_download.php?file_id=xxxx&type=bug". A
1138- // função ParseCommentsAndAttachments faz isso
1199+ // função ParseCommentsAndAttachments faz isso e muito mais
11391200 ParseCommentsAndAttachments(HTMLElement,Comment);
11401201 // Eu adoro a nova forma de incrementar a arrays a partir do XE7 <3
11411202 ATask.Comments := ATask.Comments + [Comment];
--- trunk/client/src/lib/UTypes.pas (revision 69)
+++ trunk/client/src/lib/UTypes.pas (revision 70)
@@ -105,6 +105,9 @@
105105 TCommentType = (ctRegular,ctTestsApproval,ctTestsRejection,ctHomologationApproval,ctHomologationRejection);
106106
107107 TComment = record
108+ private
109+ function GetAttachmentsCount: Word;
110+ function GetAttachmentByFileName(AFileName: String): TAttachment;
108111 public
109112 Id: Cardinal;
110113 Created: TDateTime;
@@ -118,8 +121,12 @@
118121 CanModify: Boolean;
119122 CanDelete: Boolean;
120123 CanChangeVisibility: Boolean;
124+ AttachmentsOnly: Boolean;
121125
122126 procedure AddOrUpdateAttachment(const AAttachment: TAttachment);
127+
128+ property AttachmentsCount: Word read GetAttachmentsCount;
129+ property AttachmentByFileName[AFileName: String]: TAttachment read GetAttachmentByFileName;
123130 end;
124131
125132 TComments = array of TComment;
@@ -206,6 +213,7 @@
206213 function GetCommentsCount: Word;
207214 function GetHistoryEntryCount: Word;
208215 function GetRelatedTasksCount: Word;
216+ function GetCommentById(AId: Cardinal): TComment;
209217 public
210218 procedure Clear(AAllButId: Boolean = True);
211219
@@ -234,6 +242,7 @@
234242 property CommentsCount: Word read GetCommentsCount;
235243 property HistoryEntryCount: Word read GetHistoryEntryCount;
236244 property RelatedTasksCount: Word read GetRelatedTasksCount;
245+ property CommentById[AId: Cardinal]: TComment read GetCommentById;
237246 end;
238247
239248 TTasks = array of TTask;
@@ -402,6 +411,18 @@
402411 Inc(Result,Length(Comment.Attachments));
403412 end;
404413
414+function TTask.GetCommentById(AId: Cardinal): TComment;
415+begin
416+ Result := Default(TComment);
417+
418+ for var Comment: TComment in FComments do
419+ if Comment.Id = AId then
420+ begin
421+ Result := Comment;
422+ Break;
423+ end;
424+end;
425+
405426 function TTask.GetCommentsCount: Word;
406427 begin
407428 Result := Length(FComments);
@@ -508,4 +529,21 @@
508529 Attachments := Attachments + [AAttachment];
509530 end;
510531
532+function TComment.GetAttachmentByFileName(AFileName: String): TAttachment;
533+begin
534+ Result := Default(TAttachment);
535+
536+ for var Attachment: TAttachment in Attachments do
537+ if LowerCase(Attachment.FileName) = LowerCase(AFileName) then
538+ begin
539+ Result := Attachment;
540+ Break;
541+ end;
542+end;
543+
544+function TComment.GetAttachmentsCount: Word;
545+begin
546+ Result := Length(Attachments);
547+end;
548+
511549 end.
--- trunk/client/src/UDamoTask.pas (revision 69)
+++ trunk/client/src/UDamoTask.pas (revision 70)
@@ -183,7 +183,7 @@
183183 Comment: String;
184184 CommentToken: String;
185185 begin
186- if TFormManageNote.ShowMeModal(TForm(Owner),FTask.Id,ACommentId,Comment,CommentToken) = mrOk then
186+ if TFormManageNote.ShowMeModal(Self,FTask,ACommentId,Comment,CommentToken) = mrOk then
187187 begin
188188 CommentBackupCreate(ACommentId,Comment);
189189
@@ -202,7 +202,7 @@
202202 Comment: String;
203203 Dummy: String;
204204 begin
205- if TFormManageNote.ShowMeModal(TForm(Owner),FTask.Id,0,Comment,Dummy) = mrOk then
205+ if TFormManageNote.ShowMeModal(Self,FTask,0,Comment,Dummy) = mrOk then
206206 Application.MessageBox('Código para salvar novo comentário','A fazer',MB_ICONINFORMATION);
207207 end;
208208
--- trunk/client/src/UFormManageNote.pas (revision 69)
+++ trunk/client/src/UFormManageNote.pas (revision 70)
@@ -6,7 +6,8 @@
66 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
77 Dialogs, UFormBasicDialog, StdCtrls, Buttons, UPngBitBtn, ExtCtrls, PngImage,
88 KRK.Internet.Edge, Winapi.ActiveX, Vcl.ComCtrls, UInterposersAndHelpers,
9- System.ImageList, Vcl.ImgList, Vcl.Menus, Vcl.ActnPopup, Vcl.XPStyleActnCtrls;
9+ System.ImageList, Vcl.ImgList, Vcl.Menus, Vcl.ActnPopup, Vcl.XPStyleActnCtrls,
10+ UTypes, UDamoTask;
1011
1112 type
1213 TFormManageNote = class(TFormBasicDialog)
@@ -18,6 +19,7 @@
1819 IMLIAttachments: TImageList;
1920 PUABAttachments: TPopupActionBar;
2021 MNUICopyReferenceToFile: TMenuItem;
22+ PANEAttachments: TPanel;
2123 procedure EDBRExecuteScript(Sender: TCustomEdgeBrowser; AResult: HRESULT; const AResultObjectAsJson: string);
2224 procedure EDBRCreateWebViewCompleted(Sender: TCustomEdgeBrowser; AResult: HRESULT);
2325 procedure FormCreate(Sender: TObject);
@@ -24,23 +26,27 @@
2426 procedure EDBRWebResourceRequested(Sender: TCustomEdgeBrowser; Args: TWebResourceRequestedEventArgs);
2527 procedure FormDestroy(Sender: TObject);
2628 procedure FormShow(Sender: TObject);
27- procedure LIVIAttachmentsChange(Sender: TObject; Item: TListItem;
28- Change: TItemChange);
29- procedure LIVIAttachmentsKeyUp(Sender: TObject; var Key: Word;
30- Shift: TShiftState);
29+ procedure LIVIAttachmentsChange(Sender: TObject; Item: TListItem; Change: TItemChange);
30+ procedure LIVIAttachmentsKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
3131 procedure MNUICopyReferenceToFileClick(Sender: TObject);
32+ procedure PANEAttachmentsClick(Sender: TObject);
3233 private
3334 { Private declarations }
35+ FDamoTask: TDamoTask;
36+ FTask: TTask;
37+ FComment: String;
38+ FCommentId: Cardinal;
3439 FUpdating: Boolean;
35- FComment: String;
3640 FPostContent: TStringStream;
3741
3842 procedure DoDropFiles(ASender: TObject; const ADropHandle: LRESULT; out AMessageResult: LRESULT);
43+ procedure ConfigureAttachments;
44+ procedure AddAttachment(AFileName: String; AFileSize: Cardinal);
3945 protected
4046 procedure ValidateSave; override;
4147 public
4248 { Public declarations }
43- class function ShowMeModal(AOwner: TComponent; ATaskNumber, ACommentId: Cardinal; out AComment: String; out AUpdateToken: String): TModalResult; reintroduce;
49+ class function ShowMeModal(ADamoTask: TDamoTask; ATask: TTask; ACommentId: Cardinal; out AComment: String; out AUpdateToken: String): TModalResult; reintroduce;
4450 end;
4551
4652 implementation
@@ -60,38 +66,92 @@
6066 Application.MessageBox(PChar('A referência ao arquivo "' + LIVIAttachments.Selected.Caption + '" foi adicionada à área de transferência. Você pode colá-la no editor de comentário, onde é possível também dar um apelido a esta referência, selecionando-a e clicando no ícone com um clipe de papel'),'Referência copiada',MB_ICONINFORMATION);
6167 end;
6268
63-procedure TFormManageNote.DoDropFiles(ASender: TObject; const ADropHandle: LRESULT; out AMessageResult: LRESULT);
69+procedure TFormManageNote.PANEAttachmentsClick(Sender: TObject);
70+begin
71+ inherited;
72+
73+ if FDamoTask.OPDIAttachment.Execute then
74+ begin
75+ var Info: TWin32FileAttributeData;
76+
77+ if not GetFileAttributesEx(PChar(FDamoTask.OPDIAttachment.FileName), GetFileExInfoStandard, @Info) then
78+ AddAttachment(FDamoTask.OPDIAttachment.FileName,0)
79+ else
80+ AddAttachment(FDamoTask.OPDIAttachment.FileName,Int64(Info.nFileSizeLow) or Int64(Info.nFileSizeHigh shl 32));
81+ end;
82+end;
83+
84+procedure TFormManageNote.AddAttachment(AFileName: String; AFileSize: Cardinal);
6485 var
86+ FileInfo: SHFILEINFO;
6587 Icon: TIcon;
6688 begin
89+ Icon := TIcon.Create;
90+ try
91+ with LIVIAttachments.Items.Add do
92+ begin
93+ SHGetFileInfo(PChar(AFileName), 0, FileInfo, SizeOf(SHFILEINFO), SHGFI_TYPENAME or SHGFI_ICON or SHGFI_SMALLICON or SHGFI_USEFILEATTRIBUTES);
94+
95+ Icon.Handle := FileInfo.hIcon;
96+ ImageIndex := LIVIAttachments.SmallImages.AddIcon(Icon);
97+ DestroyIcon(Icon.Handle);
98+
99+ Caption := ExtractFileName(AFileName);
100+ SubItems.Add(FormatFloat('###,###,###,##0', AFileSize) + ' Bytes');
101+ SubItems.Add(FileInfo.szTypeName);
102+
103+ if AFileName <> Caption then
104+ SubItems.Add(ExtractFilePath(AFileName));
105+ end;
106+ finally
107+ Icon.Free;
108+ end;
109+end;
110+
111+procedure TFormManageNote.ConfigureAttachments;
112+begin
113+ // A aba de anexos só é visível se estamos no modo de inserção, ou no modo de
114+ // edição quando há anexos associados ao comentário
115+ TASHAttachments.TabVisible := (not FUpdating) or (FTask.CommentById[FCommentId].AttachmentsCount > 0);
116+ // Caso a aba de anexos esteja visível e estivermos no modo de edição,
117+ // precisamos carregar os anexos no ListView, que, neste caso, não aceitará
118+ // mais anexos, servido apenas para que possamos copiar referências a eles
119+ if TASHAttachments.TabVisible and FUpdating then
120+ begin
121+ DragAcceptFiles(LIVIAttachments.Handle, False);
122+
123+ LIVIAttachments.OnKeyUp := nil;
124+ LIVIAttachments.MultiSelect := False;
125+ LIVIAttachments.Columns.Delete(3); // não tem caminho para o arquivo nessa visualização
126+ LIVIAttachments.Margins.Bottom := 7;
127+ PANEAttachments.Hide;
128+
129+ LIVIAttachments.Items.BeginUpdate;
130+ try
131+ for var Attachment in FTask.CommentById[FCommentId].Attachments do
132+ AddAttachment(Attachment.FileName,Attachment.FileSize);
133+ finally
134+ LIVIAttachments.Items.EndUpdate;
135+ end;
136+ end;
137+end;
138+
139+procedure TFormManageNote.DoDropFiles(ASender: TObject; const ADropHandle: LRESULT; out AMessageResult: LRESULT);
140+begin
67141 // Parte do código existente em TFileCatcher que diz respeito a obtenção de
68142 // informações sobre arquivos foi obtido a partir de
69143 // https://www.swissdelphicenter.ch/en/showcode.php?id=421
70144
71145 TListView(ASender).Items.BeginUpdate;
72- Icon := TIcon.Create;
73146 try
74147 with TFileCatcher.Create(ADropHandle) do
75148 try
76149 for var i: Cardinal := 0 to Pred(FileCount) do
77- begin
78- with TListView(ASender).Items.Add do
79- begin
80- Caption := ExtractFileName(FileNames[i]);
81- SubItems.Add(FormatFloat('###,###,###,##0', FileSizes[i]) + ' Bytes');
82- SubItems.Add(FileTypes[i]);
83- Subitems.Add(ExtractFilePath(FileNames[i]));
84-
85- Icon.Handle := FileIcons[i];
86- ImageIndex := TListView(ASender).SmallImages.AddIcon(Icon);
87- DestroyIcon(Icon.Handle);
88- end;
89- end;
150+ AddAttachment(FileNames[i],FileSizes[i]);
90151 finally
91152 Free;
92153 end;
93154 finally
94- Icon.Free;
95155 TListView(ASender).Items.EndUpdate;
96156 end;
97157
@@ -181,7 +241,8 @@
181241 // Navigate ou NavigateToString
182242 EDBR.CreateWebView;
183243
184- TASHAttachments.TabVisible := not FUpdating;
244+ ConfigureAttachments;
245+
185246 PACO.ActivePage := TASHComment;
186247 end;
187248
@@ -194,6 +255,8 @@
194255 else
195256 for var i: Byte := 0 to Pred(TListView(Sender).Columns.Count) do
196257 TListView(Sender).Columns[i].Width := -1;
258+
259+ TASHAttachments.Caption := 'Anexos (' + TListView(Sender).Items.Count.ToString + ')';
197260 end;
198261
199262 procedure TFormManageNote.LIVIAttachmentsKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
@@ -201,18 +264,26 @@
201264 inherited;
202265
203266 if Key = VK_DELETE then
267+ begin
204268 LIVIAttachments.DeleteSelected;
269+ LIVIAttachmentsChange(Sender,nil,ctText);
270+ end;
205271 end;
206272
207-class function TFormManageNote.ShowMeModal(AOwner: TComponent; ATaskNumber, ACommentId: Cardinal; out AComment: String; out AUpdateToken: String): TModalResult;
273+class function TFormManageNote.ShowMeModal(ADamoTask: TDamoTask; ATask: TTask; ACommentId: Cardinal; out AComment: String; out AUpdateToken: String): TModalResult;
208274 begin
209275 Result := mrAbort; // Se tudo der errado...
210276
211- with Self.Create(AOwner) do
277+ with Self.Create(ADamoTask.Owner) do
212278 begin
213- FUpdating := ACommentId > 0;
279+ FDamoTask := ADamoTask;
280+ FTask := ATask;
281+ FCommentId := ACommentId;
214282 AComment := '';
283+ AUpdateToken := '';
284+
215285 FComment := '';
286+ FUpdating := FCommentId > 0;
216287
217288 AutoCaption := False;
218289
@@ -219,9 +290,9 @@
219290 Caption := 'Gerenciador de anotações - ' + Application.Title;
220291
221292 if FUpdating then
222- LABECaption.Caption := 'Alterando a anotação ~' + ACommentId.ToString + ' da tarefa #' + ATaskNumber.ToString
293+ LABECaption.Caption := 'Alterando a anotação ~' + FCommentId.ToString + ' da tarefa #' + ATask.Id.ToString
223294 else
224- LABECaption.Caption := 'Redigindo uma nova anotação para a tarefa #' + ATaskNumber.ToString;
295+ LABECaption.Caption := 'Redigindo uma nova anotação para a tarefa #' + ATask.Id.ToString;
225296
226297 // Se estamos tentando editar um comentário..
227298 if FUpdating then
@@ -229,7 +300,7 @@
229300 // É obrigatório sempre executar esta função, pois mesmo que não usemos os
230301 // comentário original, ainda precisaremos do UpdateToken. Caso a função
231302 // não seja bem sucedida, terminamos o form com uma mensagem de erro
232- if not GetCommentForEdition(Handle,ACommentId,FComment,AUpdateToken) then
303+ if not GetCommentForEdition(Handle,FCommentId,FComment,AUpdateToken) then
233304 begin
234305 Application.MessageBox('Não foi possível obter um comentário para edição. Por favor, tente novamente!','Erro ao obter um comentário',MB_ICONERROR);
235306 Close;
@@ -236,9 +307,9 @@
236307 end
237308 // Caso exista um backup, pergunta se quer carregar. Caso se responda não
238309 // ou caso não haja backup, será carregado o comentário obtido do Mantis
239- else if CommentBackupExists(ACommentId) and (Application.MessageBox('Existe um backup do texto do comentário atual, deseja carregá-lo?','Deseja carregar o backup?',MB_ICONQUESTION or MB_YESNO) = IDYES) then
310+ else if CommentBackupExists(FCommentId) and (Application.MessageBox('Existe um backup do texto do comentário atual, deseja carregá-lo?','Deseja carregar o backup?',MB_ICONQUESTION or MB_YESNO) = IDYES) then
240311 begin
241- FComment := CommentBackupLoad(ACommentId);
312+ FComment := CommentBackupLoad(FCommentId);
242313 Result := ShowModal;
243314 AComment := FComment;
244315 end
--- trunk/client/src/UFormTask.pas (revision 69)
+++ trunk/client/src/UFormTask.pas (revision 70)
@@ -82,7 +82,7 @@
8282 procedure FormDestroy(Sender: TObject);
8383 procedure EDBRCommentsCreateWebViewCompleted(Sender: TCustomEdgeBrowser; AResult: HRESULT);
8484 procedure EDBRCommentsWebResourceRequested(Sender: TCustomEdgeBrowser; Args: TWebResourceRequestedEventArgs);
85- procedure EDBRCommentsNavigationStarting(Sender: TCustomEdgeBrowser; Args: TNavigationStartingEventArgs);
85+ procedure DoNavigationStarting(Sender: TCustomEdgeBrowser; Args: TNavigationStartingEventArgs);
8686 procedure EDBRDescriptionCreateWebViewCompleted(Sender: TCustomEdgeBrowser; AResult: HRESULT);
8787 procedure EDBRDescriptionWebResourceRequested(Sender: TCustomEdgeBrowser; Args: TWebResourceRequestedEventArgs);
8888 procedure EDBRAdditionalInformationCreateWebViewCompleted(Sender: TCustomEdgeBrowser; AResult: HRESULT);
@@ -90,9 +90,12 @@
9090 procedure EDBRStepsToReproduceCreateWebViewCompleted(Sender: TCustomEdgeBrowser; AResult: HRESULT);
9191 procedure EDBRStepsToReproduceWebResourceRequested(Sender: TCustomEdgeBrowser; Args: TWebResourceRequestedEventArgs);
9292 procedure ACTNRefreshExecute(Sender: TObject);
93+ procedure TASHDescriptionShow(Sender: TObject);
9394 private
9495 { Private declarations }
9596 FDamoTask: TDamoTask;
97+ FCommentId: Cardinal;
98+ FDescriptionLoaded: Boolean;
9699 FAttachmentsLoaded: Boolean;
97100 FCommentsAndAttachmentsLoaded: Boolean;
98101 FAdditionalInfoLoaded: Boolean;
@@ -118,10 +121,10 @@
118121 procedure CreateParams(var Params: TCreateParams); override;
119122 public
120123 { Public declarations }
121- constructor Create(AOwner: TComponent; ATaskId: Cardinal); reintroduce;
124+ constructor Create(AOwner: TComponent; ATaskId: Cardinal; ACommentId: Cardinal = 0); reintroduce;
122125
123- class function ShowMe(var ATask: TTask; AModal: Boolean = False): TModalResult; overload;
124- class function ShowMe(ATaskId: Cardinal; AModal: Boolean = False): TModalResult; overload;
126+ class function ShowMe(var ATask: TTask; ACommentId: Cardinal = 0; AModal: Boolean = False): TModalResult; overload;
127+ class function ShowMe(ATaskId: Cardinal; ACommentId: Cardinal = 0; AModal: Boolean = False): TModalResult; overload;
125128
126129 procedure InitializeFormTask(var ATask: TTask);
127130 end;
@@ -141,9 +144,10 @@
141144 FDamoTask.Refresh;
142145 end;
143146
144-constructor TFormTask.Create(AOwner: TComponent; ATaskId: Cardinal);
147+constructor TFormTask.Create(AOwner: TComponent; ATaskId: Cardinal; ACommentId: Cardinal = 0);
145148 begin
146149 FDamoTask := TDamoTask.Create(Self);
150+ FCommentId := ACommentId;
147151
148152 inherited Create(AOwner);
149153 // É necessário dar um nome único ao TDataModule para que cada instância
@@ -199,10 +203,10 @@
199203 begin
200204 inherited;
201205 Sender.AddWebResourceRequestedFilter('*MantisBTMonitorComments.php',COREWEBVIEW2_WEB_RESOURCE_CONTEXT_ALL);
202- Sender.Navigate(Configurations.MantisBTBaseUrl + '/mantis/config/MantisBTMonitorComments.php');
206+ Sender.Navigate(Configurations.MantisBTBaseUrl + '/mantis/config/MantisBTMonitorComments.php#' + FCommentId.ToString);
203207 end;
204208
205-procedure TFormTask.EDBRCommentsNavigationStarting(Sender: TCustomEdgeBrowser; Args: TNavigationStartingEventArgs);
209+procedure TFormTask.DoNavigationStarting(Sender: TCustomEdgeBrowser; Args: TNavigationStartingEventArgs);
206210 var
207211 URI: PChar;
208212 begin
@@ -385,7 +389,7 @@
385389 procedure TFormTask.HandleOpenTaskMessage(var AMessage: TMessage);
386390 begin
387391 // AMessage.LParam contem o código do comentário, mas não o estou usando aqui
388- TFormTask.ShowMe(AMessage.WParam);
392+ TFormTask.ShowMe(AMessage.WParam,AMessage.LParam);
389393 end;
390394
391395 procedure TFormTask.IMAGHeaderClick(Sender: TObject);
@@ -411,8 +415,8 @@
411415 FDamoTask.Task := ATask;
412416
413417 LoadTaskHeader;
414- LoadDescription;
415418
419+ FDescriptionLoaded := False;
416420 FAdditionalInfoLoaded := False;
417421 FStepsToReproduceLoaded := False;
418422 FAttachmentsLoaded := False;
@@ -419,7 +423,13 @@
419423 FHistoryLoaded := False;
420424 FCommentsAndAttachmentsLoaded := False;
421425
422- PACO.ActivePage := TASHDescription;
426+ // Configurar o ActivePage de um TTabSheet executa seu evento OnShow. Abaixo
427+ // estamos abrindo a página correta de acordo com ter ou não ter um id de
428+ // comentário informado
429+ if FCommentId > 0 then
430+ PACO.ActivePage := TASHComments
431+ else
432+ PACO.ActivePage := TASHDescription;
423433 end;
424434
425435 procedure TFormTask.KRDGRelatedTasksDblClick(Sender: TObject);
@@ -449,12 +459,9 @@
449459 // OnCreateWebViewCompleted, no qual um filtro é adicionado e onde é feita a
450460 // navegação para a página de informações adicionais
451461 EDBRAdditionalInformation.ReinitializeWebView;
452-// LoadWebBrowserHTML(WEBRAdditionalInformation,FDamoTask.Task.AdditionalInformation);
453462
454463 FAdditionalInfoLoaded := True;
455464 end;
456-
457-// WEBRAdditionalInformation.AddWindowSubClass;
458465 end;
459466
460467 procedure TFormTask.LoadAttachmentsAndRelatedTasks;
@@ -483,14 +490,15 @@
483490
484491 procedure TFormTask.LoadDescription;
485492 begin
486- // Reinicializa o WebView o que provoca a execução de
487- // OnCreateWebViewCompleted, no qual um filtro é adicionado e onde é feita a
488- // navegação para a página de descrição
489- EDBRDescription.ReinitializeWebView;
493+ if not FDescriptionLoaded then
494+ begin
495+ // Reinicializa o WebView o que provoca a execução de
496+ // OnCreateWebViewCompleted, no qual um filtro é adicionado e onde é feita a
497+ // navegação para a página de descrição
498+ EDBRDescription.ReinitializeWebView;
490499
491-// LoadWebBrowserHTML(WEBRDescription,FDamoTask.Task.Description);
492-// WEBRDescription.AddWindowSubClass;
493-// WEBRDescription.DisablePopUpMenu := True;
500+ FDescriptionLoaded := True;
501+ end;
494502 end;
495503
496504 procedure TFormTask.LoadHistory;
@@ -544,16 +552,16 @@
544552 Close;
545553 end;
546554
547-class function TFormTask.ShowMe(ATaskId: Cardinal; AModal: Boolean): TModalResult;
555+class function TFormTask.ShowMe(ATaskId: Cardinal; ACommentId: Cardinal = 0; AModal: Boolean = False): TModalResult;
548556 var
549557 Task: TTask;
550558 begin
551559 Task.Id := ATaskId;
552560
553- Result := ShowMe(Task,AModal);
561+ Result := ShowMe(Task,ACommentId,AModal);
554562 end;
555563
556-class function TFormTask.ShowMe(var ATask: TTask; AModal: Boolean = False): TModalResult;
564+class function TFormTask.ShowMe(var ATask: TTask; ACommentId: Cardinal = 0; AModal: Boolean = False): TModalResult;
557565 var
558566 i: Word;
559567 begin
@@ -564,7 +572,7 @@
564572 Break;
565573
566574 if i = Application.ComponentCount then
567- with Self.Create(Application,ATask.Id) do
575+ with Self.Create(Application,ATask.Id,ACommentId) do
568576 begin
569577 AutoCaption := True;
570578
@@ -588,8 +596,28 @@
588596 else
589597 Show;
590598 end
599+ // Ao chegar neste ponto estamos tentando abrir uma janela que já está aberta.
600+ // Neste caso, simplesmente coloca esta janela na frente das outras e lida com
601+ // uma possível referência a um comentário
591602 else
603+ begin
604+ TFormTask(Application.Components[i]).FCommentId := ACommentId;
605+ // Caso o id de um comentário tenha sido informado e caso os comentários
606+ // nesta janela previamente aberta já tiverem sido carregados, precisamos
607+ // executar uma função JavaScript para ir para o comentário. Caso os
608+ // comentários não tenham sido carregados ainda, a simples atribuição a
609+ // FCommentId na linha anterior é suficiente para que os comentários sejam
610+ // carregados apontando para o comentário contido em FCommentId
611+ if (ACommentId > 0) then
612+ begin
613+ if TFormTask(Application.Components[i]).FCommentsAndAttachmentsLoaded then
614+ TFormTask(Application.Components[i]).EDBRComments.ExecuteScript('goTo("' + ACommentId.ToString + '")');
615+
616+ TFormTask(Application.Components[i]).TASHComments.Show;
617+ end;
618+
592619 TForm(Application.Components[i]).BringToFront;
620+ end;
593621 end;
594622
595623 procedure TFormTask.TASHAdditionalInformationShow(Sender: TObject);
@@ -610,6 +638,12 @@
610638 LoadComments;
611639 end;
612640
641+procedure TFormTask.TASHDescriptionShow(Sender: TObject);
642+begin
643+ inherited;
644+ LoadDescription;
645+end;
646+
613647 procedure TFormTask.TASHHistoryShow(Sender: TObject);
614648 begin
615649 inherited;
Show on old repository browser