@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.)
hq.recaptime.dev/wiki/Phorge
phorge
phabricator
1<?php
2
3final class DifferentialRequiredSignaturesField
4 extends DifferentialCoreCustomField {
5
6 public function getFieldKey() {
7 return 'differential:required-signatures';
8 }
9
10 public function getFieldName() {
11 return pht('Required Signatures');
12 }
13
14 public function getFieldDescription() {
15 return pht('Display required legal agreements.');
16 }
17
18 public function shouldAppearInPropertyView() {
19 return true;
20 }
21
22 protected function readValueFromRevision(DifferentialRevision $revision) {
23 return self::loadForRevision($revision);
24 }
25
26 public static function loadForRevision($revision) {
27 $legalpad_class = PhabricatorLegalpadApplication::class;
28 if (!PhabricatorApplication::isClassInstalled($legalpad_class)) {
29 return array();
30 }
31
32 if (!$revision->getPHID()) {
33 return array();
34 }
35
36 $phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
37 $revision->getPHID(),
38 LegalpadObjectNeedsSignatureEdgeType::EDGECONST);
39
40 if ($phids) {
41
42 // NOTE: We're bypassing permissions to pull these. We have to expose
43 // some information about signature status in order to implement this
44 // field meaningfully (otherwise, we could not tell reviewers that they
45 // can't accept the revision yet), but that's OK because the only way to
46 // require signatures is with a "Global" Herald rule, which requires a
47 // high level of access.
48
49 $signatures = id(new LegalpadDocumentSignatureQuery())
50 ->setViewer(PhabricatorUser::getOmnipotentUser())
51 ->withDocumentPHIDs($phids)
52 ->withSignerPHIDs(array($revision->getAuthorPHID()))
53 ->execute();
54 $signatures = mpull($signatures, null, 'getDocumentPHID');
55
56 $phids = array_fuse($phids);
57 foreach ($phids as $phid) {
58 $phids[$phid] = isset($signatures[$phid]);
59 }
60 }
61
62 return $phids;
63 }
64
65 public function getRequiredHandlePHIDsForPropertyView() {
66 return array_keys($this->getValue());
67 }
68
69 public function renderPropertyViewValue(array $handles) {
70 if (!$handles) {
71 return null;
72 }
73
74 $author_phid = $this->getObject()->getAuthorPHID();
75 $viewer_phid = $this->getViewer()->getPHID();
76
77 $viewer_is_author = ($author_phid == $viewer_phid);
78
79 $view = new PHUIStatusListView();
80 foreach ($handles as $handle) {
81 $item = id(new PHUIStatusItemView())
82 ->setTarget($handle->renderLink());
83
84 // NOTE: If the viewer isn't the author, we just show generic document
85 // icons, because the granular information isn't very useful and there
86 // is no need to disclose it.
87
88 // If the viewer is the author, we show exactly what they need to sign.
89
90 if (!$viewer_is_author) {
91 $item->setIcon('fa-file-text-o bluegrey');
92 } else {
93 if (idx($this->getValue(), $handle->getPHID())) {
94 $item->setIcon('fa-check-square-o green');
95 } else {
96 $item->setIcon('fa-times red');
97 }
98 }
99
100 $view->addItem($item);
101 }
102
103 return $view;
104 }
105
106 public function getWarningsForDetailView() {
107 if (!$this->haveAnyUnsignedDocuments()) {
108 return array();
109 }
110
111 return array(
112 pht(
113 'The author of this revision has not signed all the required '.
114 'legal documents. The revision can not be accepted until the '.
115 'documents are signed.'),
116 );
117 }
118
119 private function haveAnyUnsignedDocuments() {
120 foreach ($this->getValue() as $phid => $signed) {
121 if (!$signed) {
122 return true;
123 }
124 }
125
126 return false;
127 }
128
129 public function getWarningsForRevisionHeader(array $handles) {
130 if (!$this->haveAnyUnsignedDocuments()) {
131 return array();
132 }
133
134 return array(
135 pht(
136 'This revision can not be accepted until the required legal '.
137 'agreements have been signed.'),
138 );
139 }
140
141}