@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
fork

Configure Feed

Select the types of activity you want to include in your feed.

Don't run Herald build and mail rules when they don't make sense

Summary:
Ref T2543. Fixes T10109.

Currently, Herald only runs in Differential when a change updates the diff. This is partly for historical reasons, and partly because we don't want to restart builds every time someone makes a comment. However, this behavior is inconsistent with other applications (which always trigger on any change), and occasionally confusing to users (in T10109, for example) or otherwise undesirable.

A similar issue is that T2543 has introduced a "Draft" state, where revisions don't send normal mail until builds finish. This interacts poorly with "Send me an email" rules (which shouldn't do anything here) and particularly with "Send me an email + only run these actions the first time the rule matches", since that might have an effect like "do nothing when the revision is created, then never anything again since you already did nothing once".

To navigate both of these issues, let objects tell Herald that certain actions (like mail or builds) are currently forbidden. If a rule uses a field or action which is currently forbidden, the whole rule automatically fails before it executes, but doesn't count toward "only the first time" as far as Herald's tracking of rule execution is concerned.

Then, forbid mail for draft revisions, and forbid builds for revisions which didn't just get updated. Forbidding mail fixes the issues with "Send me an email" that were created by the introduction of the draft state.

Finally, make Herald run on every revision update, not just substantive updates to the diff. This resolves T10109.

Test Plan:
Created revisions via the draft -> submit workflow, saw different transcripts. Here's a mail action being forbidden for a draft:

{F5237324}

Here's a build action being forbidden for a "mundane" update:

{F5237326}

Reviewers: amckinley

Reviewed By: amckinley

Subscribers: PHID-OPKG-gm6ozazyms6q6i22gyam

Maniphest Tasks: T10109, T2543

Differential Revision: https://secure.phabricator.com/D18731

