Skip to content

Commit 522e5b4

Browse files
author
epriestley
committedSep 30, 2011
Build an event dispatch mechanism into Phabricator
Summary: This is an attempt to satisfy a lot of the one-off requests a little more generally, by providing a relatively generic piece of event architecture. Allow the registation of event listeners which can react to various application events (currently, task editing). I'll doc this a bit better but I wanted to see if anyone had massive objections to doing this or the broad approach. The specific problem I want to address is that one client wants to do a bunch of routing for tasks via email, so it's either build a hook, or have them override most of ManiphestReplyHandler, or something slightly more general like this. Test Plan: Wrote a silly listener that adds "Quack!" to a task every time it is edited and edited some tasks. I was justly rewarded. Reviewers: nh, jungejason, tuomaspelkonen, aran Reviewed By: aran CC: aran, epriestley Differential Revision: 881
1 parent 8e8d91a commit 522e5b4

23 files changed

+405
-2
lines changed
 

‎conf/default.conf.php

+7-2
Original file line numberDiff line numberDiff line change
@@ -602,7 +602,6 @@
602602
// projects that want to expose an activity feed on the project homepage.
603603
'feed.public' => false,
604604

605-
606605
// -- Customization --------------------------------------------------------- //
607606

608607
// Paths to additional phutil libraries to load.
@@ -636,7 +635,13 @@
636635
// settings are the defaults.)
637636
'celerity.force-disk-reads' => false,
638637

639-
// -- Pygments ------------------------------------------------------------ //
638+
// You can respond to various application events by installing listeners,
639+
// which will receive callbacks when interesting things occur. Specify a list
640+
// of classes which extend PhabricatorEventListener here.
641+
'events.listeners' => array(),
642+
643+
// -- Pygments -------------------------------------------------------------- //
644+
640645
// Phabricator can highlight PHP by default, but if you want syntax
641646
// highlighting for other languages you should install the python package
642647
// 'Pygments', make sure the 'pygmentize' script is available in the

‎scripts/__init_script__.php

+1
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,4 @@
3333
// NOTE: This is dangerous in general, but we know we're in a script context and
3434
// are not vulnerable to CSRF.
3535
AphrontWriteGuard::allowDangerousUnguardedWrites(true);
36+
PhabricatorEventEngine::initialize();

‎src/__phutil_library_map__.php

+6
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,11 @@
412412
'PhabricatorEmailLoginController' => 'applications/auth/controller/email',
413413
'PhabricatorEmailTokenController' => 'applications/auth/controller/emailtoken',
414414
'PhabricatorEnv' => 'infrastructure/env',
415+
'PhabricatorEvent' => 'infrastructure/events/event',
416+
'PhabricatorEventConstants' => 'infrastructure/events/constant/base',
417+
'PhabricatorEventEngine' => 'infrastructure/events/engine',
418+
'PhabricatorEventListener' => 'infrastructure/events/listener',
419+
'PhabricatorEventType' => 'infrastructure/events/constant/type',
415420
'PhabricatorFeedConstants' => 'applications/feed/constants/base',
416421
'PhabricatorFeedController' => 'applications/feed/controller/base',
417422
'PhabricatorFeedDAO' => 'applications/feed/storage/base',
@@ -1050,6 +1055,7 @@
10501055
'PhabricatorDraftDAO' => 'PhabricatorLiskDAO',
10511056
'PhabricatorEmailLoginController' => 'PhabricatorAuthController',
10521057
'PhabricatorEmailTokenController' => 'PhabricatorAuthController',
1058+
'PhabricatorEventType' => 'PhabricatorEventConstants',
10531059
'PhabricatorFeedController' => 'PhabricatorController',
10541060
'PhabricatorFeedDAO' => 'PhabricatorLiskDAO',
10551061
'PhabricatorFeedPublicStreamController' => 'PhabricatorFeedController',

‎src/applications/conduit/method/maniphest/createtask/ConduitAPI_maniphest_createtask_Method.php

+14
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,20 @@ protected function execute(ConduitAPIRequest $request) {
103103
$transactions[] = $transaction;
104104
}
105105

106+
$event = new PhabricatorEvent(
107+
PhabricatorEventType::TYPE_MANIPHEST_WILLEDITTASK,
108+
array(
109+
'task' => $task,
110+
'new' => true,
111+
'transactions' => $transactions,
112+
));
113+
$event->setUser($request->getUser());
114+
$event->setConduitRequest($request);
115+
PhabricatorEventEngine::dispatchEvent($event);
116+
117+
$task = $event->getValue('task');
118+
$transactions = $event->getValue('transactions');
119+
106120
$editor = new ManiphestTransactionEditor();
107121
$editor->applyTransactions($task, $transactions);
108122

