Revision | 54 (tree) |
---|---|
Time | 2021-10-05 07:22:53 |
Author | derekwildstar |
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
@@ -114,9 +114,9 @@ | ||
114 | 114 | <AppDPIAwarenessMode>PerMonitor</AppDPIAwarenessMode> |
115 | 115 | <VerInfo_MinorVer>0</VerInfo_MinorVer> |
116 | 116 | <VerInfo_Release>0</VerInfo_Release> |
117 | - <VerInfo_Build>296</VerInfo_Build> | |
117 | + <VerInfo_Build>349</VerInfo_Build> | |
118 | 118 | <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> | |
120 | 120 | <Debugger_RunParams>/desenvolvimento</Debugger_RunParams> |
121 | 121 | <VerInfo_AutoGenVersion>false</VerInfo_AutoGenVersion> |
122 | 122 | <VerInfo_AutoIncVersion>true</VerInfo_AutoIncVersion> |
@@ -16,23 +16,23 @@ | ||
16 | 16 | |
17 | 17 | TSanitizeFlags = set of TSanitizeFlag; |
18 | 18 | |
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; | |
31 | 31 | |
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; | |
36 | 36 | procedure LoadWebBrowserHTML(AWebBrowser: TWebBrowser; AText: String; ASanitizeFlags: TSanitizeFlags = [sfDeactivateLinks,sfDeactivateOtherTags,sfActivateAllowedTags,sfMakeSpacesAndTabsPreserved,sfMakeReturnsPreserved,sfActivateLinks]; ADecode: Boolean = False; ABackgroundColor: String = '#FFFFFF'); |
37 | 37 | procedure GenerateSimpleComments(AWebBrowser: TWebBrowser; AClientDataSet: TClientDataSet); |
38 | 38 | procedure RenderProjectNameAsMenuHeader(ATexto: String; ACanvas: TCanvas; ARect: TRect; AStatusColor: TColor); |
@@ -226,7 +226,7 @@ | ||
226 | 226 | // } |
227 | 227 | // |
228 | 228 | function SanitizeString(AString: String; AFlags: Word): String; |
229 | -//////////////////////////////////////////////////////////////////////////////// | |
229 | +//-///////////////////////////////////////////////////////////////////////////// | |
230 | 230 | procedure RemoveLinks(var S: String); |
231 | 231 | var |
232 | 232 | Subject: String; |
@@ -454,35 +454,35 @@ | ||
454 | 454 | // end; |
455 | 455 | //end; |
456 | 456 | |
457 | -procedure OpenIssueWithMantis(AIssueNumber: Cardinal); | |
457 | +procedure OpenTaskWithMantis(ATaskNumber: Cardinal); | |
458 | 458 | 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); | |
460 | 460 | end; |
461 | 461 | |
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; | |
470 | 485 | |
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 | - | |
486 | 486 | procedure DownloadAttachment(AAttachmentId: Cardinal; AFileName: String; AOpen: Boolean = True); |
487 | 487 | var |
488 | 488 | SS: TStringStream; |
@@ -565,21 +565,21 @@ | ||
565 | 565 | SF: Word; |
566 | 566 | SanitizeFlag: TSanitizeFlag; |
567 | 567 | 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]); | |
572 | 572 | |
573 | 573 | // Ao depurar o loop abaixo, notei que ele circula por todos os valores |
574 | 574 | // possíveis de TSanitizeFlag, de zero a 32, mas só de fato entra no loop para |
575 | 575 | // 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 | |
577 | 577 | // 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)); | |
581 | 581 | |
582 | - ABody := SanitizeString(ABody,SF); | |
582 | +// ABody := SanitizeString(ABody,SF); | |
583 | 583 | Result := Trim(Format(HTML,[ABackgroundColor,ABody])); |
584 | 584 | end; |
585 | 585 | //////////////////////////////////////////////////////////////////////////////// |
@@ -24,7 +24,6 @@ | ||
24 | 24 | AssignedProjects: TAssignedProjects; |
25 | 25 | |
26 | 26 | function FirstName: String; |
27 | - | |
28 | 27 | end; |
29 | 28 | |
30 | 29 | TStatusColor = record |
@@ -105,6 +104,7 @@ | ||
105 | 104 | procedure GetStatusColors(AHandle: Cardinal; out AStatusColors: TStatusColors); |
106 | 105 | function GetUserInfo(AHandle: Cardinal; out AUserInfo: TUserInfo): Boolean; |
107 | 106 | function AssignedTasks(AHandle: Cardinal; AStatusColors: TStatusColors; out ATasks: TTasks; AFullInfo: Boolean = False): Boolean; |
107 | +function TaskDetails(AHandle: Cardinal; AStatusColors: TStatusColors; var ATask: TTask): Boolean; | |
108 | 108 | |
109 | 109 | implementation |
110 | 110 |
@@ -111,7 +111,7 @@ | ||
111 | 111 | uses |
112 | 112 | WinApi.Windows, WinApi.WinInet, System.Classes, System.SysUtils, MSHTML, |
113 | 113 | KRK.Rtl.Win.WinInet.Utilities, KRK.RegExp.Utils, UConfigurations, |
114 | - Vcl.Graphics; | |
114 | + Vcl.Graphics, System.Variants; | |
115 | 115 | |
116 | 116 | function FixEncoding(AText: OleVariant): String; |
117 | 117 | var |
@@ -475,13 +475,219 @@ | ||
475 | 475 | Result := StrToDateTime(Trim(DateTime)); |
476 | 476 | end; |
477 | 477 | |
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 | + | |
478 | 683 | {$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; | |
480 | 685 | var |
481 | 686 | HTMLDocument: IHTMLDocument; |
482 | 687 | HTMLElementCollection: IHTMLElementCollection; |
483 | 688 | begin |
484 | 689 | Result := False; |
690 | + | |
485 | 691 | HTMLDocument := coHTMLDocument.Create as IHTMLDocument; |
486 | 692 | |
487 | 693 | // Habilita o modo de design, o qual desabilita scripts e permite a |
@@ -492,6 +698,288 @@ | ||
492 | 698 | (HTMLDocument as IHTMLDocument2Disp).Write(ADocument); |
493 | 699 | (HTMLDocument as IHTMLDocument2).Close; |
494 | 700 | |
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 | + | |
495 | 983 | HTMLElementCollection := (HTMLDocument as IHTMLDocument7).getElementsByClassName('table table-bordered table-condensed table-striped table-hover'); |
496 | 984 | |
497 | 985 | // Lembre-se que getElementsByTagName pega todos os elementos dentro do |
@@ -519,7 +1007,7 @@ | ||
519 | 1007 | ATasks[i].Id := StrToInt(Trim(HTMLElement.InnerText)); |
520 | 1008 | |
521 | 1009 | if AFullInfo then |
522 | -// TaskDetails(0,AStatusColors,ATasks[i]) | |
1010 | + TaskDetails(AHandle,AStatusColors,ATasks[i]) | |
523 | 1011 | else |
524 | 1012 | begin |
525 | 1013 | // Primeiro <i> dentro da primeira coluna |
@@ -589,7 +1077,7 @@ | ||
589 | 1077 | try |
590 | 1078 | Request(Req,Res); |
591 | 1079 | |
592 | - Result := ParseAssignedTasks(TStringStream(Res.Content).DataString,AStatusColors,ATasks,AFullInfo); | |
1080 | + Result := ParseAssignedTasks(AHandle,TStringStream(Res.Content).DataString,AStatusColors,ATasks,AFullInfo); | |
593 | 1081 | finally |
594 | 1082 | Res.Content.Free; |
595 | 1083 | end; |
@@ -63,7 +63,7 @@ | ||
63 | 63 | MNUIClose: TMenuItem; |
64 | 64 | MNUILegenda: TMenuItem; |
65 | 65 | procedure ACTNRefreshExecute(Sender: TObject); |
66 | - procedure DoOpenIssue(Sender: TObject); | |
66 | + procedure DoOpenTask(Sender: TObject); | |
67 | 67 | procedure ACTNGeneralConfigsExecute(Sender: TObject); |
68 | 68 | procedure ACTNCloseExecute(Sender: TObject); |
69 | 69 | procedure ACTNAboutExecute(Sender: TObject); |
@@ -245,7 +245,7 @@ | ||
245 | 245 | MenuItem.Tag := ATaskId; |
246 | 246 | MenuItem.Action := TAction.Create(Self); |
247 | 247 | MenuItem.Action.Tag := ATaskId; |
248 | - MenuItem.Action.OnExecute := DoOpenIssue; | |
248 | + MenuItem.Action.OnExecute := DoOpenTask; | |
249 | 249 | TAction(MenuItem.Action).Category := MY_TASKS; |
250 | 250 | TAction(MenuItem.Action).ActionList := AMAN; |
251 | 251 | TAction(MenuItem.Action).Caption := 'Detalhes...'; |
@@ -339,7 +339,7 @@ | ||
339 | 339 | if Length(NewTasks) > 1 then |
340 | 340 | 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) |
341 | 341 | 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); | |
343 | 343 | |
344 | 344 | FMyTasks := ATasks; |
345 | 345 |
@@ -376,7 +376,7 @@ | ||
376 | 376 | end; |
377 | 377 | end; |
378 | 378 | |
379 | -procedure TDamoPrincipal.DoOpenIssue(Sender: TObject); | |
379 | +procedure TDamoPrincipal.DoOpenTask(Sender: TObject); | |
380 | 380 | begin |
381 | 381 | TFormIssue.ShowMe(TComponent(Sender).Tag); |
382 | 382 | end; |
@@ -383,7 +383,7 @@ | ||
383 | 383 | |
384 | 384 | procedure TDamoPrincipal.DoOpenWithMantis(Sender: TObject); |
385 | 385 | begin |
386 | - OpenIssueWithMantis(TComponent(Sender).Tag); | |
386 | + OpenTaskWithMantis(TComponent(Sender).Tag); | |
387 | 387 | end; |
388 | 388 | |
389 | 389 | { TIssueChecking } |
@@ -6,9 +6,9 @@ | ||
6 | 6 | Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, |
7 | 7 | Dialogs, UFormBasicDialog, StdCtrls, Buttons, UPngBitBtn, ExtCtrls, PngImage, |
8 | 8 | 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; | |
12 | 12 | |
13 | 13 | type |
14 | 14 | TFormIssue = class(TFormBasicDialog) |
@@ -18,7 +18,7 @@ | ||
18 | 18 | ACTNReject: TAction; |
19 | 19 | ACTNImpeachment: TAction; |
20 | 20 | PIML32: TPngImageList; |
21 | - LAEDProjectName: TLabeledEdit; | |
21 | + LAEDProject: TLabeledEdit; | |
22 | 22 | LAEDSummary: TLabeledEdit; |
23 | 23 | LAEDTargetVersion: TLabeledEdit; |
24 | 24 | LAEDStatus: TLabeledEdit; |
@@ -50,7 +50,6 @@ | ||
50 | 50 | DBTXProject: TDBText; |
51 | 51 | LABE1: TLabel; |
52 | 52 | LABE2: TLabel; |
53 | - IMAGOpenWithMantis: TImage; | |
54 | 53 | CLDSAttachments: TClientDataSet; |
55 | 54 | DASOAttachments: TDataSource; |
56 | 55 | KRDGAttachments: TKRKDBGrid; |
@@ -101,6 +100,7 @@ | ||
101 | 100 | PNBBNoteEdit: TPngBitBtn; |
102 | 101 | LABE3: TLabel; |
103 | 102 | PANE2: TPanel; |
103 | + ACTNOpenWithMantis: TAction; | |
104 | 104 | procedure ACTNNoteEditExecute(Sender: TObject); |
105 | 105 | procedure ACTNNoteNewExecute(Sender: TObject); |
106 | 106 | procedure PNSBNoteFirstClick(Sender: TObject); |
@@ -119,19 +119,21 @@ | ||
119 | 119 | procedure MNUIDownloadAttachmentClick(Sender: TObject); |
120 | 120 | procedure KRDGRelatedIssuesDrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState); |
121 | 121 | procedure PNBBCloseClick(Sender: TObject); |
122 | - procedure IMAGOpenWithMantisClick(Sender: TObject); | |
123 | 122 | procedure KRDGRelatedIssuesDblClick(Sender: TObject); |
124 | 123 | 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); | |
125 | 128 | private |
126 | 129 | { 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; | |
133 | 136 | |
134 | - procedure LoadIssueHeader; | |
135 | 137 | procedure LoadAdditionalInfo; |
136 | 138 | procedure LoadStepsToReproduce; |
137 | 139 |
@@ -142,7 +144,8 @@ | ||
142 | 144 | procedure CreateParams(var Params: TCreateParams); override; |
143 | 145 | public |
144 | 146 | { 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; | |
146 | 149 | end; |
147 | 150 | |
148 | 151 | implementation |
@@ -164,19 +167,19 @@ | ||
164 | 167 | procedure TFormIssue.ACTNNoteEditExecute(Sender: TObject); |
165 | 168 | begin |
166 | 169 | inherited; |
167 | - TFormManageNote.ShowMeModal(Self,FIssueInfo.Id,TAction(Sender).ActionComponent.Tag); | |
170 | + TFormManageNote.ShowMeModal(Self,FTask.Id,TAction(Sender).ActionComponent.Tag); | |
168 | 171 | end; |
169 | 172 | |
170 | 173 | procedure TFormIssue.ACTNNoteFullScreenExecute(Sender: TObject); |
171 | 174 | begin |
172 | 175 | inherited; |
173 | - TFormViewNote.ShowMeModal(Self,FIssueInfo.Id,TAction(Sender).ActionComponent.Tag); | |
176 | + TFormViewNote.ShowMeModal(Self,FTask.Id,TAction(Sender).ActionComponent.Tag); | |
174 | 177 | end; |
175 | 178 | |
176 | 179 | procedure TFormIssue.ACTNNoteNewExecute(Sender: TObject); |
177 | 180 | begin |
178 | 181 | inherited; |
179 | - TFormManageNote.ShowMeModal(Self,FIssueInfo.Id,0); | |
182 | + TFormManageNote.ShowMeModal(Self,FTask.Id,0); | |
180 | 183 | end; |
181 | 184 | |
182 | 185 | procedure TFormIssue.ACTNNoteReplyExecute(Sender: TObject); |
@@ -185,6 +188,12 @@ | ||
185 | 188 | ShowMessage('Tela para responder a anotação ' + IntToStr(TAction(Sender).ActionComponent.Tag)); |
186 | 189 | end; |
187 | 190 | |
191 | +procedure TFormIssue.ACTNOpenWithMantisExecute(Sender: TObject); | |
192 | +begin | |
193 | + inherited; | |
194 | + OpenTaskWithMantis(FTask.Id); | |
195 | +end; | |
196 | + | |
188 | 197 | procedure TFormIssue.CLDSCommentsAfterScroll(DataSet: TDataSet); |
189 | 198 | begin |
190 | 199 | inherited; |
@@ -218,12 +227,6 @@ | ||
218 | 227 | Text := '@' + Sender.AsString + '@'; |
219 | 228 | end; |
220 | 229 | |
221 | -procedure TFormIssue.IMAGOpenWithMantisClick(Sender: TObject); | |
222 | -begin | |
223 | - inherited; | |
224 | - OpenIssueWithMantis(FIssueInfo.Id); | |
225 | -end; | |
226 | - | |
227 | 230 | procedure TFormIssue.KRDGRelatedIssuesDblClick(Sender: TObject); |
228 | 231 | begin |
229 | 232 | TFormIssue.ShowMe(CLDSRelatedIssuesNumber.AsInteger); |
@@ -248,58 +251,48 @@ | ||
248 | 251 | |
249 | 252 | procedure TFormIssue.LoadAdditionalInfo; |
250 | 253 | 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; | |
256 | 260 | end; |
257 | 261 | |
258 | 262 | procedure TFormIssue.LoadAttachments; |
259 | 263 | 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); | |
262 | 267 | end; |
263 | 268 | |
264 | 269 | procedure TFormIssue.LoadComments; |
265 | 270 | 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 | |
268 | 274 | // FCommentsLoaded := GetComments(FIssueInfo.Id,Configurations.UserId,CLDSComments); |
269 | 275 | // mude para scrap |
270 | - if FCommentsLoaded then | |
276 | +// if FCommentsLoaded then | |
277 | +// acho que isso gera os comentários numa unica pagina html | |
271 | 278 | GenerateSimpleComments(WEBRNotesSimple,CLDSComments); |
272 | - end; | |
279 | +// end; | |
273 | 280 | end; |
274 | 281 | |
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 | - | |
290 | 282 | procedure TFormIssue.LoadRelationships; |
291 | 283 | 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); | |
294 | 287 | end; |
295 | 288 | |
296 | 289 | procedure TFormIssue.LoadStepsToReproduce; |
297 | 290 | 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; | |
303 | 296 | end; |
304 | 297 | |
305 | 298 | procedure TFormIssue.MNUIAttachNewClick(Sender: TObject); |
@@ -307,9 +300,10 @@ | ||
307 | 300 | inherited; |
308 | 301 | if OPDIAttachment.Execute then |
309 | 302 | 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; | |
313 | 307 | end; |
314 | 308 | end; |
315 | 309 |
@@ -320,8 +314,9 @@ | ||
320 | 314 | SADIAttachment.FileName := CLDSAttachmentsFilename.AsString; |
321 | 315 | SADIAttachment.Filter := 'Arquivos ' + SADIAttachment.DefaultExt + '|*.' + SADIAttachment.DefaultExt + '|Todos os arquivos|*.*'; |
322 | 316 | |
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); | |
325 | 320 | end; |
326 | 321 | |
327 | 322 | procedure TFormIssue.MNUIDownloadAttachmentClick(Sender: TObject); |
@@ -331,8 +326,9 @@ | ||
331 | 326 | SADIAttachment.FileName := CLDSAttachmentsFilename.AsString; |
332 | 327 | SADIAttachment.Filter := 'Arquivos ' + SADIAttachment.DefaultExt + '|*' + SADIAttachment.DefaultExt + '|Todos os arquivos|*.*'; |
333 | 328 | |
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); | |
336 | 332 | end; |
337 | 333 | |
338 | 334 | procedure TFormIssue.PNBBCloseClick(Sender: TObject); |
@@ -366,23 +362,37 @@ | ||
366 | 362 | CLDSComments.Prior; |
367 | 363 | end; |
368 | 364 | |
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; | |
370 | 368 | 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 | |
371 | 376 | Result := mrNone; |
372 | 377 | |
373 | 378 | with Self.Create(Application) do |
374 | 379 | begin |
375 | 380 | AutoCaption := True; |
376 | - FIssueInfo := DecodeIssueInfo(GetIssueInfo(AIssueNumber)); | |
377 | 381 | |
378 | - LoadIssueHeader; | |
382 | + TaskDetails(Application.Handle,Configurations.StatusColors,ATask); | |
379 | 383 | |
380 | - FAttachmentsLoaded := False; | |
381 | - FRelationshipsLoaded := False; | |
382 | - FCommentsLoaded := False; | |
383 | - FAdditionalInfoLoaded := False; | |
384 | - FStepsToReproduceLoaded := False; | |
384 | + FTask := ATask; | |
385 | 385 | |
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 | + | |
386 | 396 | if AModal then |
387 | 397 | Result := ShowModal |
388 | 398 | else |
@@ -415,4 +425,16 @@ | ||
415 | 425 | LoadStepsToReproduce; |
416 | 426 | end; |
417 | 427 | |
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 | + | |
418 | 440 | end. |
@@ -36,7 +36,7 @@ | ||
36 | 36 | private |
37 | 37 | { Private declarations } |
38 | 38 | FCaptionTemplate: String; |
39 | - FIssueNumber: Cardinal; | |
39 | + FTaskNumber: Cardinal; | |
40 | 40 | FNoteId: Cardinal; |
41 | 41 | public |
42 | 42 | { Public declarations } |
@@ -83,7 +83,7 @@ | ||
83 | 83 | procedure TFormViewNote.IMAGOpenWithMantisClick(Sender: TObject); |
84 | 84 | begin |
85 | 85 | inherited; |
86 | - OpenIssueWithMantis(FIssueNumber); | |
86 | + OpenTaskWithMantis(FTaskNumber); | |
87 | 87 | end; |
88 | 88 | |
89 | 89 | procedure TFormViewNote.PNBBOKClick(Sender: TObject); |
@@ -121,7 +121,7 @@ | ||
121 | 121 | with Self.Create(AOwner) do |
122 | 122 | begin |
123 | 123 | FCaptionTemplate := 'Visualizando anotação #%s (%u/%u)'; |
124 | - FIssueNumber := AIssueNumber; | |
124 | + FTaskNumber := AIssueNumber; | |
125 | 125 | FNoteId := ANoteId; |
126 | 126 | Result := ShowModal; |
127 | 127 | end; |