• R/O
  • SSH
  • HTTPS

mantisbtmonitor: Commit


Commit MetaInfo

Revision54 (tree)
Time2021-10-05 07:22:53
Authorderekwildstar

Log Message

Mais funções em UFunctions.pas e UScrapFunctions.pas
A janela de detalhamento de tarefas já abre corretamente, mas ainda precisa ser revisada e ajustada ao scrap. Comentários ainda não são carregados, nem anexos e nem tarefas relacionadas
Mudanças básicas de nomenclatura Issue -> Task

Change Summary

Incremental Difference

--- trunk/client/prj/MantisNotification.dproj (revision 53)
+++ trunk/client/prj/MantisNotification.dproj (revision 54)
@@ -114,9 +114,9 @@
114114 <AppDPIAwarenessMode>PerMonitor</AppDPIAwarenessMode>
115115 <VerInfo_MinorVer>0</VerInfo_MinorVer>
116116 <VerInfo_Release>0</VerInfo_Release>
117- <VerInfo_Build>296</VerInfo_Build>
117+ <VerInfo_Build>349</VerInfo_Build>
118118 <VerInfo_Locale>1033</VerInfo_Locale>
119- <VerInfo_Keys>CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.296;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.349;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>
--- trunk/client/src/lib/UFunctions.pas (revision 53)
+++ trunk/client/src/lib/UFunctions.pas (revision 54)
@@ -16,23 +16,23 @@
1616
1717 TSanitizeFlags = set of TSanitizeFlag;
1818
19- TIssueInfo = record
20- Id: Integer;
21- ProjectName: String;
22- Summary: String;
23- TargetVersion: String;
24- Status: Word;
25- LastUpdated: TDateTime;
26- Reporter: String;
27- Description: String;
28- StepsToReproduce: String;
29- AdditionalInformation: String;
30- end;
19+// TIssueInfo = record
20+// Id: Integer;
21+// ProjectName: String;
22+// Summary: String;
23+// TargetVersion: String;
24+// Status: Word;
25+// LastUpdated: TDateTime;
26+// Reporter: String;
27+// Description: String;
28+// StepsToReproduce: String;
29+// AdditionalInformation: String;
30+// end;
3131
32-function DecodeIssueInfo(AIssueData: String): TIssueInfo;
33-procedure OpenIssueWithMantis(AIssueNumber: Cardinal);
34-procedure DownloadAttachment(AAttachmentId: Cardinal; AFileName: String; AOpen: Boolean = True);
35-function UploadAttachment(AIssueNumber: Cardinal; AFileName: String): Cardinal;
32+//function DecodeIssueInfo(AIssueData: String): TIssueInfo;
33+procedure OpenTaskWithMantis(ATaskNumber: Cardinal);
34+//procedure DownloadAttachment(AAttachmentId: Cardinal; AFileName: String; AOpen: Boolean = True);
35+//function UploadAttachment(AIssueNumber: Cardinal; AFileName: String): Cardinal;
3636 procedure LoadWebBrowserHTML(AWebBrowser: TWebBrowser; AText: String; ASanitizeFlags: TSanitizeFlags = [sfDeactivateLinks,sfDeactivateOtherTags,sfActivateAllowedTags,sfMakeSpacesAndTabsPreserved,sfMakeReturnsPreserved,sfActivateLinks]; ADecode: Boolean = False; ABackgroundColor: String = '#FFFFFF');
3737 procedure GenerateSimpleComments(AWebBrowser: TWebBrowser; AClientDataSet: TClientDataSet);
3838 procedure RenderProjectNameAsMenuHeader(ATexto: String; ACanvas: TCanvas; ARect: TRect; AStatusColor: TColor);
@@ -226,7 +226,7 @@
226226 // }
227227 //
228228 function SanitizeString(AString: String; AFlags: Word): String;
229-////////////////////////////////////////////////////////////////////////////////
229+//-/////////////////////////////////////////////////////////////////////////////
230230 procedure RemoveLinks(var S: String);
231231 var
232232 Subject: String;
@@ -454,35 +454,35 @@
454454 // end;
455455 //end;
456456
457-procedure OpenIssueWithMantis(AIssueNumber: Cardinal);
457+procedure OpenTaskWithMantis(ATaskNumber: Cardinal);
458458 begin
459- ShellExecute(0, 'open', PChar(Format(Configurations.BaseUrl + '/mantis/view.php?id=%u',[AIssueNumber])), nil, nil, SW_SHOWNORMAL);
459+ ShellExecute(0, 'open', PChar(Format(Configurations.BaseUrl + '/mantis/view.php?id=%u',[ATaskNumber])), nil, nil, SW_SHOWNORMAL);
460460 end;
461461
462-function DecodeIssueInfo(AIssueData: String): TIssueInfo;
463-begin
464- ZeroMemory(@Result,SizeOf(TIssueInfo));
465- with TStringList.Create do
466- try
467- StrictDelimiter := True;
468- Delimiter := '¬';
469- DelimitedText := AIssueData;
462+//function DecodeIssueInfo(AIssueData: String): TIssueInfo;
463+//begin
464+// ZeroMemory(@Result,SizeOf(TIssueInfo));
465+// with TStringList.Create do
466+// try
467+// StrictDelimiter := True;
468+// Delimiter := '¬';
469+// DelimitedText := AIssueData;
470+//
471+// Result.Id := StrToInt(Strings[0]);
472+// Result.ProjectName := Strings[1];
473+// Result.Summary := Strings[2];
474+// Result.TargetVersion := Strings[3];
475+// Result.Status := StrToInt(Strings[4]);
476+// Result.LastUpdated := StrToDateTime(Strings[5]);
477+// Result.Reporter := Strings[6];
478+// Result.Description := Strings[7];
479+// Result.StepsToReproduce := Strings[8];
480+// Result.AdditionalInformation := Strings[9];
481+// finally
482+// Free;
483+// end;
484+//end;
470485
471- Result.Id := StrToInt(Strings[0]);
472- Result.ProjectName := Strings[1];
473- Result.Summary := Strings[2];
474- Result.TargetVersion := Strings[3];
475- Result.Status := StrToInt(Strings[4]);
476- Result.LastUpdated := StrToDateTime(Strings[5]);
477- Result.Reporter := Strings[6];
478- Result.Description := Strings[7];
479- Result.StepsToReproduce := Strings[8];
480- Result.AdditionalInformation := Strings[9];
481- finally
482- Free;
483- end;
484-end;
485-
486486 procedure DownloadAttachment(AAttachmentId: Cardinal; AFileName: String; AOpen: Boolean = True);
487487 var
488488 SS: TStringStream;
@@ -565,21 +565,21 @@
565565 SF: Word;
566566 SanitizeFlag: TSanitizeFlag;
567567 begin
568- if ADeCode then
569- ABody := StringReplace(UTF8ToString(RawByteString(DecodeString(ABody))),#13#10,'<br>',[rfReplaceAll])
570- else
571- ABody := StringReplace(ABody,#13#10,'<br>',[rfReplaceAll]);
568+// if ADeCode then
569+// ABody := StringReplace(UTF8ToString(RawByteString(DecodeString(ABody))),#13#10,'<br>',[rfReplaceAll])
570+// else
571+// ABody := StringReplace(ABody,#13#10,'<br>',[rfReplaceAll]);
572572
573573 // Ao depurar o loop abaixo, notei que ele circula por todos os valores
574574 // possíveis de TSanitizeFlag, de zero a 32, mas só de fato entra no loop para
575575 // incrementar SF para os flags que foram definidos. Em suma, funciona, mas é
576- // um pouco estrnho e dispendioso em termos de performance, contudo, nada que
576+ // um pouco estranho e dispendioso em termos de performance, contudo, nada que
577577 // prejudique o sistema
578- SF := 0;
579- for SanitizeFlag in ASanitizeFlags do
580- Inc(SF,Integer(SanitizeFlag));
578+// SF := 0;
579+// for SanitizeFlag in ASanitizeFlags do
580+// Inc(SF,Integer(SanitizeFlag));
581581
582- ABody := SanitizeString(ABody,SF);
582+// ABody := SanitizeString(ABody,SF);
583583 Result := Trim(Format(HTML,[ABackgroundColor,ABody]));
584584 end;
585585 ////////////////////////////////////////////////////////////////////////////////
--- trunk/client/src/lib/UScrapFunctions.pas (revision 53)
+++ trunk/client/src/lib/UScrapFunctions.pas (revision 54)
@@ -24,7 +24,6 @@
2424 AssignedProjects: TAssignedProjects;
2525
2626 function FirstName: String;
27-
2827 end;
2928
3029 TStatusColor = record
@@ -105,6 +104,7 @@
105104 procedure GetStatusColors(AHandle: Cardinal; out AStatusColors: TStatusColors);
106105 function GetUserInfo(AHandle: Cardinal; out AUserInfo: TUserInfo): Boolean;
107106 function AssignedTasks(AHandle: Cardinal; AStatusColors: TStatusColors; out ATasks: TTasks; AFullInfo: Boolean = False): Boolean;
107+function TaskDetails(AHandle: Cardinal; AStatusColors: TStatusColors; var ATask: TTask): Boolean;
108108
109109 implementation
110110
@@ -111,7 +111,7 @@
111111 uses
112112 WinApi.Windows, WinApi.WinInet, System.Classes, System.SysUtils, MSHTML,
113113 KRK.Rtl.Win.WinInet.Utilities, KRK.RegExp.Utils, UConfigurations,
114- Vcl.Graphics;
114+ Vcl.Graphics, System.Variants;
115115
116116 function FixEncoding(AText: OleVariant): String;
117117 var
@@ -475,13 +475,219 @@
475475 Result := StrToDateTime(Trim(DateTime));
476476 end;
477477
478+function ExtractSummary(ASummary: OleVariant): String;
479+begin
480+ RegExMatch(FixEncoding(ASummary),'\d{7}:\s(.*)',1,1,False,[],Result);
481+end;
482+
483+function ParseCommentId(AUnparsedCommentId: OleVariant): Cardinal;
484+var
485+ Aux: String;
486+begin
487+ Aux := AUnparsedCommentId;
488+ Delete(Aux,1,1);
489+ Result := Aux.ToInteger;
490+end;
491+
492+function ExtractUserId(AViewUserPageUrl: String): Cardinal;
493+var
494+ UserId: String;
495+begin
496+ RegExMatch(AViewUserPageUrl,'id=([0-9]+)',1,1,False,[],UserId);
497+
498+ Result := UserId.ToInteger;
499+end;
500+
501+function ParseAttachmentIdFromDivId(ADivId: String): Cardinal;
502+var
503+ Id: String;
504+begin
505+ Result := 0;
506+
507+ if RegExMatch(ADivId,'attachment_preview_(\d*)_open',1,1,False,[],Id) then
508+ Result := Id.ToInteger;
509+end;
510+
511+function ParseAttachmentIdFromAHref(AHref: String; out AAttachmentId: Cardinal): Boolean;
512+var
513+ AttachmentId: String;
514+begin
515+ Result := RegExMatch(AHref,'bug_file_delete\.php\?file_id=(\d*).*bug_file_delete_token=(.*)',1,1,False,[],AttachmentId);
516+
517+ if Result then
518+ AAttachmentId := AttachmentId.ToInteger;
519+end;
520+
521+
522+// O HTML é um tipo de XML, logo, o HTML é "filho" de XML. Todos os elementos do
523+// HTML são nós no XML, por isso elementos html podem ser personificados em nós
524+// XML. Quando ser quer manipular o HTML sem se preocupar com seus elementos, é
525+// sempre melhor usar a abordagem XML, a qual permite alterar a estrutura do
526+// mesmo. Para isso, use IHTMLDOMNode
527+
528+// AUnparsedComment é o <td> que contém o comentário propriamente dito
529+procedure ParseCommentsAndAttachments(AUnparsedComment: IHTMLElement; out AComment: TComment);
530+var
531+ i: SmallInt;
532+ HasAttachments: Boolean;
533+ HTMLCollection: IHTMLElementCollection;
534+ HTMLAnchorElement: IHTMLAnchorElement;
535+ HTMLElement: IHTMLElement;
536+ HTMLDOMNode: IHTMLDOMNode;
537+ AttachmentId: Cardinal;
538+begin
539+ // Primeiro verifica se existem os elementos que indicam a presença de anexos.
540+ // Anexos que tem previsualizaçao, como imagens pequenas, são formados com
541+ // <div>, que é um elemento proibido pelo mantis. Logo, sua simples presença
542+ // indica que há anexos
543+ HTMLCollection := AUnparsedComment.Children as IHTMLElementCollection;
544+ HTMLCollection := HTMLCollection.Tags('div') as IHTMLElementCollection;
545+ HasAttachments := HTMLCollection.length > 0;
546+
547+ // A segunda verificação, caso não haja <div>, verifica a presença de <a> com
548+ // href="file_download.php?file_id=xxxx&type=bug". Tais anexos são aqueles que
549+ // não tem previsualização ou que são imagens muito grandes para serem
550+ // exibidas no mantis de forma inline
551+ if not HasAttachments then
552+ begin
553+ HTMLCollection := AUnparsedComment.Children as IHTMLElementCollection;
554+ HTMLCollection := HTMLCollection.Tags('a') as IHTMLElementCollection;
555+
556+ if HTMLCollection.length > 0 then
557+ for i := 0 to Pred(HTMLCollection.length) do
558+ begin
559+ HTMLAnchorElement := (HTMLCollection.Item(i,i) as IHTMLAnchorElement);
560+ // Por um motivo que desconheço, href retorna "about:" antes do href
561+ // propriamente dito. Isso não quebra a condição abaixo, mas é estranho
562+ // e precisa ser considerado no caso de querer usar o href para algo
563+ if Pos(WideString('file_download.php?file_id='),HTMLAnchorElement.href) > 0 then
564+ begin
565+ HasAttachments := True;
566+ Break;
567+ end;
568+ end;
569+ end;
570+
571+ AComment.Text := AUnparsedComment.innerHTML;
572+
573+ if HasAttachments then
574+ begin
575+ // Extrai as informações de anexos contidos em todos os <div> existentes em
576+ // AUnparsedComment e exclui todos os <div>
577+ HTMLCollection := AUnparsedComment.Children as IHTMLElementCollection;
578+ HTMLCollection := HTMLCollection.Tags('div') as IHTMLElementCollection;
579+ // Como o Mantis não permite <div> todos os que existirem dizem respeito a
580+ // anexos, logo, todos são processados. O loop ocorre de trás pra frente ou
581+ // do último <div> para o primeiro, pois eles serão excluídos
582+ if HTMLCollection.Length > 0 then
583+ for i := Pred(HTMLCollection.length) downto 0 do
584+ begin
585+ // <div> da vez
586+ HTMLElement := (HTMLCollection.Item(i,i) as IHTMLElement);
587+ // <a>, dentro do <div> da vez, que contém o nome do anexo
588+ HTMLAnchorElement := ((HTMLElement.Children as IHTMLElementCollection).item(1,1) as IHTMLAnchorElement);
589+ // Apenas os <div> com id = open devem ser processados, mas todos os
590+ // <div> devem ser excluídos, por isso a exclusão está fora da condição
591+ // abaixo
592+ if Pos(WideString('open'),HTMLElement.id) > 0 then
593+ begin
594+ SetLength(AComment.Attachments,Length(AComment.Attachments) + 1);
595+
596+ AComment.Attachments[High(AComment.Attachments)].Id := ParseAttachmentIdFromDivId(HTMLElement.id);
597+ AComment.Attachments[High(AComment.Attachments)].FileName := FixEncoding((HTMLAnchorElement as IHTMLElement).InnerText);
598+ end;
599+
600+ (AUnparsedComment as IHTMLDOMNode).removeChild(HTMLCollection.Item(i,i) as IHTMLDOMNode);
601+ end;
602+ // Extrai as informações de anexos contidos em todos os <a> que são links
603+ // para anexos
604+ HTMLCollection := AUnparsedComment.Children as IHTMLElementCollection;
605+ HTMLCollection := HTMLCollection.Tags('a') as IHTMLElementCollection;
606+
607+ if HTMLCollection.length > 0 then
608+ begin
609+ i := Pred(HTMLCollection.length);
610+ // Os elementos são varridos de baixo pra cima, logo, primeiro vem o link
611+ // de exclusão, depois o texto, depois o link de download com o nome do
612+ // arquivo e por fim o link de download inicial que contém apens um ícone.
613+ // Links de anexos vem em 3 com um nó de texto entre eles (que também
614+ // precisa ser removido). O loop varre todos os <a> do comentário, mas
615+ // como pode haver algum <a> que não seja de um anexo, a variável i
616+ // precisa ser decrementada ao menos uma vez a cada loop, porém é
617+ // decrementada 3x quando o link achado é o link de exclusão de um anexo.
618+ while i > -1 do
619+ begin
620+ HTMLAnchorElement := (HTMLCollection.Item(i,i) as IHTMLAnchorElement);
621+ // A condição abaixo retorna true se estivermos no último <a> que faz
622+ // referência a um anexo. Neste caso a função também retorna o Id do
623+ // anexo
624+ if ParseAttachmentIdFromAHref(HTMLAnchorElement.href,AttachmentId) then
625+ begin
626+ SetLength(AComment.Attachments,Length(AComment.Attachments) + 1);
627+
628+ AComment.Attachments[High(AComment.Attachments)].Id := AttachmentId;
629+ // Exclui o nó de texto existente antes do <a> de exclusão
630+ (AUnparsedComment as IHTMLDOMNode).removeChild((HTMLAnchorElement as IHTMLDOMNode).PreviousSibling);
631+ // PreviousSibling é agora o <a> que contém o nome do arquivo porque
632+ // na linha anterior o PreviousSibling original foi excluído
633+ AComment.Attachments[High(AComment.Attachments)].FileName := FixEncoding(((HTMLAnchorElement as IHTMLDOMNode).PreviousSibling as IHTMLElement).InnerText);
634+ // Exclui o <a> que contém o nome do arquivo. Ao excluir o <a>,
635+ // precisamo decrementar o contador, pois o loop está circulando os
636+ // elementos <a>
637+ (AUnparsedComment as IHTMLDOMNode).removeChild((HTMLAnchorElement as IHTMLDOMNode).PreviousSibling);
638+ Dec(i);
639+ // PreviousSibling é agora o <a> que contém o primeiro link do
640+ // conjunto de 3 links relativos ao anexos. Como já pegamos todas as
641+ // informações necessárias, apenas excluímos este <a> e decrementamos
642+ // o contador
643+ (AUnparsedComment as IHTMLDOMNode).removeChild((HTMLAnchorElement as IHTMLDOMNode).PreviousSibling);
644+ Dec(i);
645+ // Finalmente exclui o <a> de exclusão, o qual foi usado até agora
646+ // para se achar os previoussiblings. O decremento referente a este
647+ // <a> é feito fora do bloco if atual apenas para economizar linhas de
648+ // código, já que a cada loop é necessário que haja ao menos um
649+ // decremento do iterador
650+ (AUnparsedComment as IHTMLDOMNode).removeChild(HTMLAnchorElement as IHTMLDOMNode);
651+ end;
652+
653+ Dec(i);
654+ end;
655+ end;
656+ end;
657+ // A exclusão de blocos de anexos dentro dos comentários pode deixar <br> no
658+ // fim do texto. Tais elementos no fim do comentário (se houver um) são
659+ // indesejáveis. Mesmo que o usuário os tenha colocado propositalmente, eles
660+ // não são necessários, pois não fazem sentido. O bloco de instruções abaixo
661+ // exclui de baixo pra cima, cada <br> encontrado até que não haja mais
662+ // qualquer <br> no fim do texto. Eu chamo isso de "Trim de <br>", porque é
663+ // exatamente com ele se parece! A execução deste bloco de código pode fazer
664+ // com que o comentário final seja vazio, no caso de ser um comentário que só
665+ // tem anexos
666+ HTMLCollection := AUnparsedComment.Children as IHTMLElementCollection;
667+
668+ if HTMLCollection.length > 0 then
669+ for i := Pred(HTMLCollection.length) downto 0 do
670+ begin
671+ HTMLDOMNode := (HTMLCollection.Item(i,i) as IHTMLDOMNode);
672+
673+ if LowerCase(HTMLDOMNode.nodeName) = 'br' then
674+ (AUnparsedComment as IHTMLDOMNode).removeChild(HTMLDOMNode)
675+ else
676+ Break;
677+ end;
678+
679+ // Salva em Text apenas o comentário!
680+ AComment.Text := AUnparsedComment.innerHTML;
681+end;
682+
478683 {$WARN SYMBOL_PLATFORM OFF}
479-function ParseAssignedTasks(ADocument: String; AStatusColors: TStatusColors; out ATasks: TTasks; AFullInfo: Boolean = False): Boolean;
684+function ParseTaskDetails(ADocument: String; AStatusColors: TStatusColors; out ATask: TTask): Boolean;
480685 var
481686 HTMLDocument: IHTMLDocument;
482687 HTMLElementCollection: IHTMLElementCollection;
483688 begin
484689 Result := False;
690+
485691 HTMLDocument := coHTMLDocument.Create as IHTMLDocument;
486692
487693 // Habilita o modo de design, o qual desabilita scripts e permite a
@@ -492,6 +698,288 @@
492698 (HTMLDocument as IHTMLDocument2Disp).Write(ADocument);
493699 (HTMLDocument as IHTMLDocument2).Close;
494700
701+ HTMLElementCollection := (HTMLDocument as IHTMLDocument3).getElementsByTagName('h4');
702+
703+ if (HTMLElementCollection.length > 0) and (FixEncoding((HTMLElementCollection.Item(0,0) as IHTMLElement).InnerText) = 'Ver Detalhes da Tarefa') then
704+ begin
705+ // Obtendo todos os <td> contidos no primeiro <table> e varrendo-os em busca
706+ // daqueles que tem classes específicas
707+ var HTMLTable: IHTMLTable := (HTMLDocument as IHTMLDocument3).GetElementsByTagName('table').item(0,0) as IHTMLTable;
708+ HTMLElementCollection := (HTMLTable as IHTMLElement2).GetElementsByTagName('td');
709+
710+ if HTMLElementCollection.Length > 0 then
711+ begin
712+ var HTMLElement: IHTMLElement;
713+
714+ for var i: Word := 0 to Pred(HTMLElementCollection.Length) do
715+ begin
716+ HTMLElement := HTMLElementCollection.item(i,i) as IHTMLElement;
717+
718+ if HTMLElement._ClassName = 'bug-status' then
719+ begin
720+ ATask.Status := FixEncoding(HTMLElement.InnerText);
721+ ExtractStatusColorAndCode(AStatusColors, ((HTMLElement as IHTMLDOMNode).FirstChild as IHTMLElement)._ClassName,ATask.StatusCode,ATask.StatusColor);
722+ end
723+ else if HTMLElement._ClassName = 'bug-priority' then
724+ ATask.Priority := FixEncoding(HTMLElement.InnerText)
725+ else if HTMLElement._ClassName = 'bug-project' then
726+ ATask.Project := FixEncoding(HTMLElement.InnerText)
727+ else if HTMLElement._ClassName = 'bug-category' then
728+ ATask.Category := FixEncoding(HTMLElement.InnerText)
729+ else if HTMLElement._ClassName = 'bug-last-modified' then
730+ ATask.LastUpdate := ExtractDateTime(HTMLElement.InnerText)
731+ else if HTMLElement._ClassName = 'bug-summary' then
732+ ATask.Summary := ExtractSummary(HTMLElement.InnerText)
733+ else if HTMLElement._ClassName = 'bug-description' then
734+ ATask.Description := FixEncoding(HTMLElement.InnerHtml)
735+ else if HTMLElement._ClassName = 'bug-target-version' then
736+ ATask.TargetVersion := FixEncoding(HTMLElement.innerText)
737+ else if HTMLElement._ClassName = 'bug-reporter' then
738+ ATask.Reporter := FixEncoding(HTMLElement.innerText)
739+ else if HTMLElement._ClassName = 'bug-steps-to-reproduce' then
740+ ATask.StepsToReproduce := FixEncoding(HTMLElement.InnerHtml)
741+ else if HTMLElement._ClassName = 'bug-additional-information' then
742+ ATask.AdditionalInformation := FixEncoding(HTMLElement.InnerHtml);
743+ end;
744+ end;
745+ // Obtendo todos os <input> contidos no <form> com id "bugnoteadd" e
746+ // varrendo-os em busca daqueles que tem nomes específicos a fim de obter
747+ // seus valores
748+ HTMLElementCollection := ((HTMLDocument as IHTMLDocument3).GetElementById('bugnoteadd') as IHTMLElement2).GetElementsByTagName('input');
749+
750+ if HTMLElementCollection.Length > 0 then
751+ begin
752+ var HTMLInputElement: IHTMLInputElement;
753+
754+ for var i: Word := 0 to Pred(HTMLElementCollection.Length) do
755+ begin
756+ HTMLInputElement := HTMLElementCollection.item(i,i) as IHTMLInputElement;
757+
758+ if HTMLInputElement.Name = 'bugnote_add_token' then
759+ ATask.AddCommentToken := HTMLInputElement.Value
760+ else if HTMLInputElement.Name = 'max_file_size' then
761+ ATask.MaxFileSize := StrToInt(HTMLInputElement.Value);
762+ end;
763+ end;
764+
765+ // Retorna todos os <select> e varre-os buscando aquele que contém a lista
766+ // suspensa de possíveis mudanças de status
767+ HTMLElementCollection := (HTMLDocument as IHTMLDocument3).GetElementsByTagName('select');
768+
769+ if HTMLElementCollection.Length > 0 then
770+ begin
771+ var HTMLSelectElement: IHTMLSelectElement;
772+
773+ for var i: Word := 0 to Pred(HTMLElementCollection.Length) do
774+ begin
775+ HTMLSelectElement := HTMLElementCollection.Item(i,i) as IHTMLSelectElement;
776+
777+ // IHTMLSelectElement.options não contém a coleção de opções. Ao invés
778+ // disso ele parece ter uma referência ao <select>. A coleção de opções
779+ // do <select> está em item, o que faz IHTMLSelectElement se assemelhar
780+ // a um IHTMLElementCollection
781+
782+ if HTMLSelectElement.Name = 'new_status' then
783+ begin
784+ // Configura o array de possíveis mudanças de status com a
785+ // quantidade correta
786+ SetLength(ATask.PossibleStatusChanges,HTMLSelectElement.length);
787+
788+ var HTMLOptionElement: IHTMLOptionElement;
789+
790+ // Varre todos os <option>, adicionando suas informações no array
791+ for var j: Byte := 0 to Pred(HTMLSelectElement.Length) do
792+ begin
793+ HTMLOptionElement := HTMLSelectElement.item(j,j) as IHTMLOptionElement;
794+
795+ ATask.PossibleStatusChanges[j].Id := StrToInt(HTMLOptionElement.Value);
796+ ATask.PossibleStatusChanges[j].Description := FixEncoding(HTMLOptionElement.Text);
797+ end;
798+
799+ Break;
800+ end;
801+ end;
802+ end;
803+ // Retorna todos os <tr> contidos no <div> com id "relationships" o qual
804+ // contém as tarefas relacionadas, varrendo-os e extraindo de seus <td> as
805+ // informações necessárias ao mesmo tempo em que incrementa o array de
806+ // tarefas relacionadas apenas para <tr> que representam linhas que contém
807+ // tarefas relacionadas
808+ HTMLElementCollection := ((HTMLDocument as IHTMLDocument3).GetElementById('relationships') as IHTMLElement2).GetElementsByTagName('tr');
809+
810+ if HTMLElementCollection.Length > 0 then
811+ begin
812+ var HTMLTableRow: IHTMLTableRow;
813+
814+ for var i: Word := 0 to Pred(HTMLElementCollection.Length) do
815+ begin
816+ HTMLTableRow := HTMLElementCollection.item(i,i) as IHTMLTableRow;
817+
818+ // É considerada uma linha que contém uma tarefa relacionada, apenas
819+ // as linhas que tem mais de um <td>. Uma linha que possui apenas um
820+ // <td> é uma linha de cabeçalho ou rodapé nesta lista, por exemplo, o
821+ // rodapé que fala que "nem todas as tarefas filhas foram resolvidas
822+ // ou fechadas" não é uma linha válida e não deve ser processada
823+ if HTMLTableRow.Cells.Length > 1 then
824+ begin
825+ SetLength(ATask.RelatedTasks,Length(ATask.RelatedTasks) + 1);
826+
827+ ATask.RelatedTasks[High(ATask.RelatedTasks)].Relationship := FixEncoding((HTMLTableRow.Cells.Item(0,0) as IHTMLElement).InnerText);
828+ ATask.RelatedTasks[High(ATask.RelatedTasks)].Id := StrToInt((HTMLTableRow.Cells.Item(1,1) as IHTMLElement).InnerText);
829+ ATask.RelatedTasks[High(ATask.RelatedTasks)].Status := FixEncoding((HTMLTableRow.Cells.Item(2,2) as IHTMLElement).InnerText);
830+ ExtractStatusColorAndCode(AStatusColors,((HTMLTableRow.Cells.Item(2,2) as IHTMLDOMNode).FirstChild as IHTMLElement)._ClassName,ATask.RelatedTasks[High(ATask.RelatedTasks)].StatusCode,ATask.RelatedTasks[High(ATask.RelatedTasks)].StatusColor);
831+ ATask.RelatedTasks[High(ATask.RelatedTasks)].Handler := FixEncoding((HTMLTableRow.Cells.Item(3,3) as IHTMLElement).InnerText);
832+ ATask.RelatedTasks[High(ATask.RelatedTasks)].Project := FixEncoding((HTMLTableRow.Cells.Item(4,4) as IHTMLElement).InnerText);
833+ ATask.RelatedTasks[High(ATask.RelatedTasks)].Summary := FixEncoding((HTMLTableRow.Cells.Item(5,5) as IHTMLElement).InnerText);
834+ end;
835+ end;
836+ end;
837+
838+ // Precisamos acessar o <div> que tem id "bugnotes", porém infelizmente o
839+ // mantis possui um <a> com o mesmo id, um erro, sendo assim não é possível
840+ // acessar esse div diretamente. O procedimento a seguir retorna todos os
841+ // <tr> contidos no elemento <div> com id "bugnotes" que contém os
842+ // comentários desta tarefa
843+ HTMLElementCollection := (HTMLDocument as IHTMLDocument3).GetElementsByTagName('div');
844+
845+ if HTMLElementCollection.Length > 0 then
846+ begin
847+ var HTMLElement: IHTMLElement;
848+ var HEC: IHTMLElementCollection;
849+
850+ for var i: Word := 0 to Pred(HTMLElementCollection.Length) do
851+ begin
852+ HTMLElement := HTMLElementCollection.item(i,i) as IHTMLElement;
853+
854+ if HTMLElement.id = 'bugnotes' then
855+ begin
856+ HEC := (HTMLElement as IHTMLElement2).GetElementsByTagName('tr');
857+ Break;
858+ end;
859+ end;
860+
861+ HTMLElementCollection := HEC; // será que isso funciona???
862+ end;
863+ // Neste ponto, HTMLElementCollection contém todos os <tr> contidos no
864+ // <div> que tem id = bugnotes. Agora é necessário varrer todos eles
865+ // extraindo de seus <td> as informações necessárias ao mesmo tempo em que
866+ // incrementa o array de comentários apenas para <tr> que representam
867+ // linhas que contém comentários
868+ if HTMLElementCollection.Length > 0 then
869+ begin
870+ var HTMLElement: IHTMLElement;
871+ var HTMLTableCell: IHTMLTableCell;
872+ var CommentDetails: IHTMLDivElement;
873+ var AuxString: String;
874+
875+ for var i: Word := 0 to Pred(HTMLElementCollection.length) do
876+ begin
877+ HTMLElement := HTMLElementCollection.item(i,i) as IHTMLElement;
878+
879+ // É considerada uma linha que contém um comentário, apenas as
880+ // linhas que tem a classe bugnote
881+ if HTMLElement._className = 'bugnote' then
882+ begin
883+ SetLength(ATask.Comments,Length(ATask.Comments) + 1);
884+ // Obtendo a referência a primeira célula da linha, a qual contém os
885+ // detalhesdo comentário
886+ HTMLTableCell := (HTMLElement as IHTMLTableRow).cells.item(0,0) as IHTMLTableCell;
887+ // HTMLTableCell.ChildNodes[1] é o <div> que contém todos os outros
888+ // elementos que contém os detalhes do comentário. Abaixo está a
889+ // forma de acessar este <div> usando lastChild. Para acessar como uma
890+ // coleção use a interface IHTMLDOMChildrenCollection
891+ CommentDetails := (HTMLTableCell as IHTMLDOMNode).lastChild as IHTMLDivElement;
892+
893+ ATask.Comments[High(ATask.Comments)].Id := ParseCommentId(HTMLElement.Id);
894+ ATask.Comments[High(ATask.Comments)].Creator := FixEncoding((((((CommentDetails as IHTMLDOMNode).childNodes as IHTMLDOMChildrenCollection).item(0) as IHTMLDOMNode).childNodes as IHTMLDOMChildrenCollection).item(1) as IHTMLElement).InnerText);
895+ ATask.Comments[High(ATask.Comments)].CreatorId := ExtractUserId((((((CommentDetails as IHTMLDOMNode).childNodes as IHTMLDOMChildrenCollection).item(0) as IHTMLDOMNode).childNodes as IHTMLDOMChildrenCollection).item(1) as IHTMLAnchorElement).href);
896+
897+ AuxString := (((CommentDetails as IHTMLDOMNode).childNodes as IHTMLDOMChildrenCollection).item(1) as IHTMLElement).InnerText;
898+
899+ ATask.Comments[High(ATask.Comments)].IsPrivate := Pos('privado',AuxString) > 0;
900+
901+ if ATask.Comments[High(ATask.Comments)].IsPrivate then
902+ AuxString := StringReplace(AuxString,'privado','',[rfIgnoreCase]);
903+
904+ ATask.Comments[High(ATask.Comments)].Created := ExtractDateTime(AuxString);
905+
906+ ATask.Comments[High(ATask.Comments)].LastUpdate := ExtractDateTime((((CommentDetails as IHTMLDOMNode).childNodes as IHTMLDOMChildrenCollection).item(3) as IHTMLElement).InnerText);
907+
908+ // Obtendo a referência a segunda célula da linha, a qual contém o
909+ // comentário propriamente dito e também referências arquivos
910+ // anexados juntamente com o comentário, por isso é necessário
911+ // verificar primeiramente se há arquivos anexados ao comentário
912+ // para, só assim, extrair o texto puro do mesmo
913+ HTMLElement := (HTMLElement as IHTMLTableRow).cells.item(1,1) as IHTMLElement;
914+
915+ // Um comentário tem anexos quando há em seu texto <div> ou <a> com
916+ // href apontando para "file_download.php?file_id=xxxx&type=bug". A
917+ // função ParseCommentsAndAttachments faz isso
918+ ParseCommentsAndAttachments(HTMLElement,ATask.Comments[High(ATask.Comments)]);
919+ end;
920+ end;
921+ end;
922+ // Se chegar aqui, de fato nada de errado aconteceu e podemos retornar
923+ // verdadeiro
924+ Result := True;
925+ end;
926+end;
927+{$WARN SYMBOL_PLATFORM ON}
928+
929+function TaskDetails(AHandle: Cardinal; AStatusColors: TStatusColors; var ATask: TTask): Boolean;
930+var
931+ Req: TRequestOptions;
932+ Res: TResponse;
933+begin
934+ ZeroMemory(@Req,SizeOf(Req));
935+ ZeroMemory(@Res,SizeOf(Res));
936+
937+ Req.AutoClearSSLState := True;
938+
939+ Req.InternetOpenParams.Agent := 'MantisBT Monitor';
940+ Req.InternetOpenParams.AccessType := INTERNET_OPEN_TYPE_PRECONFIG;
941+ Req.InternetConnectParams.ServerName := PChar(Configurations.BaseUrl + '/mantis/view.php?id=' + IntToStr(ATask.Id));
942+ Req.InternetConnectParams.Service := INTERNET_SERVICE_HTTP;
943+ Req.InternetConnectParams.Context := AHandle;
944+
945+ Req.HttpOpenRequestParams.Verb := 'GET';
946+ Req.HttpOpenRequestParams.AcceptTypes := TStringList.Create;
947+ try
948+ Req.HttpOpenRequestParams.AcceptTypes.Add('*/*');
949+ Req.HttpOpenRequestParams.Context := AHandle;
950+ Req.HttpOpenRequestParams.AutoDetectHTTPS := True;
951+ Req.HttpOpenRequestParams.IgnoreInvalidCertificates := True;
952+ Req.HttpSendRequestParams.IgnoreInvalidCertificateCA := True;
953+
954+ Res.Content := TStringStream.Create('');
955+ try
956+ Request(Req,Res);
957+ Result := ParseTaskDetails(TStringStream(Res.Content).DataString,AStatusColors,ATask);
958+ finally
959+ Res.Content.Free;
960+ end;
961+ finally
962+ Req.HttpOpenRequestParams.AcceptTypes.Free;
963+ end;
964+end;
965+
966+{$WARN SYMBOL_PLATFORM OFF}
967+function ParseAssignedTasks(AHandle: Cardinal; ADocument: String; AStatusColors: TStatusColors; out ATasks: TTasks; AFullInfo: Boolean = False): Boolean;
968+var
969+ HTMLDocument: IHTMLDocument;
970+ HTMLElementCollection: IHTMLElementCollection;
971+begin
972+ Result := False;
973+ HTMLDocument := coHTMLDocument.Create as IHTMLDocument;
974+
975+ // Habilita o modo de design, o qual desabilita scripts e permite a
976+ // leitura do código da página exatamente como ele é. Scripts podem
977+ // modificar o DOM, logo, ao usar esta propriedade o texto parseado pode
978+ // ser diferente daquilo que ele seria ao não usar esta propriedade
979+ (HTMLDocument as IHTMLDocument2).DesignMode := 'On';
980+ (HTMLDocument as IHTMLDocument2Disp).Write(ADocument);
981+ (HTMLDocument as IHTMLDocument2).Close;
982+
495983 HTMLElementCollection := (HTMLDocument as IHTMLDocument7).getElementsByClassName('table table-bordered table-condensed table-striped table-hover');
496984
497985 // Lembre-se que getElementsByTagName pega todos os elementos dentro do
@@ -519,7 +1007,7 @@
5191007 ATasks[i].Id := StrToInt(Trim(HTMLElement.InnerText));
5201008
5211009 if AFullInfo then
522-// TaskDetails(0,AStatusColors,ATasks[i])
1010+ TaskDetails(AHandle,AStatusColors,ATasks[i])
5231011 else
5241012 begin
5251013 // Primeiro <i> dentro da primeira coluna
@@ -589,7 +1077,7 @@
5891077 try
5901078 Request(Req,Res);
5911079
592- Result := ParseAssignedTasks(TStringStream(Res.Content).DataString,AStatusColors,ATasks,AFullInfo);
1080+ Result := ParseAssignedTasks(AHandle,TStringStream(Res.Content).DataString,AStatusColors,ATasks,AFullInfo);
5931081 finally
5941082 Res.Content.Free;
5951083 end;
--- trunk/client/src/UDamoPrincipal.pas (revision 53)
+++ trunk/client/src/UDamoPrincipal.pas (revision 54)
@@ -63,7 +63,7 @@
6363 MNUIClose: TMenuItem;
6464 MNUILegenda: TMenuItem;
6565 procedure ACTNRefreshExecute(Sender: TObject);
66- procedure DoOpenIssue(Sender: TObject);
66+ procedure DoOpenTask(Sender: TObject);
6767 procedure ACTNGeneralConfigsExecute(Sender: TObject);
6868 procedure ACTNCloseExecute(Sender: TObject);
6969 procedure ACTNAboutExecute(Sender: TObject);
@@ -245,7 +245,7 @@
245245 MenuItem.Tag := ATaskId;
246246 MenuItem.Action := TAction.Create(Self);
247247 MenuItem.Action.Tag := ATaskId;
248- MenuItem.Action.OnExecute := DoOpenIssue;
248+ MenuItem.Action.OnExecute := DoOpenTask;
249249 TAction(MenuItem.Action).Category := MY_TASKS;
250250 TAction(MenuItem.Action).ActionList := AMAN;
251251 TAction(MenuItem.Action).Caption := 'Detalhes...';
@@ -339,7 +339,7 @@
339339 if Length(NewTasks) > 1 then
340340 ShowInfoBalloon(Length(NewTasks).ToString + ' novas tarefas foram atribuídas a você! Acesse o menu do ' + Application.Title + ' na área de notificação para vê-las','Novas tarefas foram atribuídas!',10)
341341 else if Length(NewTasks) = 1 then
342- ShowInfoBalloon(NewTasks[0].Id.ToString + ' - ' + NewTasks[0].Summary,'Uma nova tarefa foi atribuída a você!',10,DoOpenIssue,NewTasks[0].Id);
342+ ShowInfoBalloon(NewTasks[0].Id.ToString + ' - ' + NewTasks[0].Summary,'Uma nova tarefa foi atribuída a você!',10,DoOpenTask,NewTasks[0].Id);
343343
344344 FMyTasks := ATasks;
345345
@@ -376,7 +376,7 @@
376376 end;
377377 end;
378378
379-procedure TDamoPrincipal.DoOpenIssue(Sender: TObject);
379+procedure TDamoPrincipal.DoOpenTask(Sender: TObject);
380380 begin
381381 TFormIssue.ShowMe(TComponent(Sender).Tag);
382382 end;
@@ -383,7 +383,7 @@
383383
384384 procedure TDamoPrincipal.DoOpenWithMantis(Sender: TObject);
385385 begin
386- OpenIssueWithMantis(TComponent(Sender).Tag);
386+ OpenTaskWithMantis(TComponent(Sender).Tag);
387387 end;
388388
389389 { TIssueChecking }
--- trunk/client/src/UFormIssue.pas (revision 53)
+++ trunk/client/src/UFormIssue.pas (revision 54)
@@ -6,9 +6,9 @@
66 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
77 Dialogs, UFormBasicDialog, StdCtrls, Buttons, UPngBitBtn, ExtCtrls, PngImage,
88 ToolWin, ActnMan, ActnCtrls, XPStyleActnCtrls, ActnList, ImgList,
9- UPngImageList, UMNWSWrapperFunctions, OleCtrls, SHDocVw, ComCtrls, DB,
10- DBClient, Grids, DBGrids, KRK.Vcl.DBGrids, DBCtrls, UFunctions,
11- UPngSpeedButton, Menus, ActnPopup, DBCGrids, ImageList, Actions;
9+ UPngImageList, OleCtrls, SHDocVw, ComCtrls, DB, DBClient, Grids, DBGrids,
10+ KRK.Vcl.DBGrids, DBCtrls, UPngSpeedButton, Menus, ActnPopup, DBCGrids,
11+ ImageList, Actions, UFunctions, UScrapFunctions;
1212
1313 type
1414 TFormIssue = class(TFormBasicDialog)
@@ -18,7 +18,7 @@
1818 ACTNReject: TAction;
1919 ACTNImpeachment: TAction;
2020 PIML32: TPngImageList;
21- LAEDProjectName: TLabeledEdit;
21+ LAEDProject: TLabeledEdit;
2222 LAEDSummary: TLabeledEdit;
2323 LAEDTargetVersion: TLabeledEdit;
2424 LAEDStatus: TLabeledEdit;
@@ -50,7 +50,6 @@
5050 DBTXProject: TDBText;
5151 LABE1: TLabel;
5252 LABE2: TLabel;
53- IMAGOpenWithMantis: TImage;
5453 CLDSAttachments: TClientDataSet;
5554 DASOAttachments: TDataSource;
5655 KRDGAttachments: TKRKDBGrid;
@@ -101,6 +100,7 @@
101100 PNBBNoteEdit: TPngBitBtn;
102101 LABE3: TLabel;
103102 PANE2: TPanel;
103+ ACTNOpenWithMantis: TAction;
104104 procedure ACTNNoteEditExecute(Sender: TObject);
105105 procedure ACTNNoteNewExecute(Sender: TObject);
106106 procedure PNSBNoteFirstClick(Sender: TObject);
@@ -119,19 +119,21 @@
119119 procedure MNUIDownloadAttachmentClick(Sender: TObject);
120120 procedure KRDGRelatedIssuesDrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
121121 procedure PNBBCloseClick(Sender: TObject);
122- procedure IMAGOpenWithMantisClick(Sender: TObject);
123122 procedure KRDGRelatedIssuesDblClick(Sender: TObject);
124123 procedure CLDSRelatedIssuesStatusGetText(Sender: TField; var Text: string; DisplayText: Boolean);
124+ procedure ACTNOpenWithMantisExecute(Sender: TObject);
125+ procedure WEBRDescriptionBeforeNavigate2(ASender: TObject;
126+ const pDisp: IDispatch; const URL, Flags, TargetFrameName, PostData,
127+ Headers: OleVariant; var Cancel: WordBool);
125128 private
126129 { Private declarations }
127- FIssueInfo: TIssueInfo;
128- FAttachmentsLoaded: Boolean;
129- FRelationshipsLoaded: Boolean;
130- FCommentsLoaded: Boolean;
131- FAdditionalInfoLoaded: Boolean;
132- FStepsToReproduceLoaded: Boolean;
130+ FTask: TTask;
131+// FAttachmentsLoaded: Boolean;
132+// FRelationshipsLoaded: Boolean;
133+// FCommentsLoaded: Boolean;
134+// FAdditionalInfoLoaded: Boolean;
135+// FStepsToReproduceLoaded: Boolean;
133136
134- procedure LoadIssueHeader;
135137 procedure LoadAdditionalInfo;
136138 procedure LoadStepsToReproduce;
137139
@@ -142,7 +144,8 @@
142144 procedure CreateParams(var Params: TCreateParams); override;
143145 public
144146 { Public declarations }
145- class function ShowMe(AIssueNumber: Cardinal; AModal: Boolean = False): TModalResult;
147+ class function ShowMe(var ATask: TTask; AModal: Boolean = False): TModalResult; overload;
148+ class function ShowMe(ATaskId: Cardinal; AModal: Boolean = False): TModalResult; overload;
146149 end;
147150
148151 implementation
@@ -164,19 +167,19 @@
164167 procedure TFormIssue.ACTNNoteEditExecute(Sender: TObject);
165168 begin
166169 inherited;
167- TFormManageNote.ShowMeModal(Self,FIssueInfo.Id,TAction(Sender).ActionComponent.Tag);
170+ TFormManageNote.ShowMeModal(Self,FTask.Id,TAction(Sender).ActionComponent.Tag);
168171 end;
169172
170173 procedure TFormIssue.ACTNNoteFullScreenExecute(Sender: TObject);
171174 begin
172175 inherited;
173- TFormViewNote.ShowMeModal(Self,FIssueInfo.Id,TAction(Sender).ActionComponent.Tag);
176+ TFormViewNote.ShowMeModal(Self,FTask.Id,TAction(Sender).ActionComponent.Tag);
174177 end;
175178
176179 procedure TFormIssue.ACTNNoteNewExecute(Sender: TObject);
177180 begin
178181 inherited;
179- TFormManageNote.ShowMeModal(Self,FIssueInfo.Id,0);
182+ TFormManageNote.ShowMeModal(Self,FTask.Id,0);
180183 end;
181184
182185 procedure TFormIssue.ACTNNoteReplyExecute(Sender: TObject);
@@ -185,6 +188,12 @@
185188 ShowMessage('Tela para responder a anotação ' + IntToStr(TAction(Sender).ActionComponent.Tag));
186189 end;
187190
191+procedure TFormIssue.ACTNOpenWithMantisExecute(Sender: TObject);
192+begin
193+ inherited;
194+ OpenTaskWithMantis(FTask.Id);
195+end;
196+
188197 procedure TFormIssue.CLDSCommentsAfterScroll(DataSet: TDataSet);
189198 begin
190199 inherited;
@@ -218,12 +227,6 @@
218227 Text := '@' + Sender.AsString + '@';
219228 end;
220229
221-procedure TFormIssue.IMAGOpenWithMantisClick(Sender: TObject);
222-begin
223- inherited;
224- OpenIssueWithMantis(FIssueInfo.Id);
225-end;
226-
227230 procedure TFormIssue.KRDGRelatedIssuesDblClick(Sender: TObject);
228231 begin
229232 TFormIssue.ShowMe(CLDSRelatedIssuesNumber.AsInteger);
@@ -248,58 +251,48 @@
248251
249252 procedure TFormIssue.LoadAdditionalInfo;
250253 begin
251- if not FAdditionalInfoLoaded then
252- begin
253- LoadWebBrowserHTML(WEBRAdditionalInformation,FIssueInfo.AdditionalInformation);
254- FAdditionalInfoLoaded := True;
255- end;
254+// Isso ja vem junto com os detalhes da tarefa
255+// if not FAdditionalInfoLoaded then
256+// begin
257+ LoadWebBrowserHTML(WEBRAdditionalInformation,FTask.AdditionalInformation);
258+// FAdditionalInfoLoaded := True;
259+// end;
256260 end;
257261
258262 procedure TFormIssue.LoadAttachments;
259263 begin
260- if not FAttachmentsLoaded then
261- FAttachmentsLoaded := GetAttachments(FIssueInfo.Id,CLDSAttachments);
264+// isso ja vem junto com os detalhes da tarefa
265+// if not FAttachmentsLoaded then
266+// FAttachmentsLoaded := GetAttachments(FTask.Id,CLDSAttachments);
262267 end;
263268
264269 procedure TFormIssue.LoadComments;
265270 begin
266- if not FCommentsLoaded then
267- begin
271+// if not FCommentsLoaded then
272+// begin
273+// acho que isso carregava os comentários no CLDS, mas nao sei se vou usar isso ainda
268274 // FCommentsLoaded := GetComments(FIssueInfo.Id,Configurations.UserId,CLDSComments);
269275 // mude para scrap
270- if FCommentsLoaded then
276+// if FCommentsLoaded then
277+// acho que isso gera os comentários numa unica pagina html
271278 GenerateSimpleComments(WEBRNotesSimple,CLDSComments);
272- end;
279+// end;
273280 end;
274281
275-procedure TFormIssue.LoadIssueHeader;
276-begin
277- Caption := 'Caso Mantis #' + IntToStr(FIssueInfo.Id);
278- LAEDProjectName.Text := FIssueInfo.ProjectName;
279- LAEDSummary.Text := FIssueInfo.Summary;
280- LAEDTargetVersion.Text := FIssueInfo.TargetVersion;
281-
282- // Isso não é mais necessário porque esta informação vem junto com o resto dos detalhes da tarefa
283-// LAEDStatus.Text := Configurations.GetStatusName(FIssueInfo.Status);
284- LAEDLastUpdate.Text := FormatDateTime('dd/mm/yyyy "às" hh:nn:ss',FIssueInfo.LastUpdated);
285- LAEDReporter.Text := FIssueInfo.Reporter;
286-
287- LoadWebBrowserHTML(WEBRDescription,FIssueInfo.Description);
288-end;
289-
290282 procedure TFormIssue.LoadRelationships;
291283 begin
292- if not FRelationshipsLoaded then
293- FRelationshipsLoaded := GetRelatedIssues(FIssueInfo.Id,CLDSRelatedIssues);
284+// Isso ja vem junto com os detalhes da tarefa
285+// if not FRelationshipsLoaded then
286+// FRelationshipsLoaded := GetRelatedIssues(FTask.Id,CLDSRelatedIssues);
294287 end;
295288
296289 procedure TFormIssue.LoadStepsToReproduce;
297290 begin
298- if not FStepsToReproduceLoaded then
299- begin
300- LoadWebBrowserHTML(WEBRStepsToReproduce,FIssueInfo.StepsToReproduce);
301- FStepsToReproduceLoaded := True;
302- end;
291+// if not FStepsToReproduceLoaded then
292+// begin
293+ LoadWebBrowserHTML(WEBRStepsToReproduce,FTask.StepsToReproduce);
294+// FStepsToReproduceLoaded := True;
295+// end;
303296 end;
304297
305298 procedure TFormIssue.MNUIAttachNewClick(Sender: TObject);
@@ -307,9 +300,10 @@
307300 inherited;
308301 if OPDIAttachment.Execute then
309302 begin
310- UploadAttachment(FIssueInfo.Id,OPDIAttachment.FileName);
311- FAttachmentsLoaded := False;
312- LoadAttachments;
303+// O anexo é anexado de outra forma agora, junto com o comentário
304+// UploadAttachment(FTask.Id,OPDIAttachment.FileName);
305+// FAttachmentsLoaded := False;
306+// LoadAttachments;
313307 end;
314308 end;
315309
@@ -320,8 +314,9 @@
320314 SADIAttachment.FileName := CLDSAttachmentsFilename.AsString;
321315 SADIAttachment.Filter := 'Arquivos ' + SADIAttachment.DefaultExt + '|*.' + SADIAttachment.DefaultExt + '|Todos os arquivos|*.*';
322316
323- if SADIAttachment.Execute then
324- DownloadAttachment(CLDSAttachmentsId.AsInteger,SADIAttachment.FileName);
317+//Não sei como isso será feito ainda
318+// if SADIAttachment.Execute then
319+// DownloadAttachment(CLDSAttachmentsId.AsInteger,SADIAttachment.FileName);
325320 end;
326321
327322 procedure TFormIssue.MNUIDownloadAttachmentClick(Sender: TObject);
@@ -331,8 +326,9 @@
331326 SADIAttachment.FileName := CLDSAttachmentsFilename.AsString;
332327 SADIAttachment.Filter := 'Arquivos ' + SADIAttachment.DefaultExt + '|*' + SADIAttachment.DefaultExt + '|Todos os arquivos|*.*';
333328
334- if SADIAttachment.Execute then
335- DownloadAttachment(CLDSAttachmentsId.AsInteger,SADIAttachment.FileName,False);
329+//Não sei como isso será feito ainda
330+// if SADIAttachment.Execute then
331+// DownloadAttachment(CLDSAttachmentsId.AsInteger,SADIAttachment.FileName,False);
336332 end;
337333
338334 procedure TFormIssue.PNBBCloseClick(Sender: TObject);
@@ -366,23 +362,37 @@
366362 CLDSComments.Prior;
367363 end;
368364
369-class function TFormIssue.ShowMe(AIssueNumber: Cardinal; AModal: Boolean = False): TModalResult;
365+class function TFormIssue.ShowMe(ATaskId: Cardinal; AModal: Boolean): TModalResult;
366+var
367+ Task: TTask;
370368 begin
369+ Task.Id := ATaskId;
370+
371+ Result := ShowMe(Task,AModal);
372+end;
373+
374+class function TFormIssue.ShowMe(var ATask: TTask; AModal: Boolean = False): TModalResult;
375+begin
371376 Result := mrNone;
372377
373378 with Self.Create(Application) do
374379 begin
375380 AutoCaption := True;
376- FIssueInfo := DecodeIssueInfo(GetIssueInfo(AIssueNumber));
377381
378- LoadIssueHeader;
382+ TaskDetails(Application.Handle,Configurations.StatusColors,ATask);
379383
380- FAttachmentsLoaded := False;
381- FRelationshipsLoaded := False;
382- FCommentsLoaded := False;
383- FAdditionalInfoLoaded := False;
384- FStepsToReproduceLoaded := False;
384+ FTask := ATask;
385385
386+ Caption := 'Tarefa Mantis #' + IntToStr(FTask.Id);
387+ LAEDProject.Text := FTask.Project;
388+ LAEDSummary.Text := FTask.Summary;
389+ LAEDTargetVersion.Text := FTask.TargetVersion;
390+ LAEDStatus.Text := FTask.Status;
391+ LAEDLastUpdate.Text := FormatDateTime('dd/mm/yyyy "às" hh:nn:ss',FTask.LastUpdate);
392+ LAEDReporter.Text := FTask.Reporter;
393+
394+ LoadWebBrowserHTML(WEBRDescription,FTask.Description);
395+
386396 if AModal then
387397 Result := ShowModal
388398 else
@@ -415,4 +425,16 @@
415425 LoadStepsToReproduce;
416426 end;
417427
428+procedure TFormIssue.WEBRDescriptionBeforeNavigate2(ASender: TObject;
429+ const pDisp: IDispatch; const URL, Flags, TargetFrameName, PostData,
430+ Headers: OleVariant; var Cancel: WordBool);
431+begin
432+ inherited;
433+ if URL <> 'about:blank' then
434+ Cancel := True;
435+
436+ // se começa com about: é um caminho relativo
437+ // do contrário é absoluto
438+end;
439+
418440 end.
--- trunk/client/src/UFormViewNote.pas (revision 53)
+++ trunk/client/src/UFormViewNote.pas (revision 54)
@@ -36,7 +36,7 @@
3636 private
3737 { Private declarations }
3838 FCaptionTemplate: String;
39- FIssueNumber: Cardinal;
39+ FTaskNumber: Cardinal;
4040 FNoteId: Cardinal;
4141 public
4242 { Public declarations }
@@ -83,7 +83,7 @@
8383 procedure TFormViewNote.IMAGOpenWithMantisClick(Sender: TObject);
8484 begin
8585 inherited;
86- OpenIssueWithMantis(FIssueNumber);
86+ OpenTaskWithMantis(FTaskNumber);
8787 end;
8888
8989 procedure TFormViewNote.PNBBOKClick(Sender: TObject);
@@ -121,7 +121,7 @@
121121 with Self.Create(AOwner) do
122122 begin
123123 FCaptionTemplate := 'Visualizando anotação #%s (%u/%u)';
124- FIssueNumber := AIssueNumber;
124+ FTaskNumber := AIssueNumber;
125125 FNoteId := ANoteId;
126126 Result := ShowModal;
127127 end;
Show on old repository browser