‎src/applications/conduit/method/maniphest/createtask/__init__.php

+3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
phutil_require_module('phabricator', 'applications/maniphest/storage/transaction');
1616
phutil_require_module('phabricator', 'applications/metamta/contentsource/source');
1717
phutil_require_module('phabricator', 'applications/phid/constants');
18+
phutil_require_module('phabricator', 'infrastructure/events/constant/type');
19+
phutil_require_module('phabricator', 'infrastructure/events/engine');
20+
phutil_require_module('phabricator', 'infrastructure/events/event');
1821

1922

2023
phutil_require_source('ConduitAPI_maniphest_createtask_Method.php');

‎src/applications/maniphest/controller/taskedit/ManiphestTaskEditController.php

+15
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,21 @@ public function processRequest() {
201201
}
202202

203203
if ($transactions) {
204+
205+
$event = new PhabricatorEvent(
206+
PhabricatorEventType::TYPE_MANIPHEST_WILLEDITTASK,
207+
array(
208+
'task' => $task,
209+
'new' => !$task->getID(),
210+
'transactions' => $transactions,
211+
));
212+
$event->setUser($user);
213+
$event->setAphrontRequest($request);
214+
PhabricatorEventEngine::dispatchEvent($event);
215+
216+
$task = $event->getValue('task');
217+
$transactions = $event->getValue('transactions');
218+
204219
$editor = new ManiphestTransactionEditor();
205220
$editor->applyTransactions($task, $transactions);
206221
}

‎src/applications/maniphest/controller/taskedit/__init__.php

+3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
phutil_require_module('phabricator', 'applications/phid/handle/data');
2323
phutil_require_module('phabricator', 'infrastructure/celerity/api');
2424
phutil_require_module('phabricator', 'infrastructure/env');
25+
phutil_require_module('phabricator', 'infrastructure/events/constant/type');
26+
phutil_require_module('phabricator', 'infrastructure/events/engine');
27+
phutil_require_module('phabricator', 'infrastructure/events/event');
2528
phutil_require_module('phabricator', 'infrastructure/javelin/api');
2629
phutil_require_module('phabricator', 'infrastructure/javelin/markup');
2730
phutil_require_module('phabricator', 'view/form/base');

‎src/applications/maniphest/controller/transactionsave/ManiphestTransactionSaveController.php

+14
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,20 @@ public function processRequest() {
229229
$transaction->setContentSource($content_source);
230230
}
231231

232+
$event = new PhabricatorEvent(
233+
PhabricatorEventType::TYPE_MANIPHEST_WILLEDITTASK,
234+
array(
235+
'task' => $task,
236+
'new' => false,
237+
'transactions' => $transactions,
238+
));
239+
$event->setUser($user);
240+
$event->setAphrontRequest($request);
241+
PhabricatorEventEngine::dispatchEvent($event);
242+
243+
$task = $event->getValue('task');
244+
$transactions = $event->getValue('transactions');
245+
232246
$editor = new ManiphestTransactionEditor();
233247
$editor->applyTransactions($task, $transactions);
234248

‎src/applications/maniphest/controller/transactionsave/__init__.php

+3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
phutil_require_module('phabricator', 'applications/markup/engine');
2020
phutil_require_module('phabricator', 'applications/metamta/contentsource/source');
2121
phutil_require_module('phabricator', 'applications/phid/constants');
22+
phutil_require_module('phabricator', 'infrastructure/events/constant/type');
23+
phutil_require_module('phabricator', 'infrastructure/events/engine');
24+
phutil_require_module('phabricator', 'infrastructure/events/event');
2225

2326
phutil_require_module('phutil', 'utils');
2427

‎src/applications/maniphest/replyhandler/ManiphestReplyHandler.php

+15
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,21 @@ public function receiveEmail(PhabricatorMetaMTAReceivedMail $mail) {
150150
$xactions[] = $file_xaction;
151151
}
152152

153+
$event = new PhabricatorEvent(
154+
PhabricatorEventType::TYPE_MANIPHEST_WILLEDITTASK,
155+
array(
156+
'task' => $task,
157+
'mail' => $mail,
158+
'new' => $is_new_task,
159+
'transactions' => $xactions,
160+
));
161+
$event->setUser($user);
162+
PhabricatorEventEngine::dispatchEvent($event);
163+
164+
$task = $event->getValue('task');
165+
$xactions = $event->getValue('transactions');
166+
167+
153168
$editor = new ManiphestTransactionEditor();
154169
$editor->setParentMessageID($mail->getMessageID());
155170
$editor->applyTransactions($task, $xactions);

