@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

Workboard Trigger Rule: allow to Add/Remove Subscribers

Summary:
This is a natural expansion of the Workboard Trigger system
to support actions about Subscribers.

Context: even before this change - Workboard users
were already able to move Tasks to Columns with
nice automatisms. For instance, you can already:

- Play a nice Sound
- Change Task Priority
- Change Task Status
- Add or Remove a Task Owner
- Add or Remove a Task Project Tag

With this change, you can also:

- Add or Remove Task Subscribers

If you need inspiration, this feature is useful for adding
more eyes on a given work area; lighten the notifications
of certain people after certain workflows have already been
done, to increase happiness and mitigate Burnout.

If your goal is to oppress yourself or your coworkers - of
course you can also use this feature to increase the Burnout
of yourself or your coworkers, adding random people as
Subscriber.

Note: every trigger action can be confusing in general, but
it is the fault of Triggers in general and not related to
this specific feature - that is totally loveable,
just like you.

Closes T15162

Test Plan:
- Workboard > Column > Create Trigger
- Add Action > Add Subscriber > (your best friend who hates NodeJS)
- Add Action > Remove Subscriber > (your antagonist who hates PHP)
- Move some Tasks there and here, and note that Add/Remove works

Reviewers: O1 Blessed Committers, Cigaryno, aklapper

Reviewed By: O1 Blessed Committers, Cigaryno, aklapper

Subscribers: aklapper, speck, tobiaswiese, Matthew, Cigaryno

Tags: #workboard

Maniphest Tasks: T15162

Differential Revision: https://we.phorge.it/D25080