+359 -64
+10
src/__phutil_library_map__.php
··· 458 458 'DifferentialGetWorkingCopy' => 'applications/differential/DifferentialGetWorkingCopy.php', 459 459 'DifferentialGitSVNIDCommitMessageField' => 'applications/differential/field/DifferentialGitSVNIDCommitMessageField.php', 460 460 'DifferentialHarbormasterField' => 'applications/differential/customfield/DifferentialHarbormasterField.php', 461 + 'DifferentialHeraldStateReasons' => 'applications/differential/herald/DifferentialHeraldStateReasons.php', 461 462 'DifferentialHiddenComment' => 'applications/differential/storage/DifferentialHiddenComment.php', 462 463 'DifferentialHostField' => 'applications/differential/customfield/DifferentialHostField.php', 463 464 'DifferentialHovercardEngineExtension' => 'applications/differential/engineextension/DifferentialHovercardEngineExtension.php', ··· 1328 1329 'HeraldApplicationActionGroup' => 'applications/herald/action/HeraldApplicationActionGroup.php', 1329 1330 'HeraldApplyTranscript' => 'applications/herald/storage/transcript/HeraldApplyTranscript.php', 1330 1331 'HeraldBasicFieldGroup' => 'applications/herald/field/HeraldBasicFieldGroup.php', 1332 + 'HeraldBuildableState' => 'applications/herald/state/HeraldBuildableState.php', 1331 1333 'HeraldCommitAdapter' => 'applications/diffusion/herald/HeraldCommitAdapter.php', 1332 1334 'HeraldCondition' => 'applications/herald/storage/HeraldCondition.php', 1333 1335 'HeraldConditionTranscript' => 'applications/herald/storage/transcript/HeraldConditionTranscript.php', ··· 1351 1353 'HeraldGroup' => 'applications/herald/group/HeraldGroup.php', 1352 1354 'HeraldInvalidActionException' => 'applications/herald/engine/exception/HeraldInvalidActionException.php', 1353 1355 'HeraldInvalidConditionException' => 'applications/herald/engine/exception/HeraldInvalidConditionException.php', 1356 + 'HeraldMailableState' => 'applications/herald/state/HeraldMailableState.php', 1354 1357 'HeraldManageGlobalRulesCapability' => 'applications/herald/capability/HeraldManageGlobalRulesCapability.php', 1355 1358 'HeraldManiphestTaskAdapter' => 'applications/maniphest/herald/HeraldManiphestTaskAdapter.php', 1356 1359 'HeraldNewController' => 'applications/herald/controller/HeraldNewController.php', ··· 1388 1391 'HeraldSchemaSpec' => 'applications/herald/storage/HeraldSchemaSpec.php', 1389 1392 'HeraldSelectFieldValue' => 'applications/herald/value/HeraldSelectFieldValue.php', 1390 1393 'HeraldSpaceField' => 'applications/spaces/herald/HeraldSpaceField.php', 1394 + 'HeraldState' => 'applications/herald/state/HeraldState.php', 1395 + 'HeraldStateReasons' => 'applications/herald/state/HeraldStateReasons.php', 1391 1396 'HeraldSubscribersField' => 'applications/subscriptions/herald/HeraldSubscribersField.php', 1392 1397 'HeraldSupportActionGroup' => 'applications/herald/action/HeraldSupportActionGroup.php', 1393 1398 'HeraldSupportFieldGroup' => 'applications/herald/field/HeraldSupportFieldGroup.php', ··· 5467 5472 'DifferentialGetWorkingCopy' => 'Phobject', 5468 5473 'DifferentialGitSVNIDCommitMessageField' => 'DifferentialCommitMessageField', 5469 5474 'DifferentialHarbormasterField' => 'DifferentialCustomField', 5475 + 'DifferentialHeraldStateReasons' => 'HeraldStateReasons', 5470 5476 'DifferentialHiddenComment' => 'DifferentialDAO', 5471 5477 'DifferentialHostField' => 'DifferentialCustomField', 5472 5478 'DifferentialHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension', ··· 6458 6464 'HeraldApplicationActionGroup' => 'HeraldActionGroup', 6459 6465 'HeraldApplyTranscript' => 'Phobject', 6460 6466 'HeraldBasicFieldGroup' => 'HeraldFieldGroup', 6467 + 'HeraldBuildableState' => 'HeraldState', 6461 6468 'HeraldCommitAdapter' => array( 6462 6469 'HeraldAdapter', 6463 6470 'HarbormasterBuildableAdapterInterface', ··· 6487 6494 'HeraldGroup' => 'Phobject', 6488 6495 'HeraldInvalidActionException' => 'Exception', 6489 6496 'HeraldInvalidConditionException' => 'Exception', 6497 + 'HeraldMailableState' => 'HeraldState', 6490 6498 'HeraldManageGlobalRulesCapability' => 'PhabricatorPolicyCapability', 6491 6499 'HeraldManiphestTaskAdapter' => 'HeraldAdapter', 6492 6500 'HeraldNewController' => 'HeraldController', ··· 6531 6539 'HeraldSchemaSpec' => 'PhabricatorConfigSchemaSpec', 6532 6540 'HeraldSelectFieldValue' => 'HeraldFieldValue', 6533 6541 'HeraldSpaceField' => 'HeraldField', 6542 + 'HeraldState' => 'Phobject', 6543 + 'HeraldStateReasons' => 'Phobject', 6534 6544 'HeraldSubscribersField' => 'HeraldField', 6535 6545 'HeraldSupportActionGroup' => 'HeraldActionGroup', 6536 6546 'HeraldSupportFieldGroup' => 'HeraldFieldGroup',
+28 -20
src/applications/differential/editor/DifferentialTransactionEditor.php
··· 1003 1003 protected function shouldApplyHeraldRules( 1004 1004 PhabricatorLiskDAO $object, 1005 1005 array $xactions) { 1006 - 1007 - if ($this->getIsNewObject()) { 1008 - return true; 1009 - } 1010 - 1011 - foreach ($xactions as $xaction) { 1012 - switch ($xaction->getTransactionType()) { 1013 - case DifferentialTransaction::TYPE_UPDATE: 1014 - if (!$this->getIsCloseByCommit()) { 1015 - return true; 1016 - } 1017 - break; 1018 - case DifferentialRevisionCommandeerTransaction::TRANSACTIONTYPE: 1019 - // When users commandeer revisions, we may need to trigger 1020 - // signatures or author-based rules. 1021 - return true; 1022 - } 1023 - } 1024 - 1025 - return parent::shouldApplyHeraldRules($object, $xactions); 1006 + return true; 1026 1007 } 1027 1008 1028 1009 protected function didApplyHeraldRules( ··· 1210 1191 $adapter = HeraldDifferentialRevisionAdapter::newLegacyAdapter( 1211 1192 $revision, 1212 1193 $revision->getActiveDiff()); 1194 + 1195 + // If the object is still a draft, prevent "Send me an email" and other 1196 + // similar rules from acting yet. 1197 + if (!$object->shouldBroadcast()) { 1198 + $adapter->setForbiddenAction( 1199 + HeraldMailableState::STATECONST, 1200 + DifferentialHeraldStateReasons::REASON_DRAFT); 1201 + } 1202 + 1203 + // If this edit didn't actually change the diff (for example, a user 1204 + // edited the title or changed subscribers), prevent "Run build plan" 1205 + // and other similar rules from acting yet, since the build results will 1206 + // not (or, at least, should not) change unless the actual source changes. 1207 + $has_update = false; 1208 + $type_update = DifferentialTransaction::TYPE_UPDATE; 1209 + foreach ($xactions as $xaction) { 1210 + if ($xaction->getTransactionType() == $type_update) { 1211 + $has_update = true; 1212 + break; 1213 + } 1214 + } 1215 + 1216 + if (!$has_update) { 1217 + $adapter->setForbiddenAction( 1218 + HeraldBuildableState::STATECONST, 1219 + DifferentialHeraldStateReasons::REASON_UNCHANGED); 1220 + } 1213 1221 1214 1222 return $adapter; 1215 1223 }
+22
src/applications/differential/herald/DifferentialHeraldStateReasons.php
··· 1 + <?php 2 + 3 + final class DifferentialHeraldStateReasons 4 + extends HeraldStateReasons { 5 + 6 + const REASON_DRAFT = 'differential.draft'; 7 + const REASON_UNCHANGED = 'differential.unchanged'; 8 + 9 + public function explainReason($reason) { 10 + $reasons = array( 11 + self::REASON_DRAFT => pht( 12 + 'This revision is still an unsubmitted draft, so mail will not '. 13 + 'be sent yet.'), 14 + self::REASON_UNCHANGED => pht( 15 + 'The update which triggered Herald did not update the diff for '. 16 + 'this revision, so builds will not run.'), 17 + ); 18 + 19 + return idx($reasons, $reason); 20 + } 21 + 22 + }
+6
src/applications/harbormaster/herald/HarbormasterRunBuildPlansHeraldAction.php
··· 7 7 8 8 const ACTIONCONST = 'harbormaster.build'; 9 9 10 + public function getRequiredAdapterStates() { 11 + return array( 12 + HeraldBuildableState::STATECONST, 13 + ); 14 + } 15 + 10 16 public function getActionGroupKey() { 11 17 return HeraldSupportActionGroup::ACTIONGROUPKEY; 12 18 }
+12
src/applications/herald/action/HeraldAction.php
··· 17 17 const DO_STANDARD_PERMISSION = 'do.standard.permission'; 18 18 const DO_STANDARD_INVALID_ACTION = 'do.standard.invalid-action'; 19 19 const DO_STANDARD_WRONG_RULE_TYPE = 'do.standard.wrong-rule-type'; 20 + const DO_STANDARD_FORBIDDEN = 'do.standard.forbidden'; 20 21 21 22 abstract public function getHeraldActionName(); 22 23 abstract public function supportsObject($object); ··· 24 25 abstract public function applyEffect($object, HeraldEffect $effect); 25 26 26 27 abstract public function renderActionDescription($value); 28 + 29 + public function getRequiredAdapterStates() { 30 + return array(); 31 + } 27 32 28 33 protected function renderActionEffectDescription($type, $data) { 29 34 return null; ··· 336 341 'color' => 'red', 337 342 'name' => pht('Wrong Rule Type'), 338 343 ), 344 + self::DO_STANDARD_FORBIDDEN => array( 345 + 'icon' => 'fa-ban', 346 + 'color' => 'violet', 347 + 'name' => pht('Forbidden'), 348 + ), 339 349 ); 340 350 } 341 351 ··· 381 391 return pht( 382 392 'This action does not support rules of type "%s".', 383 393 $data); 394 + case self::DO_STANDARD_FORBIDDEN: 395 + return HeraldStateReasons::getExplanation($data); 384 396 } 385 397 386 398 return null;
+35
src/applications/herald/adapter/HeraldAdapter.php
··· 37 37 private $fieldMap; 38 38 private $actionMap; 39 39 private $edgeCache = array(); 40 + private $forbiddenActions = array(); 40 41 41 42 public function getEmailPHIDs() { 42 43 return array_values($this->emailPHIDs); ··· 1114 1115 $this->edgeCache[$type] = array_fuse($phids); 1115 1116 } 1116 1117 return $this->edgeCache[$type]; 1118 + } 1119 + 1120 + 1121 + /* -( Forbidden Actions )-------------------------------------------------- */ 1122 + 1123 + 1124 + final public function getForbiddenActions() { 1125 + return array_keys($this->forbiddenActions); 1126 + } 1127 + 1128 + final public function setForbiddenAction($action, $reason) { 1129 + $this->forbiddenActions[$action] = $reason; 1130 + return $this; 1131 + } 1132 + 1133 + final public function getRequiredFieldStates($field_key) { 1134 + return $this->requireFieldImplementation($field_key) 1135 + ->getRequiredAdapterStates(); 1136 + } 1137 + 1138 + final public function getRequiredActionStates($action_key) { 1139 + return $this->requireActionImplementation($action_key) 1140 + ->getRequiredAdapterStates(); 1141 + } 1142 + 1143 + final public function getForbiddenReason($action) { 1144 + if (!isset($this->forbiddenActions[$action])) { 1145 + throw new Exception( 1146 + pht( 1147 + 'Action "%s" is not forbidden!', 1148 + $action)); 1149 + } 1150 + 1151 + return $this->forbiddenActions[$action]; 1117 1152 } 1118 1153 1119 1154 }
+17 -3
src/applications/herald/controller/HeraldTranscriptController.php
··· 273 273 ->setTarget(phutil_tag('strong', array(), pht('Conditions')))); 274 274 275 275 foreach ($cond_xscripts as $cond_xscript) { 276 - if ($cond_xscript->getResult()) { 276 + if ($cond_xscript->isForbidden()) { 277 + $icon = 'fa-ban'; 278 + $color = 'indigo'; 279 + $result = pht('Forbidden'); 280 + } else if ($cond_xscript->getResult()) { 277 281 $icon = 'fa-check'; 278 282 $color = 'green'; 279 283 $result = pht('Passed'); ··· 284 288 } 285 289 286 290 if ($cond_xscript->getNote()) { 291 + $note_text = $cond_xscript->getNote(); 292 + if ($cond_xscript->isForbidden()) { 293 + $note_text = HeraldStateReasons::getExplanation($note_text); 294 + } 295 + 287 296 $note = phutil_tag( 288 297 'div', 289 298 array( 290 299 'class' => 'herald-condition-note', 291 300 ), 292 - $cond_xscript->getNote()); 301 + $note_text); 293 302 } else { 294 303 $note = null; 295 304 } ··· 310 319 $cond_list->addItem($cond_item); 311 320 } 312 321 313 - if ($rule_xscript->getResult()) { 322 + if ($rule_xscript->isForbidden()) { 323 + $last_icon = 'fa-ban'; 324 + $last_color = 'indigo'; 325 + $last_result = pht('Forbidden'); 326 + $last_note = pht('Object state prevented rule evaluation.'); 327 + } else if ($rule_xscript->getResult()) { 314 328 $last_icon = 'fa-check-circle'; 315 329 $last_color = 'green'; 316 330 $last_result = pht('Passed');
+163 -41
src/applications/herald/engine/HeraldEngine.php
··· 12 12 protected $object; 13 13 private $dryRun; 14 14 15 + private $forbiddenFields = array(); 16 + private $forbiddenActions = array(); 17 + 15 18 public function setDryRun($dry_run) { 16 19 $this->dryRun = $dry_run; 17 20 return $this; ··· 76 79 // This is not a dry run, and this rule is only supposed to be 77 80 // applied a single time, and it's already been applied... 78 81 // That means automatic failure. 79 - $xscript = id(new HeraldRuleTranscript()) 80 - ->setRuleID($rule->getID()) 82 + $this->newRuleTranscript($rule) 81 83 ->setResult(false) 82 - ->setRuleName($rule->getName()) 83 - ->setRuleOwner($rule->getAuthorPHID()) 84 84 ->setReason( 85 85 pht( 86 86 'This rule is only supposed to be repeated a single time, '. 87 87 'and it has already been applied.')); 88 - $this->transcript->addRuleTranscript($xscript); 88 + 89 89 $rule_matches = false; 90 90 } else { 91 - $rule_matches = $this->doesRuleMatch($rule, $object); 91 + if ($this->isForbidden($rule, $object)) { 92 + $this->newRuleTranscript($rule) 93 + ->setResult(HeraldRuleTranscript::RESULT_FORBIDDEN) 94 + ->setReason( 95 + pht( 96 + 'Object state is not compatible with rule.')); 97 + 98 + $rule_matches = false; 99 + } else { 100 + $rule_matches = $this->doesRuleMatch($rule, $object); 101 + } 92 102 } 93 103 } catch (HeraldRecursiveConditionsException $ex) { 94 104 $names = array(); 95 - foreach ($this->stack as $rule_id => $ignored) { 96 - $names[] = '"'.$rules[$rule_id]->getName().'"'; 105 + foreach ($this->stack as $rule_phid => $ignored) { 106 + $names[] = '"'.$rules[$rule_phid]->getName().'"'; 97 107 } 98 108 $names = implode(', ', $names); 99 - foreach ($this->stack as $rule_id => $ignored) { 100 - $xscript = new HeraldRuleTranscript(); 101 - $xscript->setRuleID($rule_id); 102 - $xscript->setResult(false); 103 - $xscript->setReason( 104 - pht( 105 - "Rules %s are recursively dependent upon one another! ". 106 - "Don't do this! You have formed an unresolvable cycle in the ". 107 - "dependency graph!", 108 - $names)); 109 - $xscript->setRuleName($rules[$rule_id]->getName()); 110 - $xscript->setRuleOwner($rules[$rule_id]->getAuthorPHID()); 111 - $this->transcript->addRuleTranscript($xscript); 109 + foreach ($this->stack as $rule_phid => $ignored) { 110 + $this->newRuleTranscript($rules[$rule_phid]) 111 + ->setResult(false) 112 + ->setReason( 113 + pht( 114 + "Rules %s are recursively dependent upon one another! ". 115 + "Don't do this! You have formed an unresolvable cycle in the ". 116 + "dependency graph!", 117 + $names)); 112 118 } 113 119 $rule_matches = false; 114 120 } ··· 309 315 } 310 316 } 311 317 312 - $rule_transcript = new HeraldRuleTranscript(); 313 - $rule_transcript->setRuleID($rule->getID()); 314 - $rule_transcript->setResult($result); 315 - $rule_transcript->setReason($reason); 316 - $rule_transcript->setRuleName($rule->getName()); 317 - $rule_transcript->setRuleOwner($rule->getAuthorPHID()); 318 - 319 - $this->transcript->addRuleTranscript($rule_transcript); 318 + $this->newRuleTranscript($rule) 319 + ->setResult($result) 320 + ->setReason($reason); 320 321 321 322 return $result; 322 323 } ··· 327 328 HeraldAdapter $object) { 328 329 329 330 $object_value = $this->getConditionObjectValue($condition, $object); 330 - $test_value = $condition->getValue(); 331 - 332 - $cond = $condition->getFieldCondition(); 333 - 334 - $transcript = new HeraldConditionTranscript(); 335 - $transcript->setRuleID($rule->getID()); 336 - $transcript->setConditionID($condition->getID()); 337 - $transcript->setFieldName($condition->getFieldName()); 338 - $transcript->setCondition($cond); 339 - $transcript->setTestValue($test_value); 331 + $transcript = $this->newConditionTranscript($rule, $condition); 340 332 341 333 try { 342 334 $result = $object->doesConditionMatch( ··· 350 342 } 351 343 352 344 $transcript->setResult($result); 353 - 354 - $this->transcript->addConditionTranscript($transcript); 355 345 356 346 return $result; 357 347 } ··· 444 434 } 445 435 446 436 return false; 437 + } 438 + 439 + private function newRuleTranscript(HeraldRule $rule) { 440 + $xscript = id(new HeraldRuleTranscript()) 441 + ->setRuleID($rule->getID()) 442 + ->setRuleName($rule->getName()) 443 + ->setRuleOwner($rule->getAuthorPHID()); 444 + 445 + $this->transcript->addRuleTranscript($xscript); 446 + 447 + return $xscript; 448 + } 449 + 450 + private function newConditionTranscript( 451 + HeraldRule $rule, 452 + HeraldCondition $condition) { 453 + 454 + $xscript = id(new HeraldConditionTranscript()) 455 + ->setRuleID($rule->getID()) 456 + ->setConditionID($condition->getID()) 457 + ->setFieldName($condition->getFieldName()) 458 + ->setCondition($condition->getFieldCondition()) 459 + ->setTestValue($condition->getValue()); 460 + 461 + $this->transcript->addConditionTranscript($xscript); 462 + 463 + return $xscript; 464 + } 465 + 466 + private function newApplyTranscript( 467 + HeraldAdapter $adapter, 468 + HeraldRule $rule, 469 + HeraldActionRecord $action) { 470 + 471 + $effect = id(new HeraldEffect()) 472 + ->setObjectPHID($adapter->getPHID()) 473 + ->setAction($action->getAction()) 474 + ->setTarget($action->getTarget()) 475 + ->setRule($rule); 476 + 477 + $xscript = new HeraldApplyTranscript($effect, false); 478 + 479 + $this->transcript->addApplyTranscript($xscript); 480 + 481 + return $xscript; 482 + } 483 + 484 + private function isForbidden( 485 + HeraldRule $rule, 486 + HeraldAdapter $adapter) { 487 + 488 + $forbidden = $adapter->getForbiddenActions(); 489 + if (!$forbidden) { 490 + return false; 491 + } 492 + 493 + $forbidden = array_fuse($forbidden); 494 + 495 + $is_forbidden = false; 496 + 497 + foreach ($rule->getConditions() as $condition) { 498 + $field_key = $condition->getFieldName(); 499 + 500 + if (!isset($this->forbiddenFields[$field_key])) { 501 + $reason = null; 502 + 503 + try { 504 + $states = $adapter->getRequiredFieldStates($field_key); 505 + } catch (Exception $ex) { 506 + $states = array(); 507 + } 508 + 509 + foreach ($states as $state) { 510 + if (!isset($forbidden[$state])) { 511 + continue; 512 + } 513 + $reason = $adapter->getForbiddenReason($state); 514 + break; 515 + } 516 + 517 + $this->forbiddenFields[$field_key] = $reason; 518 + } 519 + 520 + $forbidden_reason = $this->forbiddenFields[$field_key]; 521 + if ($forbidden_reason !== null) { 522 + $this->newConditionTranscript($rule, $condition) 523 + ->setResult(HeraldConditionTranscript::RESULT_FORBIDDEN) 524 + ->setNote($forbidden_reason); 525 + 526 + $is_forbidden = true; 527 + } 528 + } 529 + 530 + foreach ($rule->getActions() as $action_record) { 531 + $action_key = $action_record->getAction(); 532 + 533 + if (!isset($this->forbiddenActions[$action_key])) { 534 + $reason = null; 535 + 536 + try { 537 + $states = $adapter->getRequiredActionStates($action_key); 538 + } catch (Exception $ex) { 539 + $states = array(); 540 + } 541 + 542 + foreach ($states as $state) { 543 + if (!isset($forbidden[$state])) { 544 + continue; 545 + } 546 + $reason = $adapter->getForbiddenReason($state); 547 + break; 548 + } 549 + 550 + $this->forbiddenActions[$action_key] = $reason; 551 + } 552 + 553 + $forbidden_reason = $this->forbiddenActions[$action_key]; 554 + if ($forbidden_reason !== null) { 555 + $this->newApplyTranscript($adapter, $rule, $action_record) 556 + ->setAppliedReason( 557 + array( 558 + array( 559 + 'type' => HeraldAction::DO_STANDARD_FORBIDDEN, 560 + 'data' => $forbidden_reason, 561 + ), 562 + )); 563 + 564 + $is_forbidden = true; 565 + } 566 + } 567 + 568 + return $is_forbidden; 447 569 } 448 570 449 571 }
+4
src/applications/herald/field/HeraldField.php
··· 20 20 return null; 21 21 } 22 22 23 + public function getRequiredAdapterStates() { 24 + return array(); 25 + } 26 + 23 27 protected function getHeraldFieldStandardType() { 24 28 throw new PhutilMethodNotImplementedException(); 25 29 }
+7
src/applications/herald/state/HeraldBuildableState.php
··· 1 + <?php 2 + 3 + final class HeraldBuildableState extends HeraldState { 4 + 5 + const STATECONST = 'buildable'; 6 + 7 + }
+7
src/applications/herald/state/HeraldMailableState.php
··· 1 + <?php 2 + 3 + final class HeraldMailableState extends HeraldState { 4 + 5 + const STATECONST = 'mailable'; 6 + 7 + }
+3
src/applications/herald/state/HeraldState.php
··· 1 + <?php 2 + 3 + abstract class HeraldState extends Phobject {}
+26
src/applications/herald/state/HeraldStateReasons.php
··· 1 + <?php 2 + 3 + abstract class HeraldStateReasons extends Phobject { 4 + 5 + abstract public function explainReason($reason); 6 + 7 + final public static function getAllReasons() { 8 + return id(new PhutilClassMapQuery()) 9 + ->setAncestorClass(__CLASS__) 10 + ->execute(); 11 + } 12 + 13 + final public static function getExplanation($reason) { 14 + $reasons = self::getAllReasons(); 15 + 16 + foreach ($reasons as $reason_implementation) { 17 + $explanation = $reason_implementation->explainReason($reason); 18 + if ($explanation !== null) { 19 + return $explanation; 20 + } 21 + } 22 + 23 + return pht('Unknown reason ("%s").', $reason); 24 + } 25 + 26 + }
+7
src/applications/herald/storage/transcript/HeraldConditionTranscript.php
··· 10 10 protected $note; 11 11 protected $result; 12 12 13 + const RESULT_FORBIDDEN = 'forbidden'; 14 + 13 15 public function setRuleID($rule_id) { 14 16 $this->ruleID = $rule_id; 15 17 return $this; ··· 72 74 public function getResult() { 73 75 return $this->result; 74 76 } 77 + 78 + public function isForbidden() { 79 + return ($this->getResult() === self::RESULT_FORBIDDEN); 80 + } 81 + 75 82 }
+6
src/applications/herald/storage/transcript/HeraldRuleTranscript.php
··· 9 9 protected $ruleName; 10 10 protected $ruleOwner; 11 11 12 + const RESULT_FORBIDDEN = 'forbidden'; 13 + 14 + public function isForbidden() { 15 + return ($this->getResult() === self::RESULT_FORBIDDEN); 16 + } 17 + 12 18 public function setResult($result) { 13 19 $this->result = $result; 14 20 return $this;
+6
src/applications/metamta/herald/PhabricatorMetaMTAEmailHeraldAction.php
··· 6 6 const DO_SEND = 'do.send'; 7 7 const DO_FORCE = 'do.force'; 8 8 9 + public function getRequiredAdapterStates() { 10 + return array( 11 + HeraldMailableState::STATECONST, 12 + ); 13 + } 14 + 9 15 public function supportsObject($object) { 10 16 // NOTE: This implementation lacks generality, but there's no great way to 11 17 // figure out if something generates email right now.