‎src/applications/maniphest/replyhandler/__init__.php

+3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
phutil_require_module('phabricator', 'applications/metamta/replyhandler/base');
1515
phutil_require_module('phabricator', 'applications/phid/constants');
1616
phutil_require_module('phabricator', 'infrastructure/env');
17+
phutil_require_module('phabricator', 'infrastructure/events/constant/type');
18+
phutil_require_module('phabricator', 'infrastructure/events/engine');
19+
phutil_require_module('phabricator', 'infrastructure/events/event');
1720

1821
phutil_require_module('phutil', 'utils');
1922

‎src/docs/userguide/events.diviner

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
@title Events User Guide: Installing Event Listeners
2+
@group userguide
3+
4+
Using Phabricator event listeners to customize behavior.
5+
6+
= Overview =
7+
8+
Phabricator allows you to install custom runtime event listeners which can react
9+
to certain things happening (like a Maniphest Task being edited) and run custom
10+
code to perform logging, synchronize with other systems, or modify workflows.
11+
12+
NOTE: This feature is new and experimental, so few events are available and
13+
things might not be completely stable.
14+
15+
= Available Events =
16+
17+
== PhabricatorEventType::TYPE_MANIPHEST_WILLEDITTASK ==
18+
19+
This event is dispatched before a task is edited, and allows you to respond to
20+
or alter the edit. Data available on this event:
21+
22+
- ##task## The {@class:ManiphestTask} being edited.
23+
- ##transactions## The list of edits (objects of class
24+
@{class:ManiphestTransaction}) being applied.
25+
- ##new## A boolean indicating if this task is being created.
26+
- ##mail## If this edit originates from email, the
27+
@{class:PhabricatorMetaMTAReceivedMail} object.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
/*
4+
* Copyright 2011 Facebook, Inc.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
abstract class PhabricatorEventConstants {
20+
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
/**
3+
* This file is automatically generated. Lint this module to rebuild it.
4+
* @generated
5+
*/
6+
7+
8+
9+
10+
phutil_require_source('PhabricatorEventConstants.php');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
/*
4+
* Copyright 2011 Facebook, Inc.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
final class PhabricatorEventType extends PhabricatorEventConstants {
20+
21+
const TYPE_MANIPHEST_WILLEDITTASK = 'maniphest.willEditTask';
22+
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
/**
3+
* This file is automatically generated. Lint this module to rebuild it.
4+
* @generated
5+
*/
6+
7+
8+
9+
phutil_require_module('phabricator', 'infrastructure/events/constant/base');
10+
11+
12+
phutil_require_source('PhabricatorEventType.php');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
3+
/*
4+
* Copyright 2011 Facebook, Inc.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
class PhabricatorEventEngine {
20+
21+
private static $instance;
22+
23+
private $listeners = array();
24+
25+
private function __construct() {
26+
// <empty>
27+
}
28+
29+
public static function initialize() {
30+
self::$instance = new PhabricatorEventEngine();
31+
32+
// Instantiate and register custom event listeners so they can react to
33+
// events.
34+
$listeners = PhabricatorEnv::getEnvConfig('events.listeners');
35+
foreach ($listeners as $listener) {
36+
id(new $listener())->register();
37+
}
38+
}
39+
40+
public static function getInstance() {
41+
if (!self::$instance) {
42+
throw new Exception("Event engine has not been initialized!");
43+
}
44+
return self::$instance;
45+
}
46+
47+
public function addListener(
48+
PhabricatorEventListener $listener,
49+
$type) {
50+
$this->listeners[$type][] = $listener;
51+
return $this;
52+
}
53+
54+
public static function dispatchEvent(PhabricatorEvent $event) {
55+
$instance = self::getInstance();
56+
57+
$listeners = idx($instance->listeners, $event->getType(), array());
58+
foreach ($listeners as $listener) {
59+
if ($event->isStopped()) {
60+
// Do this first so if someone tries to dispatch a stopped event it
61+
// doesn't go anywhere. Silly but less surprising.
62+
break;
63+
}
64+
$listener->handleEvent($event);
65+
}
66+
}
67+
68+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
/**
3+
* This file is automatically generated. Lint this module to rebuild it.
4+
* @generated
5+
*/
6+
7+
8+
9+
phutil_require_module('phabricator', 'infrastructure/env');
10+
11+
phutil_require_module('phutil', 'utils');
12+
13+
14+
phutil_require_source('PhabricatorEventEngine.php');

0 commit comments

Comments
 (0)
Failed to load comments.