@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

Modularize the Legalpad "Require Signature" Herald Action

Ref T8726. Modularizes "Require Signatures" for Legalpad.

+211 -51
+6
resources/sql/autopatches/20150730.herald.6.sql
··· 1 + UPDATE {$NAMESPACE}_herald.herald_action a 2 + JOIN {$NAMESPACE}_herald.herald_rule r 3 + ON a.ruleID = r.id 4 + SET a.action = 'legalpad.require' 5 + WHERE r.ruleType != 'personal' 6 + AND a.action = 'signature';
+2
src/__phutil_library_map__.php
··· 1124 1124 'LegalpadMailReceiver' => 'applications/legalpad/mail/LegalpadMailReceiver.php', 1125 1125 'LegalpadObjectNeedsSignatureEdgeType' => 'applications/legalpad/edge/LegalpadObjectNeedsSignatureEdgeType.php', 1126 1126 'LegalpadReplyHandler' => 'applications/legalpad/mail/LegalpadReplyHandler.php', 1127 + 'LegalpadRequireSignatureHeraldAction' => 'applications/legalpad/herald/LegalpadRequireSignatureHeraldAction.php', 1127 1128 'LegalpadSchemaSpec' => 'applications/legalpad/storage/LegalpadSchemaSpec.php', 1128 1129 'LegalpadSignatureNeededByObjectEdgeType' => 'applications/legalpad/edge/LegalpadSignatureNeededByObjectEdgeType.php', 1129 1130 'LegalpadTransaction' => 'applications/legalpad/storage/LegalpadTransaction.php', ··· 4852 4853 'LegalpadMailReceiver' => 'PhabricatorObjectMailReceiver', 4853 4854 'LegalpadObjectNeedsSignatureEdgeType' => 'PhabricatorEdgeType', 4854 4855 'LegalpadReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler', 4856 + 'LegalpadRequireSignatureHeraldAction' => 'HeraldAction', 4855 4857 'LegalpadSchemaSpec' => 'PhabricatorConfigSchemaSpec', 4856 4858 'LegalpadSignatureNeededByObjectEdgeType' => 'PhabricatorEdgeType', 4857 4859 'LegalpadTransaction' => 'PhabricatorApplicationTransaction',
+1 -31
src/applications/differential/editor/DifferentialTransactionEditor.php
··· 1596 1596 HeraldAdapter $adapter, 1597 1597 HeraldTranscript $transcript) { 1598 1598 1599 - $xactions = array(); 1600 - 1601 - // Require legalpad document signatures. 1602 - $legal_phids = $adapter->getRequiredSignatureDocumentPHIDs(); 1603 - if ($legal_phids) { 1604 - // We only require signatures of documents which have not already 1605 - // been signed. In general, this reduces the amount of churn that 1606 - // signature rules cause. 1607 - 1608 - $signatures = id(new LegalpadDocumentSignatureQuery()) 1609 - ->setViewer(PhabricatorUser::getOmnipotentUser()) 1610 - ->withDocumentPHIDs($legal_phids) 1611 - ->withSignerPHIDs(array($object->getAuthorPHID())) 1612 - ->execute(); 1613 - $signed_phids = mpull($signatures, 'getDocumentPHID'); 1614 - $legal_phids = array_diff($legal_phids, $signed_phids); 1615 - 1616 - // If we still have something to trigger, add the edges. 1617 - if ($legal_phids) { 1618 - $edge_legal = LegalpadObjectNeedsSignatureEdgeType::EDGECONST; 1619 - $xactions[] = id(new DifferentialTransaction()) 1620 - ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) 1621 - ->setMetadataValue('edge:type', $edge_legal) 1622 - ->setNewValue( 1623 - array( 1624 - '+' => array_fuse($legal_phids), 1625 - )); 1626 - } 1627 - } 1628 - 1629 1599 // Apply build plans. 1630 1600 HarbormasterBuildable::applyBuildPlans( 1631 1601 $adapter->getDiff()->getPHID(), 1632 1602 $adapter->getPHID(), 1633 1603 $adapter->getBuildPlans()); 1634 1604 1635 - return $xactions; 1605 + return array(); 1636 1606 } 1637 1607 1638 1608 /**
-15
src/applications/differential/herald/HeraldDifferentialRevisionAdapter.php
··· 6 6 protected $revision; 7 7 8 8 protected $buildPlans = array(); 9 - protected $requiredSignatureDocumentPHIDs = array(); 10 9 11 10 protected $affectedPackages; 12 11 protected $changesets; ··· 80 79 return $object; 81 80 } 82 81 83 - public function getRequiredSignatureDocumentPHIDs() { 84 - return $this->requiredSignatureDocumentPHIDs; 85 - } 86 - 87 82 public function getBuildPlans() { 88 83 return $this->buildPlans; 89 84 } ··· 141 136 return array_merge( 142 137 array( 143 138 self::ACTION_APPLY_BUILD_PLANS, 144 - self::ACTION_REQUIRE_SIGNATURE, 145 139 ), 146 140 parent::getActions($rule_type)); 147 141 case HeraldRuleTypeConfig::RULE_TYPE_PERSONAL: ··· 167 161 $effect, 168 162 true, 169 163 pht('Applied build plans.')); 170 - break; 171 - case self::ACTION_REQUIRE_SIGNATURE: 172 - foreach ($effect->getTarget() as $phid) { 173 - $this->requiredSignatureDocumentPHIDs[] = $phid; 174 - } 175 - $result[] = new HeraldApplyTranscript( 176 - $effect, 177 - true, 178 - pht('Required signatures.')); 179 164 break; 180 165 default: 181 166 $result[] = $this->applyStandardEffect($effect);
-5
src/applications/herald/adapter/HeraldAdapter.php
··· 29 29 const ACTION_AUDIT = 'audit'; 30 30 const ACTION_APPLY_BUILD_PLANS = 'applybuildplans'; 31 31 const ACTION_BLOCK = 'block'; 32 - const ACTION_REQUIRE_SIGNATURE = 'signature'; 33 32 34 33 private $contentSource; 35 34 private $isNewObject; ··· 716 715 $standard = array( 717 716 self::ACTION_AUDIT => pht('Trigger an Audit by'), 718 717 self::ACTION_APPLY_BUILD_PLANS => pht('Run build plans'), 719 - self::ACTION_REQUIRE_SIGNATURE => pht('Require legal signatures'), 720 718 self::ACTION_BLOCK => pht('Block change with message'), 721 719 ); 722 720 break; ··· 805 803 case self::ACTION_APPLY_BUILD_PLANS: 806 804 return $this->buildTokenizerFieldValue( 807 805 new HarbormasterBuildPlanDatasource()); 808 - case self::ACTION_REQUIRE_SIGNATURE: 809 - return $this->buildTokenizerFieldValue( 810 - new LegalpadDocumentDatasource()); 811 806 case self::ACTION_BLOCK: 812 807 return new HeraldTextFieldValue(); 813 808 }
+197
src/applications/legalpad/herald/LegalpadRequireSignatureHeraldAction.php
··· 1 + <?php 2 + 3 + final class LegalpadRequireSignatureHeraldAction 4 + extends HeraldAction { 5 + 6 + const DO_NO_TARGETS = 'do.no-targets'; 7 + const DO_ALREADY_REQUIRED = 'do.already-required'; 8 + const DO_INVALID = 'do.invalid'; 9 + const DO_SIGNED = 'do.signed'; 10 + const DO_REQUIRED = 'do.required'; 11 + 12 + const ACTIONCONST = 'legalpad.require'; 13 + 14 + public function getActionGroupKey() { 15 + return HeraldSupportActionGroup::ACTIONGROUPKEY; 16 + } 17 + 18 + public function supportsObject($object) { 19 + // TODO: This could probably be more general. Note that we call 20 + // getAuthorPHID() on the object explicitly below, and this also needs to 21 + // be generalized. 22 + return ($object instanceof DifferentialRevision); 23 + } 24 + 25 + protected function applyRequire(array $phids) { 26 + $adapter = $this->getAdapter(); 27 + $edgetype_legal = LegalpadObjectNeedsSignatureEdgeType::EDGECONST; 28 + 29 + $phids = array_fuse($phids); 30 + 31 + if (!$phids) { 32 + $this->logEffect(self::DO_NO_TARGETS); 33 + return; 34 + } 35 + 36 + $current = $adapter->loadEdgePHIDs($edgetype_legal); 37 + 38 + $already = array(); 39 + foreach ($phids as $phid) { 40 + if (isset($current[$phid])) { 41 + $already[] = $phid; 42 + unset($phids[$phid]); 43 + } 44 + } 45 + 46 + if ($already) { 47 + $this->logEffect(self::DO_ALREADY_REQUIRED, $phids); 48 + } 49 + 50 + if (!$phids) { 51 + return; 52 + } 53 + 54 + $documents = id(new LegalpadDocumentQuery()) 55 + ->setViewer(PhabricatorUser::getOmnipotentUser()) 56 + ->withPHIDs($phids) 57 + ->execute(); 58 + $documents = mpull($documents, null, 'getPHID'); 59 + 60 + $invalid = array(); 61 + foreach ($phids as $phid) { 62 + if (empty($documents[$phid])) { 63 + $invalid[] = $phid; 64 + unset($documents[$phid]); 65 + } 66 + } 67 + 68 + if ($invalid) { 69 + $this->logEffect(self::DO_INVALID, $phids); 70 + } 71 + 72 + if (!$phids) { 73 + return; 74 + } 75 + 76 + $object = $adapter->getObject(); 77 + $author_phid = $object->getAuthorPHID(); 78 + 79 + $signatures = id(new LegalpadDocumentSignatureQuery()) 80 + ->setViewer(PhabricatorUser::getOmnipotentUser()) 81 + ->withDocumentPHIDs($phids) 82 + ->withSignerPHIDs(array($author_phid)) 83 + ->execute(); 84 + $signatures = mpull($signatures, null, 'getDocumentPHID'); 85 + 86 + $signed = array(); 87 + foreach ($phids as $phid) { 88 + if (isset($signatures[$phid])) { 89 + $signed[] = $phid; 90 + unset($phids[$phid]); 91 + } 92 + } 93 + 94 + if ($signed) { 95 + $this->logEffect(self::DO_SIGNED, $phids); 96 + } 97 + 98 + if (!$phids) { 99 + return; 100 + } 101 + 102 + $xaction = $adapter->newTransaction() 103 + ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) 104 + ->setMetadataValue('edge:type', $edgetype_legal) 105 + ->setNewValue( 106 + array( 107 + '+' => $phids, 108 + )); 109 + 110 + $adapter->queueTransaction($xaction); 111 + 112 + $this->logEffect(self::DO_REQUIRED, $phids); 113 + } 114 + 115 + protected function getActionEffectMap() { 116 + return array( 117 + self::DO_NO_TARGETS => array( 118 + 'icon' => 'fa-ban', 119 + 'color' => 'grey', 120 + 'name' => pht('No Targets'), 121 + ), 122 + self::DO_INVALID => array( 123 + 'icon' => 'fa-ban', 124 + 'color' => 'red', 125 + 'name' => pht('Invalid Targets'), 126 + ), 127 + self::DO_ALREADY_REQUIRED => array( 128 + 'icon' => 'fa-terminal', 129 + 'color' => 'grey', 130 + 'name' => pht('Already Required'), 131 + ), 132 + self::DO_SIGNED => array( 133 + 'icon' => 'fa-terminal', 134 + 'color' => 'green', 135 + 'name' => pht('Already Signed'), 136 + ), 137 + self::DO_REQUIRED => array( 138 + 'icon' => 'fa-terminal', 139 + 'color' => 'green', 140 + 'name' => pht('Required Signature'), 141 + ), 142 + ); 143 + } 144 + 145 + public function renderActionEffectDescription($type, $data) { 146 + switch ($type) { 147 + case self::DO_NO_TARGETS: 148 + return pht('Rule lists no targets.'); 149 + case self::DO_INVALID: 150 + return pht( 151 + '%s document(s) are not valid: %s.', 152 + new PhutilNumber(count($data)), 153 + $this->renderHandleList($data)); 154 + case self::DO_ALREADY_REQUIRED: 155 + return pht( 156 + '%s document signature(s) are already required: %s.', 157 + new PhutilNumber(count($data)), 158 + $this->renderHandleList($data)); 159 + case self::DO_SIGNED: 160 + return pht( 161 + '%s document(s) are already signed: %s.', 162 + new PhutilNumber(count($data)), 163 + $this->renderHandleList($data)); 164 + case self::DO_REQUIRED: 165 + return pht( 166 + 'Required %s signature(s): %s.', 167 + new PhutilNumber(count($data)), 168 + $this->renderHandleList($data)); 169 + } 170 + } 171 + 172 + public function getHeraldActionName() { 173 + return pht('Require signatures'); 174 + } 175 + 176 + public function supportsRuleType($rule_type) { 177 + return ($rule_type != HeraldRuleTypeConfig::RULE_TYPE_PERSONAL); 178 + } 179 + 180 + public function applyEffect($object, HeraldEffect $effect) { 181 + return $this->applyRequire($effect->getTarget()); 182 + } 183 + 184 + public function getHeraldActionStandardType() { 185 + return self::STANDARD_PHID_LIST; 186 + } 187 + 188 + protected function getDatasource() { 189 + return new LegalpadDocumentDatasource(); 190 + } 191 + 192 + public function renderActionDescription($value) { 193 + return pht( 194 + 'Require document signatures: %s.', 195 + $this->renderHandleList($value)); 196 + } 197 + }
+5
src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php
··· 1333 1333 'Added blocking reviewers: %2$s.', 1334 1334 ), 1335 1335 1336 + 'Required %s signature(s): %s.' => array( 1337 + 'Required a signature: %2$s.', 1338 + 'Required signatures: %2$s.', 1339 + ), 1340 + 1336 1341 ); 1337 1342 } 1338 1343