+258
+4
src/__phutil_library_map__.php
··· 4491 4491 'PhabricatorProjectTransactionType' => 'applications/project/xaction/PhabricatorProjectTransactionType.php', 4492 4492 'PhabricatorProjectTrigger' => 'applications/project/storage/PhabricatorProjectTrigger.php', 4493 4493 'PhabricatorProjectTriggerAddProjectsRule' => 'applications/project/trigger/PhabricatorProjectTriggerAddProjectsRule.php', 4494 + 'PhabricatorProjectTriggerAddSubscribersRule' => 'applications/project/trigger/PhabricatorProjectTriggerAddSubscribersRule.php', 4494 4495 'PhabricatorProjectTriggerController' => 'applications/project/controller/trigger/PhabricatorProjectTriggerController.php', 4495 4496 'PhabricatorProjectTriggerCorruptionException' => 'applications/project/exception/PhabricatorProjectTriggerCorruptionException.php', 4496 4497 'PhabricatorProjectTriggerEditController' => 'applications/project/controller/trigger/PhabricatorProjectTriggerEditController.php', ··· 4505 4506 'PhabricatorProjectTriggerPlaySoundRule' => 'applications/project/trigger/PhabricatorProjectTriggerPlaySoundRule.php', 4506 4507 'PhabricatorProjectTriggerQuery' => 'applications/project/query/PhabricatorProjectTriggerQuery.php', 4507 4508 'PhabricatorProjectTriggerRemoveProjectsRule' => 'applications/project/trigger/PhabricatorProjectTriggerRemoveProjectsRule.php', 4509 + 'PhabricatorProjectTriggerRemoveSubscribersRule' => 'applications/project/trigger/PhabricatorProjectTriggerRemoveSubscribersRule.php', 4508 4510 'PhabricatorProjectTriggerRule' => 'applications/project/trigger/PhabricatorProjectTriggerRule.php', 4509 4511 'PhabricatorProjectTriggerRuleRecord' => 'applications/project/trigger/PhabricatorProjectTriggerRuleRecord.php', 4510 4512 'PhabricatorProjectTriggerRulesetTransaction' => 'applications/project/xaction/trigger/PhabricatorProjectTriggerRulesetTransaction.php', ··· 11129 11131 'PhabricatorDestructibleInterface', 11130 11132 ), 11131 11133 'PhabricatorProjectTriggerAddProjectsRule' => 'PhabricatorProjectTriggerRule', 11134 + 'PhabricatorProjectTriggerAddSubscribersRule' => 'PhabricatorProjectTriggerRule', 11132 11135 'PhabricatorProjectTriggerController' => 'PhabricatorProjectController', 11133 11136 'PhabricatorProjectTriggerCorruptionException' => 'Exception', 11134 11137 'PhabricatorProjectTriggerEditController' => 'PhabricatorProjectTriggerController', ··· 11143 11146 'PhabricatorProjectTriggerPlaySoundRule' => 'PhabricatorProjectTriggerRule', 11144 11147 'PhabricatorProjectTriggerQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 11145 11148 'PhabricatorProjectTriggerRemoveProjectsRule' => 'PhabricatorProjectTriggerRule', 11149 + 'PhabricatorProjectTriggerRemoveSubscribersRule' => 'PhabricatorProjectTriggerRule', 11146 11150 'PhabricatorProjectTriggerRule' => 'Phobject', 11147 11151 'PhabricatorProjectTriggerRuleRecord' => 'Phobject', 11148 11152 'PhabricatorProjectTriggerRulesetTransaction' => 'PhabricatorProjectTriggerTransactionType',
+127
src/applications/project/trigger/PhabricatorProjectTriggerAddSubscribersRule.php
··· 1 + <?php 2 + 3 + /** 4 + * Trigger Rule that Adds Subscribers 5 + * 6 + * This may be useful to automatically engage some 7 + * Users or Project Tags on certain Workboard areas. 8 + * 9 + * This class was adapted from these classes: 10 + * - PhabricatorProjectTriggerAddProjectsRule 11 + * - PhabricatorProjectTriggerManiphestOwnerRule 12 + * 13 + * https://we.phorge.it/T15162 14 + */ 15 + final class PhabricatorProjectTriggerAddSubscribersRule 16 + extends PhabricatorProjectTriggerRule { 17 + 18 + const TRIGGERTYPE = 'task.subscriber.add'; 19 + 20 + public function getSelectControlName() { 21 + return pht('Add subscribers'); 22 + } 23 + 24 + protected function getValueForEditorField() { 25 + return $this->getDatasource()->getWireTokens($this->getValue()); 26 + } 27 + 28 + protected function assertValidRuleRecordFormat($value) { 29 + if (!is_array($value)) { 30 + throw new Exception( 31 + pht( 32 + 'Add subscribers rule value should be a list, but is not '. 33 + '(value is "%s").', 34 + phutil_describe_type($value))); 35 + } 36 + } 37 + 38 + protected function assertValidRuleRecordValue($value) { 39 + if (!$value) { 40 + throw new Exception( 41 + pht( 42 + 'You must select at least one user or project tag to add.')); 43 + } 44 + } 45 + 46 + protected function newDropTransactions($object, $value) { 47 + $subscriber_edge_type = PhabricatorObjectHasSubscriberEdgeType::EDGECONST; 48 + 49 + $xaction = $object->getApplicationTransactionTemplate() 50 + ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) 51 + ->setMetadataValue('edge:type', $subscriber_edge_type) 52 + ->setNewValue( 53 + array( 54 + '+' => array_fuse($value), 55 + )); 56 + 57 + return array($xaction); 58 + } 59 + 60 + protected function newDropEffects($value) { 61 + return array( 62 + $this->newEffect() 63 + ->setIcon('fa-briefcase') 64 + ->setContent($this->getRuleViewDescription($value)), 65 + ); 66 + } 67 + 68 + protected function getDefaultValue() { 69 + return null; 70 + } 71 + 72 + protected function getPHUIXControlType() { 73 + return 'tokenizer'; 74 + } 75 + 76 + private function getDatasource() { 77 + $datasource = new PhabricatorProjectOrUserDatasource(); 78 + 79 + if ($this->getViewer()) { 80 + $datasource->setViewer($this->getViewer()); 81 + } 82 + 83 + return $datasource; 84 + } 85 + 86 + protected function getPHUIXControlSpecification() { 87 + $template = id(new AphrontTokenizerTemplateView()) 88 + ->setViewer($this->getViewer()); 89 + 90 + $template_markup = $template->render(); 91 + $datasource = $this->getDatasource(); 92 + 93 + return array( 94 + 'markup' => (string)hsprintf('%s', $template_markup), 95 + 'config' => array( 96 + 'src' => $datasource->getDatasourceURI(), 97 + 'browseURI' => $datasource->getBrowseURI(), 98 + 'placeholder' => $datasource->getPlaceholderText(), 99 + 'limit' => $datasource->getLimit(), 100 + ), 101 + 'value' => null, 102 + ); 103 + } 104 + 105 + public function getRuleViewLabel() { 106 + return pht('Add subscribers'); 107 + } 108 + 109 + public function getRuleViewDescription($value) { 110 + return pht( 111 + 'Add subscribers: %s.', 112 + phutil_tag( 113 + 'strong', 114 + array(), 115 + $this->getViewer() 116 + ->renderHandleList($value) 117 + ->setAsInline(true) 118 + ->render())); 119 + } 120 + 121 + public function getRuleViewIcon($value) { 122 + return id(new PHUIIconView()) 123 + ->setIcon('fa-users', 'green'); 124 + } 125 + 126 + 127 + }
+127
src/applications/project/trigger/PhabricatorProjectTriggerRemoveSubscribersRule.php
··· 1 + <?php 2 + 3 + /** 4 + * Trigger Rule that Removes Subscribers 5 + * 6 + * This may be useful to have a column that simplify 7 + * Task handovers. You can remove both Users or Project Tags 8 + * from the list of Task Subscribers. 9 + * 10 + * This class was adapted from these classes: 11 + * - PhabricatorProjectTriggerRemoveProjectsRule 12 + * - PhabricatorProjectTriggerManiphestOwnerRule 13 + * 14 + * https://we.phorge.it/T15162 15 + */ 16 + final class PhabricatorProjectTriggerRemoveSubscribersRule 17 + extends PhabricatorProjectTriggerRule { 18 + 19 + const TRIGGERTYPE = 'task.subscriber.remove'; 20 + 21 + public function getSelectControlName() { 22 + return pht('Remove subscribers'); 23 + } 24 + 25 + protected function getValueForEditorField() { 26 + return $this->getDatasource()->getWireTokens($this->getValue()); 27 + } 28 + 29 + protected function assertValidRuleRecordFormat($value) { 30 + if (!is_array($value)) { 31 + throw new Exception( 32 + pht( 33 + 'Remove subscribers rule value should be a list, but is not '. 34 + '(value is "%s").', 35 + phutil_describe_type($value))); 36 + } 37 + } 38 + 39 + protected function assertValidRuleRecordValue($value) { 40 + if (!$value) { 41 + throw new Exception( 42 + pht( 43 + 'You must select at least one user or project tag to remove.')); 44 + } 45 + } 46 + 47 + protected function newDropTransactions($object, $value) { 48 + $subscriber_edge_type = PhabricatorObjectHasSubscriberEdgeType::EDGECONST; 49 + 50 + $xaction = $object->getApplicationTransactionTemplate() 51 + ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) 52 + ->setMetadataValue('edge:type', $subscriber_edge_type) 53 + ->setNewValue( 54 + array( 55 + '-' => array_fuse($value), 56 + )); 57 + 58 + return array($xaction); 59 + } 60 + 61 + protected function newDropEffects($value) { 62 + return array( 63 + $this->newEffect() 64 + ->setIcon('fa-briefcase') 65 + ->setContent($this->getRuleViewDescription($value)), 66 + ); 67 + } 68 + 69 + protected function getDefaultValue() { 70 + return null; 71 + } 72 + 73 + protected function getPHUIXControlType() { 74 + return 'tokenizer'; 75 + } 76 + 77 + private function getDatasource() { 78 + $datasource = new PhabricatorProjectOrUserDatasource(); 79 + 80 + if ($this->getViewer()) { 81 + $datasource->setViewer($this->getViewer()); 82 + } 83 + 84 + return $datasource; 85 + } 86 + 87 + protected function getPHUIXControlSpecification() { 88 + $template = id(new AphrontTokenizerTemplateView()) 89 + ->setViewer($this->getViewer()); 90 + 91 + $template_markup = $template->render(); 92 + $datasource = $this->getDatasource(); 93 + 94 + return array( 95 + 'markup' => (string)hsprintf('%s', $template_markup), 96 + 'config' => array( 97 + 'src' => $datasource->getDatasourceURI(), 98 + 'browseURI' => $datasource->getBrowseURI(), 99 + 'placeholder' => $datasource->getPlaceholderText(), 100 + 'limit' => $datasource->getLimit(), 101 + ), 102 + 'value' => null, 103 + ); 104 + } 105 + 106 + public function getRuleViewLabel() { 107 + return pht('Remove subscribers'); 108 + } 109 + 110 + public function getRuleViewDescription($value) { 111 + return pht( 112 + 'Remove subscribers: %s.', 113 + phutil_tag( 114 + 'strong', 115 + array(), 116 + $this->getViewer() 117 + ->renderHandleList($value) 118 + ->setAsInline(true) 119 + ->render())); 120 + } 121 + 122 + public function getRuleViewIcon($value) { 123 + return id(new PHUIIconView()) 124 + ->setIcon('fa-users', 'red'); 125 + } 126 + 127 + }