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

Make "Add Subscribers" and "Remove Subscribers" Herald actions modular

Summary: Ref T8726. Converts these actions to be modular. No real surprises in this change.

Test Plan:
{F658709}

- Wrote some rules.
- Migrated them forward.
- Used a bunch of these rules.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: joshuaspence, epriestley

Maniphest Tasks: T8726

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

+513 -150
+27
resources/sql/autopatches/20150724.herald.1.sql
··· 1 + UPDATE {$NAMESPACE}_herald.herald_actionrecord a 2 + JOIN {$NAMESPACE}_herald.herald_rule r 3 + ON a.ruleID = r.id 4 + SET a.action = 'subscribers.add' 5 + WHERE r.ruleType != 'personal' 6 + AND a.action = 'addcc'; 7 + 8 + UPDATE {$NAMESPACE}_herald.herald_actionrecord a 9 + JOIN {$NAMESPACE}_herald.herald_rule r 10 + ON a.ruleID = r.id 11 + SET a.action = 'subscribers.self.add' 12 + WHERE r.ruleType = 'personal' 13 + AND a.action = 'addcc'; 14 + 15 + UPDATE {$NAMESPACE}_herald.herald_actionrecord a 16 + JOIN {$NAMESPACE}_herald.herald_rule r 17 + ON a.ruleID = r.id 18 + SET a.action = 'subscribers.remove' 19 + WHERE r.ruleType != 'personal' 20 + AND a.action = 'remcc'; 21 + 22 + UPDATE {$NAMESPACE}_herald.herald_actionrecord a 23 + JOIN {$NAMESPACE}_herald.herald_rule r 24 + ON a.ruleID = r.id 25 + SET a.action = 'subscribers.self.remove' 26 + WHERE r.ruleType = 'personal' 27 + AND a.action = 'remcc';
+10
src/__phutil_library_map__.php
··· 2880 2880 'PhabricatorSubscribedToObjectEdgeType' => 'applications/transactions/edges/PhabricatorSubscribedToObjectEdgeType.php', 2881 2881 'PhabricatorSubscribersQuery' => 'applications/subscriptions/query/PhabricatorSubscribersQuery.php', 2882 2882 'PhabricatorSubscriptionTriggerClock' => 'infrastructure/daemon/workers/clock/PhabricatorSubscriptionTriggerClock.php', 2883 + 'PhabricatorSubscriptionsAddSelfHeraldAction' => 'applications/subscriptions/herald/PhabricatorSubscriptionsAddSelfHeraldAction.php', 2884 + 'PhabricatorSubscriptionsAddSubscribersHeraldAction' => 'applications/subscriptions/herald/PhabricatorSubscriptionsAddSubscribersHeraldAction.php', 2883 2885 'PhabricatorSubscriptionsApplication' => 'applications/subscriptions/application/PhabricatorSubscriptionsApplication.php', 2884 2886 'PhabricatorSubscriptionsEditController' => 'applications/subscriptions/controller/PhabricatorSubscriptionsEditController.php', 2885 2887 'PhabricatorSubscriptionsEditor' => 'applications/subscriptions/editor/PhabricatorSubscriptionsEditor.php', 2888 + 'PhabricatorSubscriptionsHeraldAction' => 'applications/subscriptions/herald/PhabricatorSubscriptionsHeraldAction.php', 2886 2889 'PhabricatorSubscriptionsListController' => 'applications/subscriptions/controller/PhabricatorSubscriptionsListController.php', 2890 + 'PhabricatorSubscriptionsRemoveSelfHeraldAction' => 'applications/subscriptions/herald/PhabricatorSubscriptionsRemoveSelfHeraldAction.php', 2891 + 'PhabricatorSubscriptionsRemoveSubscribersHeraldAction' => 'applications/subscriptions/herald/PhabricatorSubscriptionsRemoveSubscribersHeraldAction.php', 2887 2892 'PhabricatorSubscriptionsSubscribeEmailCommand' => 'applications/subscriptions/command/PhabricatorSubscriptionsSubscribeEmailCommand.php', 2888 2893 'PhabricatorSubscriptionsSubscribersPolicyRule' => 'applications/subscriptions/policyrule/PhabricatorSubscriptionsSubscribersPolicyRule.php', 2889 2894 'PhabricatorSubscriptionsTransactionController' => 'applications/subscriptions/controller/PhabricatorSubscriptionsTransactionController.php', ··· 6894 6899 'PhabricatorSubscribedToObjectEdgeType' => 'PhabricatorEdgeType', 6895 6900 'PhabricatorSubscribersQuery' => 'PhabricatorQuery', 6896 6901 'PhabricatorSubscriptionTriggerClock' => 'PhabricatorTriggerClock', 6902 + 'PhabricatorSubscriptionsAddSelfHeraldAction' => 'PhabricatorSubscriptionsHeraldAction', 6903 + 'PhabricatorSubscriptionsAddSubscribersHeraldAction' => 'PhabricatorSubscriptionsHeraldAction', 6897 6904 'PhabricatorSubscriptionsApplication' => 'PhabricatorApplication', 6898 6905 'PhabricatorSubscriptionsEditController' => 'PhabricatorController', 6899 6906 'PhabricatorSubscriptionsEditor' => 'PhabricatorEditor', 6907 + 'PhabricatorSubscriptionsHeraldAction' => 'HeraldAction', 6900 6908 'PhabricatorSubscriptionsListController' => 'PhabricatorController', 6909 + 'PhabricatorSubscriptionsRemoveSelfHeraldAction' => 'PhabricatorSubscriptionsHeraldAction', 6910 + 'PhabricatorSubscriptionsRemoveSubscribersHeraldAction' => 'PhabricatorSubscriptionsHeraldAction', 6901 6911 'PhabricatorSubscriptionsSubscribeEmailCommand' => 'MetaMTAEmailTransactionCommand', 6902 6912 'PhabricatorSubscriptionsSubscribersPolicyRule' => 'PhabricatorPolicyRule', 6903 6913 'PhabricatorSubscriptionsTransactionController' => 'PhabricatorController',
-4
src/applications/differential/herald/HeraldDifferentialRevisionAdapter.php
··· 162 162 case HeraldRuleTypeConfig::RULE_TYPE_GLOBAL: 163 163 return array_merge( 164 164 array( 165 - self::ACTION_ADD_CC, 166 - self::ACTION_REMOVE_CC, 167 165 self::ACTION_EMAIL, 168 166 self::ACTION_ADD_REVIEWERS, 169 167 self::ACTION_ADD_BLOCKING_REVIEWERS, ··· 174 172 case HeraldRuleTypeConfig::RULE_TYPE_PERSONAL: 175 173 return array_merge( 176 174 array( 177 - self::ACTION_ADD_CC, 178 - self::ACTION_REMOVE_CC, 179 175 self::ACTION_EMAIL, 180 176 self::ACTION_ADD_REVIEWERS, 181 177 self::ACTION_ADD_BLOCKING_REVIEWERS,
-4
src/applications/diffusion/herald/HeraldCommitAdapter.php
··· 89 89 case HeraldRuleTypeConfig::RULE_TYPE_OBJECT: 90 90 return array_merge( 91 91 array( 92 - self::ACTION_ADD_CC, 93 - self::ACTION_REMOVE_CC, 94 92 self::ACTION_EMAIL, 95 93 self::ACTION_AUDIT, 96 94 self::ACTION_APPLY_BUILD_PLANS, ··· 99 97 case HeraldRuleTypeConfig::RULE_TYPE_PERSONAL: 100 98 return array_merge( 101 99 array( 102 - self::ACTION_ADD_CC, 103 - self::ACTION_REMOVE_CC, 104 100 self::ACTION_EMAIL, 105 101 self::ACTION_AUDIT, 106 102 ),
+23 -1
src/applications/herald/action/HeraldAction.php
··· 41 41 return new HeraldEmptyFieldValue(); 42 42 case self::STANDARD_PHID_LIST: 43 43 $tokenizer = id(new HeraldTokenizerFieldValue()) 44 - ->setKey($this->getHeraldFieldName()) 44 + ->setKey($this->getHeraldActionName()) 45 45 ->setDatasource($this->getDatasource()); 46 46 47 47 $value_map = $this->getDatasourceValueMap(); ··· 56 56 } 57 57 58 58 public function willSaveActionValue($value) { 59 + try { 60 + $type = $this->getHeraldActionStandardType(); 61 + } catch (PhutilMethodNotImplementedException $ex) { 62 + return $value; 63 + } 64 + 65 + switch ($type) { 66 + case self::STANDARD_PHID_LIST: 67 + return array_keys($value); 68 + } 69 + 59 70 return $value; 60 71 } 61 72 ··· 160 171 public function renderActionEffectName($type, $data) { 161 172 $map = $this->getActionEffectSpec($type); 162 173 return idx($map, 'name'); 174 + } 175 + 176 + protected function renderHandleList($phids) { 177 + if (!is_array($phids)) { 178 + return pht('(Invalid List)'); 179 + } 180 + 181 + return $this->getViewer() 182 + ->renderHandleList($phids) 183 + ->setAsInline(true) 184 + ->render(); 163 185 } 164 186 165 187 }
+30 -114
src/applications/herald/adapter/HeraldAdapter.php
··· 26 26 const CONDITION_IS_TRUE = 'true'; 27 27 const CONDITION_IS_FALSE = 'false'; 28 28 29 - const ACTION_ADD_CC = 'addcc'; 30 - const ACTION_REMOVE_CC = 'remcc'; 31 29 const ACTION_EMAIL = 'email'; 32 30 const ACTION_AUDIT = 'audit'; 33 31 const ACTION_ASSIGN_TASK = 'assigntask'; ··· 46 44 private $queuedTransactions = array(); 47 45 private $emailPHIDs = array(); 48 46 private $forcedEmailPHIDs = array(); 49 - private $unsubscribedPHIDs; 50 47 private $fieldMap; 51 48 private $actionMap; 49 + private $edgeCache = array(); 52 50 53 51 public function getEmailPHIDs() { 54 52 return array_values($this->emailPHIDs); ··· 179 177 return $this->queuedTransactions; 180 178 } 181 179 182 - protected function newTransaction() { 180 + public function newTransaction() { 183 181 $object = $this->newObject(); 184 182 185 183 if (!($object instanceof PhabricatorApplicationTransactionInterface)) { ··· 723 721 case HeraldRuleTypeConfig::RULE_TYPE_GLOBAL: 724 722 case HeraldRuleTypeConfig::RULE_TYPE_OBJECT: 725 723 $standard = array( 726 - self::ACTION_ADD_CC => pht('Add Subscribers'), 727 - self::ACTION_REMOVE_CC => pht('Remove Subscribers'), 728 724 self::ACTION_EMAIL => pht('Send an email to'), 729 725 self::ACTION_AUDIT => pht('Trigger an Audit by'), 730 726 self::ACTION_ASSIGN_TASK => pht('Assign task to'), ··· 739 735 break; 740 736 case HeraldRuleTypeConfig::RULE_TYPE_PERSONAL: 741 737 $standard = array( 742 - self::ACTION_ADD_CC => pht('Add me as a subscriber'), 743 - self::ACTION_REMOVE_CC => pht('Remove me as a subscriber'), 744 738 self::ACTION_EMAIL => pht('Send me an email'), 745 739 self::ACTION_AUDIT => pht('Trigger an Audit by me'), 746 740 self::ACTION_ASSIGN_TASK => pht('Assign task to me'), ··· 786 780 if ($rule_type == HeraldRuleTypeConfig::RULE_TYPE_PERSONAL) { 787 781 switch ($action->getAction()) { 788 782 case self::ACTION_EMAIL: 789 - case self::ACTION_ADD_CC: 790 - case self::ACTION_REMOVE_CC: 791 783 case self::ACTION_AUDIT: 792 784 case self::ACTION_ASSIGN_TASK: 793 785 case self::ACTION_ADD_REVIEWERS: ··· 828 820 829 821 if ($is_personal) { 830 822 switch ($action) { 831 - case self::ACTION_ADD_CC: 832 - case self::ACTION_REMOVE_CC: 833 823 case self::ACTION_EMAIL: 834 824 case self::ACTION_AUDIT: 835 825 case self::ACTION_ASSIGN_TASK: ··· 843 833 } 844 834 } else { 845 835 switch ($action) { 846 - case self::ACTION_ADD_CC: 847 - case self::ACTION_REMOVE_CC: 848 836 case self::ACTION_EMAIL: 849 837 return $this->buildTokenizerFieldValue( 850 838 new PhabricatorMetaMTAMailableDatasource()); ··· 1048 1036 PhabricatorUser $viewer) { 1049 1037 1050 1038 $field_type = $condition->getFieldName(); 1039 + $field = $this->getFieldImplementation($field_type); 1051 1040 1052 - $default = pht('(Unknown Field "%s")', $field_type); 1041 + if (!$field) { 1042 + return pht('Unknown Field: "%s"', $field_type); 1043 + } 1053 1044 1054 - $field_name = idx($this->getFieldNameMap(), $field_type, $default); 1045 + $field_name = $field->getHeraldFieldName(); 1055 1046 1056 1047 $condition_type = $condition->getFieldCondition(); 1057 1048 $condition_name = idx($this->getConditionNameMap(), $condition_type); 1058 1049 1059 1050 $value = $this->renderConditionValueAsText($condition, $handles, $viewer); 1060 1051 1061 - return hsprintf(' %s %s %s', $field_name, $condition_name, $value); 1052 + return array( 1053 + $field_name, 1054 + $condition_name, 1055 + $value, 1056 + ); 1062 1057 } 1063 1058 1064 1059 private function renderActionAsText( ··· 1068 1063 1069 1064 $impl = $this->getActionImplementation($action->getAction()); 1070 1065 if ($impl) { 1066 + $impl->setViewer($viewer); 1067 + 1071 1068 $value = $action->getTarget(); 1072 - return $impl->renderActionDescription($viewer, $value); 1069 + return $impl->renderActionDescription($value); 1073 1070 } 1074 1071 1075 1072 $rule_global = HeraldRuleTypeConfig::RULE_TYPE_GLOBAL; ··· 1180 1177 */ 1181 1178 protected function applyStandardEffect(HeraldEffect $effect) { 1182 1179 $action = $effect->getAction(); 1180 + $rule_type = $effect->getRule()->getRuleType(); 1183 1181 1184 1182 $impl = $this->getActionImplementation($action); 1185 1183 if ($impl) { 1186 - $impl->applyEffect($this->getObject(), $effect); 1187 - return $impl->getApplyTranscript($effect); 1184 + if ($impl->supportsRuleType($rule_type)) { 1185 + $impl->applyEffect($this->getObject(), $effect); 1186 + return $impl->getApplyTranscript($effect); 1187 + } 1188 1188 } 1189 1189 1190 - $rule_type = $effect->getRule()->getRuleType(); 1191 1190 $supported = $this->getActions($rule_type); 1192 1191 $supported = array_fuse($supported); 1193 1192 if (empty($supported[$action])) { ··· 1205 1204 case self::ACTION_ADD_PROJECTS: 1206 1205 case self::ACTION_REMOVE_PROJECTS: 1207 1206 return $this->applyProjectsEffect($effect); 1208 - case self::ACTION_ADD_CC: 1209 - case self::ACTION_REMOVE_CC: 1210 - return $this->applySubscribersEffect($effect); 1211 1207 case self::ACTION_EMAIL: 1212 1208 return $this->applyEmailEffect($effect); 1213 1209 default: ··· 1257 1253 pht('Added projects.')); 1258 1254 } 1259 1255 1260 - /** 1261 - * @task apply 1262 - */ 1263 - private function applySubscribersEffect(HeraldEffect $effect) { 1264 - if ($effect->getAction() == self::ACTION_ADD_CC) { 1265 - $kind = '+'; 1266 - $is_add = true; 1267 - } else { 1268 - $kind = '-'; 1269 - $is_add = false; 1270 - } 1271 - 1272 - $subscriber_phids = array_fuse($effect->getTarget()); 1273 - if (!$subscriber_phids) { 1274 - return new HeraldApplyTranscript( 1275 - $effect, 1276 - false, 1277 - pht('This action lists no users or objects to affect.')); 1278 - } 1279 - 1280 - // The "Add Subscribers" rule only adds subscribers who haven't previously 1281 - // unsubscribed from the object explicitly. Filter these subscribers out 1282 - // before continuing. 1283 - $unsubscribed = array(); 1284 - if ($is_add) { 1285 - if ($this->unsubscribedPHIDs === null) { 1286 - $this->unsubscribedPHIDs = PhabricatorEdgeQuery::loadDestinationPHIDs( 1287 - $this->getObject()->getPHID(), 1288 - PhabricatorObjectHasUnsubscriberEdgeType::EDGECONST); 1289 - } 1290 - 1291 - foreach ($this->unsubscribedPHIDs as $phid) { 1292 - if (isset($subscriber_phids[$phid])) { 1293 - $unsubscribed[$phid] = $phid; 1294 - unset($subscriber_phids[$phid]); 1295 - } 1296 - } 1297 - } 1298 - 1299 - if (!$subscriber_phids) { 1300 - return new HeraldApplyTranscript( 1301 - $effect, 1302 - false, 1303 - pht('All targets have previously unsubscribed explicitly.')); 1304 - } 1305 - 1306 - // Filter out PHIDs which aren't valid subscribers. Lower levels of the 1307 - // stack will fail loudly if we try to add subscribers with invalid PHIDs 1308 - // or unknown PHID types, so drop them here. 1309 - $invalid = array(); 1310 - foreach ($subscriber_phids as $phid) { 1311 - $type = phid_get_type($phid); 1312 - switch ($type) { 1313 - case PhabricatorPeopleUserPHIDType::TYPECONST: 1314 - case PhabricatorProjectProjectPHIDType::TYPECONST: 1315 - break; 1316 - default: 1317 - $invalid[$phid] = $phid; 1318 - unset($subscriber_phids[$phid]); 1319 - break; 1320 - } 1321 - } 1322 - 1323 - if (!$subscriber_phids) { 1324 - return new HeraldApplyTranscript( 1325 - $effect, 1326 - false, 1327 - pht('All targets are invalid as subscribers.')); 1328 - } 1329 - 1330 - $xaction = $this->newTransaction() 1331 - ->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS) 1332 - ->setNewValue( 1333 - array( 1334 - $kind => $subscriber_phids, 1335 - )); 1336 - 1337 - $this->queueTransaction($xaction); 1338 - 1339 - // TODO: We could be more detailed about this, but doing it meaningfully 1340 - // probably requires substantial changes to how transactions are rendered 1341 - // first. 1342 - if ($is_add) { 1343 - $message = pht('Subscribed targets.'); 1344 - } else { 1345 - $message = pht('Unsubscribed targets.'); 1346 - } 1347 - 1348 - return new HeraldApplyTranscript($effect, true, $message); 1349 - } 1350 1256 1351 1257 /** 1352 1258 * @task apply ··· 1370 1276 pht('Added mailable to mail targets.')); 1371 1277 } 1372 1278 1279 + public function loadEdgePHIDs($type) { 1280 + if (!isset($this->edgeCache[$type])) { 1281 + $phids = PhabricatorEdgeQuery::loadDestinationPHIDs( 1282 + $this->getObject()->getPHID(), 1283 + $type); 1284 + 1285 + $this->edgeCache[$type] = array_fuse($phids); 1286 + } 1287 + return $this->edgeCache[$type]; 1288 + } 1373 1289 1374 1290 }
-4
src/applications/maniphest/herald/HeraldManiphestTaskAdapter.php
··· 72 72 case HeraldRuleTypeConfig::RULE_TYPE_GLOBAL: 73 73 return array_merge( 74 74 array( 75 - self::ACTION_ADD_CC, 76 - self::ACTION_REMOVE_CC, 77 75 self::ACTION_EMAIL, 78 76 self::ACTION_ASSIGN_TASK, 79 77 ), ··· 81 79 case HeraldRuleTypeConfig::RULE_TYPE_PERSONAL: 82 80 return array_merge( 83 81 array( 84 - self::ACTION_ADD_CC, 85 - self::ACTION_REMOVE_CC, 86 82 self::ACTION_EMAIL, 87 83 self::ACTION_ASSIGN_TASK, 88 84 ),
-19
src/applications/pholio/herald/HeraldPholioMockAdapter.php
··· 47 47 } 48 48 } 49 49 50 - public function getActions($rule_type) { 51 - switch ($rule_type) { 52 - case HeraldRuleTypeConfig::RULE_TYPE_GLOBAL: 53 - return array_merge( 54 - array( 55 - self::ACTION_ADD_CC, 56 - self::ACTION_REMOVE_CC, 57 - ), 58 - parent::getActions($rule_type)); 59 - case HeraldRuleTypeConfig::RULE_TYPE_PERSONAL: 60 - return array_merge( 61 - array( 62 - self::ACTION_ADD_CC, 63 - self::ACTION_REMOVE_CC, 64 - ), 65 - parent::getActions($rule_type)); 66 - } 67 - } 68 - 69 50 public function getHeraldName() { 70 51 return 'M'.$this->getMock()->getID(); 71 52 }
-4
src/applications/phriction/herald/PhrictionDocumentHeraldAdapter.php
··· 53 53 case HeraldRuleTypeConfig::RULE_TYPE_GLOBAL: 54 54 return array_merge( 55 55 array( 56 - self::ACTION_ADD_CC, 57 - self::ACTION_REMOVE_CC, 58 56 self::ACTION_EMAIL, 59 57 ), 60 58 parent::getActions($rule_type)); 61 59 case HeraldRuleTypeConfig::RULE_TYPE_PERSONAL: 62 60 return array_merge( 63 61 array( 64 - self::ACTION_ADD_CC, 65 - self::ACTION_REMOVE_CC, 66 62 self::ACTION_EMAIL, 67 63 ), 68 64 parent::getActions($rule_type));
+37
src/applications/subscriptions/herald/PhabricatorSubscriptionsAddSelfHeraldAction.php
··· 1 + <?php 2 + 3 + final class PhabricatorSubscriptionsAddSelfHeraldAction 4 + extends PhabricatorSubscriptionsHeraldAction { 5 + 6 + const ACTIONCONST = 'subscribers.self.add'; 7 + 8 + public function getHeraldActionName() { 9 + return pht('Add me as a subscriber'); 10 + } 11 + 12 + public function getActionGroupKey() { 13 + return HeraldSupportActionGroup::ACTIONGROUPKEY; 14 + } 15 + 16 + public function supportsObject($object) { 17 + return ($object instanceof PhabricatorSubscribableInterface); 18 + } 19 + 20 + public function supportsRuleType($rule_type) { 21 + return ($rule_type == HeraldRuleTypeConfig::RULE_TYPE_PERSONAL); 22 + } 23 + 24 + public function applyEffect($object, HeraldEffect $effect) { 25 + $phid = $effect->getRule()->getAuthorPHID(); 26 + return $this->applySubscribe(array($phid), $is_add = true); 27 + } 28 + 29 + public function getHeraldActionStandardType() { 30 + return self::STANDARD_NONE; 31 + } 32 + 33 + public function renderActionDescription($value) { 34 + return pht('Add rule author as subscriber.'); 35 + } 36 + 37 + }
+40
src/applications/subscriptions/herald/PhabricatorSubscriptionsAddSubscribersHeraldAction.php
··· 1 + <?php 2 + 3 + final class PhabricatorSubscriptionsAddSubscribersHeraldAction 4 + extends PhabricatorSubscriptionsHeraldAction { 5 + 6 + const ACTIONCONST = 'subscribers.add'; 7 + 8 + public function getHeraldActionName() { 9 + return pht('Add subscribers'); 10 + } 11 + 12 + public function getActionGroupKey() { 13 + return HeraldSupportActionGroup::ACTIONGROUPKEY; 14 + } 15 + 16 + public function supportsObject($object) { 17 + return ($object instanceof PhabricatorSubscribableInterface); 18 + } 19 + 20 + public function supportsRuleType($rule_type) { 21 + return ($rule_type != HeraldRuleTypeConfig::RULE_TYPE_PERSONAL); 22 + } 23 + 24 + public function applyEffect($object, HeraldEffect $effect) { 25 + return $this->applySubscribe($effect->getTarget(), $is_add = true); 26 + } 27 + 28 + public function getHeraldActionStandardType() { 29 + return self::STANDARD_PHID_LIST; 30 + } 31 + 32 + protected function getDatasource() { 33 + return new PhabricatorMetaMTAMailableDatasource(); 34 + } 35 + 36 + public function renderActionDescription($value) { 37 + return pht('Add subscribers: %s.', $this->renderHandleList($value)); 38 + } 39 + 40 + }
+235
src/applications/subscriptions/herald/PhabricatorSubscriptionsHeraldAction.php
··· 1 + <?php 2 + 3 + abstract class PhabricatorSubscriptionsHeraldAction 4 + extends HeraldAction { 5 + 6 + const DO_NO_TARGETS = 'do.no-targets'; 7 + const DO_PREVIOUSLY_UNSUBSCRIBED = 'do.previously-unsubscribed'; 8 + const DO_INVALID = 'do.invalid'; 9 + const DO_AUTOSUBSCRIBED = 'do.autosubscribed'; 10 + const DO_ALREADY_SUBSCRIBED = 'do.already-subscribed'; 11 + const DO_ALREADY_UNSUBSCRIBED = 'do.already-unsubscribed'; 12 + const DO_SUBSCRIBED = 'do.subscribed'; 13 + const DO_UNSUBSCRIBED = 'do.unsubscribed'; 14 + 15 + protected function applySubscribe(array $phids, $is_add) { 16 + $adapter = $this->getAdapter(); 17 + 18 + if ($is_add) { 19 + $kind = '+'; 20 + } else { 21 + $kind = '-'; 22 + } 23 + 24 + $subscriber_phids = array_fuse($phids); 25 + if (!$subscriber_phids) { 26 + $this->logEffect(self::DO_NO_TARGETS); 27 + return; 28 + } 29 + 30 + // The "Add Subscribers" rule only adds subscribers who haven't previously 31 + // unsubscribed from the object explicitly. Filter these subscribers out 32 + // before continuing. 33 + if ($is_add) { 34 + $unsubscribed = $adapter->loadEdgePHIDs( 35 + PhabricatorObjectHasUnsubscriberEdgeType::EDGECONST); 36 + 37 + foreach ($unsubscribed as $phid) { 38 + if (isset($subscriber_phids[$phid])) { 39 + $unsubscribed[$phid] = $phid; 40 + unset($subscriber_phids[$phid]); 41 + } 42 + } 43 + 44 + if ($unsubscribed) { 45 + $this->logEffect( 46 + self::DO_PREVIOUSLY_UNSUBSCRIBED, 47 + array_values($unsubscribed)); 48 + } 49 + } 50 + 51 + if (!$subscriber_phids) { 52 + return; 53 + } 54 + 55 + // Filter out PHIDs which aren't valid subscribers. Lower levels of the 56 + // stack will fail loudly if we try to add subscribers with invalid PHIDs 57 + // or unknown PHID types, so drop them here. 58 + $invalid = array(); 59 + foreach ($subscriber_phids as $phid) { 60 + $type = phid_get_type($phid); 61 + switch ($type) { 62 + case PhabricatorPeopleUserPHIDType::TYPECONST: 63 + case PhabricatorProjectProjectPHIDType::TYPECONST: 64 + break; 65 + default: 66 + $invalid[$phid] = $phid; 67 + unset($subscriber_phids[$phid]); 68 + break; 69 + } 70 + } 71 + 72 + if ($invalid) { 73 + $this->logEffect(self::DO_INVALID, array_values($invalid)); 74 + } 75 + 76 + if (!$subscriber_phids) { 77 + return; 78 + } 79 + 80 + $auto = array(); 81 + $object = $adapter->getObject(); 82 + foreach ($subscriber_phids as $phid) { 83 + if ($object->isAutomaticallySubscribed($phid)) { 84 + $auto[$phid] = $phid; 85 + unset($subscriber_phids[$phid]); 86 + } 87 + } 88 + 89 + if ($auto) { 90 + $this->logEffect(self::DO_AUTOSUBSCRIBED, array_values($auto)); 91 + } 92 + 93 + if (!$subscriber_phids) { 94 + return; 95 + } 96 + 97 + $current = $adapter->loadEdgePHIDs( 98 + PhabricatorObjectHasSubscriberEdgeType::EDGECONST); 99 + 100 + if ($is_add) { 101 + $already = array(); 102 + foreach ($subscriber_phids as $phid) { 103 + if (isset($current[$phid])) { 104 + $already[$phid] = $phid; 105 + unset($subscriber_phids[$phid]); 106 + } 107 + } 108 + 109 + if ($already) { 110 + $this->logEffect(self::DO_ALREADY_SUBSCRIBED, $already); 111 + } 112 + } else { 113 + $already = array(); 114 + foreach ($subscriber_phids as $phid) { 115 + if (empty($current[$phid])) { 116 + $already[$phid] = $phid; 117 + unset($subscriber_phids[$phid]); 118 + } 119 + } 120 + 121 + if ($already) { 122 + $this->logEffect(self::DO_ALREADY_UNSUBSCRIBED, $already); 123 + } 124 + } 125 + 126 + if (!$subscriber_phids) { 127 + return; 128 + } 129 + 130 + $xaction = $adapter->newTransaction() 131 + ->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS) 132 + ->setNewValue( 133 + array( 134 + $kind => $subscriber_phids, 135 + )); 136 + 137 + $adapter->queueTransaction($xaction); 138 + 139 + if ($is_add) { 140 + $this->logEffect(self::DO_SUBSCRIBED, $subscriber_phids); 141 + } else { 142 + $this->logEffect(self::DO_UNSUBSCRIBED, $subscriber_phids); 143 + } 144 + } 145 + 146 + protected function getActionEffectMap() { 147 + return array( 148 + self::DO_NO_TARGETS => array( 149 + 'icon' => 'fa-ban', 150 + 'color' => 'grey', 151 + 'name' => pht('No Targets'), 152 + ), 153 + self::DO_PREVIOUSLY_UNSUBSCRIBED => array( 154 + 'icon' => 'fa-minus-circle', 155 + 'color' => 'grey', 156 + 'name' => pht('Previously Unsubscribed'), 157 + ), 158 + self::DO_AUTOSUBSCRIBED => array( 159 + 'icon' => 'fa-envelope', 160 + 'color' => 'grey', 161 + 'name' => pht('Automatically Subscribed'), 162 + ), 163 + self::DO_INVALID => array( 164 + 'icon' => 'fa-ban', 165 + 'color' => 'red', 166 + 'name' => pht('Invalid Targets'), 167 + ), 168 + self::DO_ALREADY_SUBSCRIBED => array( 169 + 'icon' => 'fa-chevron-right', 170 + 'color' => 'grey', 171 + 'name' => pht('Already Subscribed'), 172 + ), 173 + self::DO_ALREADY_UNSUBSCRIBED => array( 174 + 'icon' => 'fa-chevron-right', 175 + 'color' => 'grey', 176 + 'name' => pht('Already Unsubscribed'), 177 + ), 178 + self::DO_SUBSCRIBED => array( 179 + 'icon' => 'fa-envelope', 180 + 'color' => 'green', 181 + 'name' => pht('Added Subscribers'), 182 + ), 183 + self::DO_UNSUBSCRIBED => array( 184 + 'icon' => 'fa-minus-circle', 185 + 'color' => 'green', 186 + 'name' => pht('Removed Subscribers'), 187 + ), 188 + ); 189 + } 190 + 191 + public function renderActionEffectDescription($type, $data) { 192 + switch ($type) { 193 + case self::DO_NO_TARGETS: 194 + return pht('Rule lists no targets.'); 195 + case self::DO_PREVIOUSLY_UNSUBSCRIBED: 196 + return pht( 197 + 'Declined to resubscribe %s target(s) because they previously '. 198 + 'unsubscribed: %s.', 199 + new PhutilNumber(count($data)), 200 + $this->renderHandleList($data)); 201 + case self::DO_INVALID: 202 + return pht( 203 + 'Declined to act on %s invalid target(s): %s.', 204 + new PhutilNumber(count($data)), 205 + $this->renderHandleList($data)); 206 + case self::DO_AUTOSUBSCRIBED: 207 + return pht( 208 + '%s automatically subscribed target(s) were not affected: %s.', 209 + new PhutilNumber(count($data)), 210 + $this->renderHandleList($data)); 211 + case self::DO_ALREADY_SUBSCRIBED: 212 + return pht( 213 + '%s target(s) are already subscribed: %s.', 214 + new PhutilNumber(count($data)), 215 + $this->renderHandleList($data)); 216 + case self::DO_ALREADY_UNSUBSCRIBED: 217 + return pht( 218 + '%s target(s) are not subscribed: %s.', 219 + new PhutilNumber(count($data)), 220 + $this->renderHandleList($data)); 221 + case self::DO_SUBSCRIBED: 222 + return pht( 223 + 'Added %s subscriber(s): %s.', 224 + new PhutilNumber(count($data)), 225 + $this->renderHandleList($data)); 226 + case self::DO_UNSUBSCRIBED: 227 + return pht( 228 + 'Removed %s subscriber(s): %s.', 229 + new PhutilNumber(count($data)), 230 + $this->renderHandleList($data)); 231 + } 232 + } 233 + 234 + 235 + }
+37
src/applications/subscriptions/herald/PhabricatorSubscriptionsRemoveSelfHeraldAction.php
··· 1 + <?php 2 + 3 + final class PhabricatorSubscriptionsRemoveSelfHeraldAction 4 + extends PhabricatorSubscriptionsHeraldAction { 5 + 6 + const ACTIONCONST = 'subscribers.self.remove'; 7 + 8 + public function getHeraldActionName() { 9 + return pht('Remove me as a subscriber'); 10 + } 11 + 12 + public function getActionGroupKey() { 13 + return HeraldSupportActionGroup::ACTIONGROUPKEY; 14 + } 15 + 16 + public function supportsObject($object) { 17 + return ($object instanceof PhabricatorSubscribableInterface); 18 + } 19 + 20 + public function supportsRuleType($rule_type) { 21 + return ($rule_type == HeraldRuleTypeConfig::RULE_TYPE_PERSONAL); 22 + } 23 + 24 + public function applyEffect($object, HeraldEffect $effect) { 25 + $phid = $effect->getRule()->getAuthorPHID(); 26 + return $this->applySubscribe(array($phid), $is_add = false); 27 + } 28 + 29 + public function getHeraldActionStandardType() { 30 + return self::STANDARD_NONE; 31 + } 32 + 33 + public function renderActionDescription($value) { 34 + return pht('Remove rule author as subscriber.'); 35 + } 36 + 37 + }
+40
src/applications/subscriptions/herald/PhabricatorSubscriptionsRemoveSubscribersHeraldAction.php
··· 1 + <?php 2 + 3 + final class PhabricatorSubscriptionsRemoveSubscribersHeraldAction 4 + extends PhabricatorSubscriptionsHeraldAction { 5 + 6 + const ACTIONCONST = 'subscribers.remove'; 7 + 8 + public function getHeraldActionName() { 9 + return pht('Remove subscribers'); 10 + } 11 + 12 + public function getActionGroupKey() { 13 + return HeraldSupportActionGroup::ACTIONGROUPKEY; 14 + } 15 + 16 + public function supportsObject($object) { 17 + return ($object instanceof PhabricatorSubscribableInterface); 18 + } 19 + 20 + public function supportsRuleType($rule_type) { 21 + return ($rule_type != HeraldRuleTypeConfig::RULE_TYPE_PERSONAL); 22 + } 23 + 24 + public function applyEffect($object, HeraldEffect $effect) { 25 + return $this->applySubscribe($effect->getTarget(), $is_add = false); 26 + } 27 + 28 + public function getHeraldActionStandardType() { 29 + return self::STANDARD_PHID_LIST; 30 + } 31 + 32 + protected function getDatasource() { 33 + return new PhabricatorMetaMTAMailableDatasource(); 34 + } 35 + 36 + public function renderActionDescription($value) { 37 + return pht('Remove subscribers: %s.', $this->renderHandleList($value)); 38 + } 39 + 40 + }
+34
src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php
··· 1182 1182 '%s Task', 1183 1183 '%s Tasks', 1184 1184 ), 1185 + 1185 1186 '%s added %s badge(s) for %s: %s.' => array( 1186 1187 array( 1187 1188 '%s added a badge for %s: %3$s.', ··· 1254 1255 '%s revoked a recipient: %3$s.', 1255 1256 '%s revoked multiple recipients: %3$s.', 1256 1257 ), 1258 + ), 1259 + 1260 + '%s automatically subscribed target(s) were not affected: %s.' => array( 1261 + 'An automatically subscribed target was not affected: %2$s.', 1262 + 'Automatically subscribed targets were not affected: %2$s.', 1263 + ), 1264 + 1265 + 'Declined to resubscribe %s target(s) because they previously '. 1266 + 'unsubscribed: %s.' => array( 1267 + 'Delined to resubscribe a target because they previously '. 1268 + 'unsubscribed: %2$s.', 1269 + 'Declined to resubscribe targets because they previously '. 1270 + 'unsubscribed: %2$s.', 1271 + ), 1272 + 1273 + '%s target(s) are not subscribed: %s.' => array( 1274 + 'A target is not subscribed: %2$s.', 1275 + 'Targets are not subscribed: %2$s.', 1276 + ), 1277 + 1278 + '%s target(s) are already subscribed: %s.' => array( 1279 + 'A target is already subscribed: %2$s.', 1280 + 'Targets are already subscribed: %2$s.', 1281 + ), 1282 + 1283 + 'Added %s subscriber(s): %s.' => array( 1284 + 'Added a subscriber: %2$s.', 1285 + 'Added subscribers: %2$s.', 1286 + ), 1287 + 1288 + 'Removed %s subscriber(s): %s.' => array( 1289 + 'Removed a subscriber: %2$s.', 1290 + 'Removed subscribers: %2$s.', 1257 1291 ), 1258 1292 1259 1293 );