mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-15 01:50:55 +01:00
(stable) Promote 2018 Week 6
This commit is contained in:
commit
88faa980c6
178 changed files with 6904 additions and 791 deletions
1
bin/conduit
Symbolic link
1
bin/conduit
Symbolic link
|
@ -0,0 +1 @@
|
|||
../scripts/setup/manage_conduit.php
|
1
bin/webhook
Symbolic link
1
bin/webhook
Symbolic link
|
@ -0,0 +1 @@
|
|||
../scripts/setup/manage_webhook.php
|
|
@ -9,11 +9,11 @@ return array(
|
|||
'names' => array(
|
||||
'conpherence.pkg.css' => 'e68cf1fa',
|
||||
'conpherence.pkg.js' => '15191c65',
|
||||
'core.pkg.css' => '51debec3',
|
||||
'core.pkg.js' => '4c79d74f',
|
||||
'core.pkg.css' => 'e4f098a5',
|
||||
'core.pkg.js' => '3ac6e174',
|
||||
'darkconsole.pkg.js' => '1f9a31bc',
|
||||
'differential.pkg.css' => '45951e9e',
|
||||
'differential.pkg.js' => '19ee9979',
|
||||
'differential.pkg.css' => '113e692c',
|
||||
'differential.pkg.js' => '5d53d5ce',
|
||||
'diffusion.pkg.css' => 'a2d17c7d',
|
||||
'diffusion.pkg.js' => '6134c5a1',
|
||||
'favicon.ico' => '30672e08',
|
||||
|
@ -31,7 +31,7 @@ return array(
|
|||
'rsrc/css/aphront/multi-column.css' => '84cc6640',
|
||||
'rsrc/css/aphront/notification.css' => '457861ec',
|
||||
'rsrc/css/aphront/panel-view.css' => '8427b78d',
|
||||
'rsrc/css/aphront/phabricator-nav-view.css' => 'faf6a6fc',
|
||||
'rsrc/css/aphront/phabricator-nav-view.css' => '028126f6',
|
||||
'rsrc/css/aphront/table-view.css' => '8c9bbafe',
|
||||
'rsrc/css/aphront/tokenizer.css' => '15d5ff71',
|
||||
'rsrc/css/aphront/tooltip.css' => '173b9431',
|
||||
|
@ -121,7 +121,7 @@ return array(
|
|||
'rsrc/css/font/font-awesome.css' => 'e838e088',
|
||||
'rsrc/css/font/font-lato.css' => 'c7ccd872',
|
||||
'rsrc/css/font/phui-font-icon-base.css' => '870a7360',
|
||||
'rsrc/css/layout/phabricator-filetree-view.css' => 'fccf9f82',
|
||||
'rsrc/css/layout/phabricator-filetree-view.css' => 'b912ad97',
|
||||
'rsrc/css/layout/phabricator-source-code-view.css' => 'aea41829',
|
||||
'rsrc/css/phui/button/phui-button-bar.css' => 'f1ff5494',
|
||||
'rsrc/css/phui/button/phui-button-simple.css' => '8e1baf68',
|
||||
|
@ -136,7 +136,7 @@ return array(
|
|||
'rsrc/css/phui/object-item/phui-oi-flush-ui.css' => '9d9685d6',
|
||||
'rsrc/css/phui/object-item/phui-oi-list-view.css' => '6ae18df0',
|
||||
'rsrc/css/phui/object-item/phui-oi-simple-ui.css' => 'a8beebea',
|
||||
'rsrc/css/phui/phui-action-list.css' => 'f7f61a34',
|
||||
'rsrc/css/phui/phui-action-list.css' => '0bcd9a45',
|
||||
'rsrc/css/phui/phui-action-panel.css' => 'b4798122',
|
||||
'rsrc/css/phui/phui-badge.css' => '22c0cf4f',
|
||||
'rsrc/css/phui/phui-basic-nav-view.css' => '98c11ab3',
|
||||
|
@ -395,8 +395,8 @@ return array(
|
|||
'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => '408bf173',
|
||||
'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375',
|
||||
'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63',
|
||||
'rsrc/js/application/diff/DiffChangeset.js' => '99abf4cd',
|
||||
'rsrc/js/application/diff/DiffChangesetList.js' => '3b77efdd',
|
||||
'rsrc/js/application/diff/DiffChangeset.js' => 'b49b59d6',
|
||||
'rsrc/js/application/diff/DiffChangesetList.js' => '1f2e5265',
|
||||
'rsrc/js/application/diff/DiffInline.js' => 'e83d28f3',
|
||||
'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832',
|
||||
'rsrc/js/application/differential/behavior-comment-preview.js' => '51c5ad07',
|
||||
|
@ -498,7 +498,7 @@ return array(
|
|||
'rsrc/js/core/behavior-more.js' => 'a80d0378',
|
||||
'rsrc/js/core/behavior-object-selector.js' => '77c1f0b0',
|
||||
'rsrc/js/core/behavior-oncopy.js' => '2926fff2',
|
||||
'rsrc/js/core/behavior-phabricator-nav.js' => '947753e0',
|
||||
'rsrc/js/core/behavior-phabricator-nav.js' => '81144dfa',
|
||||
'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => 'acd29eee',
|
||||
'rsrc/js/core/behavior-read-only-warning.js' => 'ba158207',
|
||||
'rsrc/js/core/behavior-refresh-csrf.js' => 'ab2f381b',
|
||||
|
@ -657,7 +657,7 @@ return array(
|
|||
'javelin-behavior-phabricator-keyboard-pager' => 'a8da01f0',
|
||||
'javelin-behavior-phabricator-keyboard-shortcuts' => '01fca1f0',
|
||||
'javelin-behavior-phabricator-line-linker' => '1499a8cb',
|
||||
'javelin-behavior-phabricator-nav' => '947753e0',
|
||||
'javelin-behavior-phabricator-nav' => '81144dfa',
|
||||
'javelin-behavior-phabricator-notification-example' => '8ce821c5',
|
||||
'javelin-behavior-phabricator-object-selector' => '77c1f0b0',
|
||||
'javelin-behavior-phabricator-oncopy' => '2926fff2',
|
||||
|
@ -766,7 +766,7 @@ return array(
|
|||
'path-typeahead' => 'f7fc67ec',
|
||||
'people-picture-menu-item-css' => 'a06f7f34',
|
||||
'people-profile-css' => '4df76faf',
|
||||
'phabricator-action-list-view-css' => 'f7f61a34',
|
||||
'phabricator-action-list-view-css' => '0bcd9a45',
|
||||
'phabricator-busy' => '59a7976a',
|
||||
'phabricator-chatlog-css' => 'd295b020',
|
||||
'phabricator-content-source-view-css' => '4b8b05d4',
|
||||
|
@ -775,8 +775,8 @@ return array(
|
|||
'phabricator-darklog' => 'c8e1ffe3',
|
||||
'phabricator-darkmessage' => 'c48cccdd',
|
||||
'phabricator-dashboard-css' => 'fe5b1869',
|
||||
'phabricator-diff-changeset' => '99abf4cd',
|
||||
'phabricator-diff-changeset-list' => '3b77efdd',
|
||||
'phabricator-diff-changeset' => 'b49b59d6',
|
||||
'phabricator-diff-changeset-list' => '1f2e5265',
|
||||
'phabricator-diff-inline' => 'e83d28f3',
|
||||
'phabricator-drag-and-drop-file-upload' => '58dea2fa',
|
||||
'phabricator-draggable-list' => 'bea6e7f4',
|
||||
|
@ -784,12 +784,12 @@ return array(
|
|||
'phabricator-favicon' => '1fe2510c',
|
||||
'phabricator-feed-css' => 'ecd4ec57',
|
||||
'phabricator-file-upload' => '680ea2c8',
|
||||
'phabricator-filetree-view-css' => 'fccf9f82',
|
||||
'phabricator-filetree-view-css' => 'b912ad97',
|
||||
'phabricator-flag-css' => 'bba8f811',
|
||||
'phabricator-keyboard-shortcut' => '1ae869f2',
|
||||
'phabricator-keyboard-shortcut-manager' => 'c19dd9b9',
|
||||
'phabricator-main-menu-view' => '1802a242',
|
||||
'phabricator-nav-view-css' => 'faf6a6fc',
|
||||
'phabricator-nav-view-css' => '028126f6',
|
||||
'phabricator-notification' => '008faf9c',
|
||||
'phabricator-notification-css' => '457861ec',
|
||||
'phabricator-notification-menu-css' => '10685bd4',
|
||||
|
@ -1044,6 +1044,10 @@ return array(
|
|||
'javelin-uri',
|
||||
'javelin-routable',
|
||||
),
|
||||
'1f2e5265' => array(
|
||||
'javelin-install',
|
||||
'phuix-button-view',
|
||||
),
|
||||
'1f6794f6' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
|
@ -1143,10 +1147,6 @@ return array(
|
|||
'javelin-dom',
|
||||
'javelin-magical-init',
|
||||
),
|
||||
'3b77efdd' => array(
|
||||
'javelin-install',
|
||||
'phuix-button-view',
|
||||
),
|
||||
'3cb0b2fc' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
|
@ -1561,6 +1561,16 @@ return array(
|
|||
'7f243deb' => array(
|
||||
'javelin-install',
|
||||
),
|
||||
'81144dfa' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-behavior-device',
|
||||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
'javelin-magical-init',
|
||||
'javelin-vector',
|
||||
'javelin-request',
|
||||
'javelin-util',
|
||||
),
|
||||
'834a1173' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-scrollbar',
|
||||
|
@ -1648,16 +1658,6 @@ return array(
|
|||
'javelin-workflow',
|
||||
'javelin-dom',
|
||||
),
|
||||
'947753e0' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-behavior-device',
|
||||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
'javelin-magical-init',
|
||||
'javelin-vector',
|
||||
'javelin-request',
|
||||
'javelin-util',
|
||||
),
|
||||
'949c0fe5' => array(
|
||||
'javelin-install',
|
||||
),
|
||||
|
@ -1678,17 +1678,6 @@ return array(
|
|||
'javelin-mask',
|
||||
'phabricator-drag-and-drop-file-upload',
|
||||
),
|
||||
'99abf4cd' => array(
|
||||
'javelin-dom',
|
||||
'javelin-util',
|
||||
'javelin-stratcom',
|
||||
'javelin-install',
|
||||
'javelin-workflow',
|
||||
'javelin-router',
|
||||
'javelin-behavior-device',
|
||||
'javelin-vector',
|
||||
'phabricator-diff-inline',
|
||||
),
|
||||
'9a6dd75c' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
|
@ -1837,6 +1826,17 @@ return array(
|
|||
'b3e7d692' => array(
|
||||
'javelin-install',
|
||||
),
|
||||
'b49b59d6' => array(
|
||||
'javelin-dom',
|
||||
'javelin-util',
|
||||
'javelin-stratcom',
|
||||
'javelin-install',
|
||||
'javelin-workflow',
|
||||
'javelin-router',
|
||||
'javelin-behavior-device',
|
||||
'javelin-vector',
|
||||
'phabricator-diff-inline',
|
||||
),
|
||||
'b59e1e96' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
|
|
|
@ -1622,5 +1622,9 @@
|
|||
"zipper_mouth": "\ud83e\udd10",
|
||||
"zzz": "\ud83d\udca4",
|
||||
"100": "\ud83d\udcaf",
|
||||
"1234": "\ud83d\udd22"
|
||||
"1234": "\ud83d\udd22",
|
||||
|
||||
"party": "\ud83c\udf89",
|
||||
"celebration": "\ud83c\udf89",
|
||||
"confetti": "\ud83c\udf89"
|
||||
}
|
||||
|
|
2
resources/sql/autopatches/20180207.mail.01.task.sql
Normal file
2
resources/sql/autopatches/20180207.mail.01.task.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_maniphest.maniphest_task
|
||||
DROP originalTitle;
|
2
resources/sql/autopatches/20180207.mail.02.revision.sql
Normal file
2
resources/sql/autopatches/20180207.mail.02.revision.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_differential.differential_revision
|
||||
DROP originalTitle;
|
2
resources/sql/autopatches/20180207.mail.03.mock.sql
Normal file
2
resources/sql/autopatches/20180207.mail.03.mock.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_pholio.pholio_mock
|
||||
DROP originalName;
|
|
@ -0,0 +1,5 @@
|
|||
ALTER TABLE {$NAMESPACE}_maniphest.maniphest_task
|
||||
ADD closedEpoch INT UNSIGNED;
|
||||
|
||||
ALTER TABLE {$NAMESPACE}_maniphest.maniphest_task
|
||||
ADD closerPHID VARBINARY(64);
|
65
resources/sql/autopatches/20180208.maniphest.02.populate.php
Normal file
65
resources/sql/autopatches/20180208.maniphest.02.populate.php
Normal file
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
$table = new ManiphestTask();
|
||||
$conn = $table->establishConnection('w');
|
||||
$viewer = PhabricatorUser::getOmnipotentUser();
|
||||
|
||||
foreach (new LiskMigrationIterator($table) as $task) {
|
||||
if ($task->getClosedEpoch()) {
|
||||
// Task already has a closed date.
|
||||
continue;
|
||||
}
|
||||
|
||||
$status = $task->getStatus();
|
||||
if (!ManiphestTaskStatus::isClosedStatus($status)) {
|
||||
// Task isn't closed.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Look through the transactions from newest to oldest until we find one
|
||||
// where the task was closed. A merge also counts as a close, even though
|
||||
// it doesn't currently produce a separate transaction.
|
||||
|
||||
$type_merge = ManiphestTaskStatusTransaction::TRANSACTIONTYPE;
|
||||
$type_status = ManiphestTaskMergedIntoTransaction::TRANSACTIONTYPE;
|
||||
|
||||
$xactions = id(new ManiphestTransactionQuery())
|
||||
->setViewer($viewer)
|
||||
->withObjectPHIDs(array($task->getPHID()))
|
||||
->withTransactionTypes(
|
||||
array(
|
||||
$type_merge,
|
||||
$type_status,
|
||||
))
|
||||
->execute();
|
||||
foreach ($xactions as $xaction) {
|
||||
$old = $xaction->getOldValue();
|
||||
$new = $xaction->getNewValue();
|
||||
|
||||
$type = $xaction->getTransactionType();
|
||||
|
||||
// If this is a status change, but is not a close, don't use it.
|
||||
// (We always use merges, even though it's possible to merge a task which
|
||||
// was previously closed: we can't tell when this happens very easily.)
|
||||
if ($type === $type_status) {
|
||||
if (!ManiphestTaskStatus::isClosedStatus($new)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($old && ManiphestTaskStatus::isClosedStatus($old)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
queryfx(
|
||||
$conn,
|
||||
'UPDATE %T SET closedEpoch = %d, closerPHID = %ns
|
||||
WHERE id = %d',
|
||||
$table->getTableName(),
|
||||
$xaction->getDateCreated(),
|
||||
$xaction->getAuthorPHID(),
|
||||
$task->getID());
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
12
resources/sql/autopatches/20180209.hook.01.hook.sql
Normal file
12
resources/sql/autopatches/20180209.hook.01.hook.sql
Normal file
|
@ -0,0 +1,12 @@
|
|||
CREATE TABLE {$NAMESPACE}_herald.herald_webhook (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
phid VARBINARY(64) NOT NULL,
|
||||
name VARCHAR(128) NOT NULL COLLATE {$COLLATE_TEXT},
|
||||
webhookURI VARCHAR(255) NOT NULL COLLATE {$COLLATE_TEXT},
|
||||
viewPolicy VARBINARY(64) NOT NULL,
|
||||
editPolicy VARBINARY(64) NOT NULL,
|
||||
status VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT},
|
||||
hmacKey VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT},
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
dateModified INT UNSIGNED NOT NULL
|
||||
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
19
resources/sql/autopatches/20180209.hook.02.hookxaction.sql
Normal file
19
resources/sql/autopatches/20180209.hook.02.hookxaction.sql
Normal file
|
@ -0,0 +1,19 @@
|
|||
CREATE TABLE {$NAMESPACE}_herald.herald_webhooktransaction (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
phid VARBINARY(64) NOT NULL,
|
||||
authorPHID VARBINARY(64) NOT NULL,
|
||||
objectPHID VARBINARY(64) NOT NULL,
|
||||
viewPolicy VARBINARY(64) NOT NULL,
|
||||
editPolicy VARBINARY(64) NOT NULL,
|
||||
commentPHID VARBINARY(64) DEFAULT NULL,
|
||||
commentVersion INT UNSIGNED NOT NULL,
|
||||
transactionType VARCHAR(32) COLLATE {$COLLATE_TEXT} NOT NULL,
|
||||
oldValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
|
||||
newValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
|
||||
contentSource LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
|
||||
metadata LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
dateModified INT UNSIGNED NOT NULL,
|
||||
UNIQUE KEY `key_phid` (`phid`),
|
||||
KEY `key_object` (`objectPHID`)
|
||||
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
12
resources/sql/autopatches/20180209.hook.03.hookrequest.sql
Normal file
12
resources/sql/autopatches/20180209.hook.03.hookrequest.sql
Normal file
|
@ -0,0 +1,12 @@
|
|||
CREATE TABLE {$NAMESPACE}_herald.herald_webhookrequest (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
phid VARBINARY(64) NOT NULL,
|
||||
webhookPHID VARBINARY(64) NOT NULL,
|
||||
objectPHID VARBINARY(64) NOT NULL,
|
||||
status VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT},
|
||||
properties LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT},
|
||||
lastRequestResult VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT},
|
||||
lastRequestEpoch INT UNSIGNED NOT NULL,
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
dateModified INT UNSIGNED NOT NULL
|
||||
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
21
scripts/setup/manage_conduit.php
Executable file
21
scripts/setup/manage_conduit.php
Executable file
|
@ -0,0 +1,21 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
$root = dirname(dirname(dirname(__FILE__)));
|
||||
require_once $root.'/scripts/init/init-script.php';
|
||||
|
||||
$args = new PhutilArgumentParser($argv);
|
||||
$args->setTagline(pht('manage Conduit'));
|
||||
$args->setSynopsis(<<<EOSYNOPSIS
|
||||
**conduit** __command__ [__options__]
|
||||
Manage Conduit.
|
||||
|
||||
EOSYNOPSIS
|
||||
);
|
||||
$args->parseStandardArguments();
|
||||
|
||||
$workflows = id(new PhutilClassMapQuery())
|
||||
->setAncestorClass('PhabricatorConduitManagementWorkflow')
|
||||
->execute();
|
||||
$workflows[] = new PhutilHelpArgumentWorkflow();
|
||||
$args->parseWorkflows($workflows);
|
21
scripts/setup/manage_webhook.php
Executable file
21
scripts/setup/manage_webhook.php
Executable file
|
@ -0,0 +1,21 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
$root = dirname(dirname(dirname(__FILE__)));
|
||||
require_once $root.'/scripts/init/init-script.php';
|
||||
|
||||
$args = new PhutilArgumentParser($argv);
|
||||
$args->setTagline(pht('manage webhooks'));
|
||||
$args->setSynopsis(<<<EOSYNOPSIS
|
||||
**webhook** __command__ [__options__]
|
||||
Manage webhooks.
|
||||
|
||||
EOSYNOPSIS
|
||||
);
|
||||
$args->parseStandardArguments();
|
||||
|
||||
$workflows = id(new PhutilClassMapQuery())
|
||||
->setAncestorClass('HeraldWebhookManagementWorkflow')
|
||||
->execute();
|
||||
$workflows[] = new PhutilHelpArgumentWorkflow();
|
||||
$args->parseWorkflows($workflows);
|
|
@ -487,6 +487,7 @@ phutil_register_library_map(array(
|
|||
'DifferentialLintField' => 'applications/differential/customfield/DifferentialLintField.php',
|
||||
'DifferentialLintStatus' => 'applications/differential/constants/DifferentialLintStatus.php',
|
||||
'DifferentialLocalCommitsView' => 'applications/differential/view/DifferentialLocalCommitsView.php',
|
||||
'DifferentialMailEngineExtension' => 'applications/differential/engineextension/DifferentialMailEngineExtension.php',
|
||||
'DifferentialMailView' => 'applications/differential/mail/DifferentialMailView.php',
|
||||
'DifferentialManiphestTasksField' => 'applications/differential/customfield/DifferentialManiphestTasksField.php',
|
||||
'DifferentialModernHunk' => 'applications/differential/storage/DifferentialModernHunk.php',
|
||||
|
@ -1345,6 +1346,7 @@ phutil_register_library_map(array(
|
|||
'HarbormasterWaitForPreviousBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterWaitForPreviousBuildStepImplementation.php',
|
||||
'HarbormasterWorker' => 'applications/harbormaster/worker/HarbormasterWorker.php',
|
||||
'HarbormasterWorkingCopyArtifact' => 'applications/harbormaster/artifact/HarbormasterWorkingCopyArtifact.php',
|
||||
'HeraldActingUserField' => 'applications/herald/field/HeraldActingUserField.php',
|
||||
'HeraldAction' => 'applications/herald/action/HeraldAction.php',
|
||||
'HeraldActionGroup' => 'applications/herald/action/HeraldActionGroup.php',
|
||||
'HeraldActionRecord' => 'applications/herald/storage/HeraldActionRecord.php',
|
||||
|
@ -1355,6 +1357,7 @@ phutil_register_library_map(array(
|
|||
'HeraldApplyTranscript' => 'applications/herald/storage/transcript/HeraldApplyTranscript.php',
|
||||
'HeraldBasicFieldGroup' => 'applications/herald/field/HeraldBasicFieldGroup.php',
|
||||
'HeraldBuildableState' => 'applications/herald/state/HeraldBuildableState.php',
|
||||
'HeraldCallWebhookAction' => 'applications/herald/action/HeraldCallWebhookAction.php',
|
||||
'HeraldCommentAction' => 'applications/herald/action/HeraldCommentAction.php',
|
||||
'HeraldCommitAdapter' => 'applications/diffusion/herald/HeraldCommitAdapter.php',
|
||||
'HeraldCondition' => 'applications/herald/storage/HeraldCondition.php',
|
||||
|
@ -1362,6 +1365,7 @@ phutil_register_library_map(array(
|
|||
'HeraldContentSourceField' => 'applications/herald/field/HeraldContentSourceField.php',
|
||||
'HeraldController' => 'applications/herald/controller/HeraldController.php',
|
||||
'HeraldCoreStateReasons' => 'applications/herald/state/HeraldCoreStateReasons.php',
|
||||
'HeraldCreateWebhooksCapability' => 'applications/herald/capability/HeraldCreateWebhooksCapability.php',
|
||||
'HeraldDAO' => 'applications/herald/storage/HeraldDAO.php',
|
||||
'HeraldDeprecatedFieldGroup' => 'applications/herald/field/HeraldDeprecatedFieldGroup.php',
|
||||
'HeraldDifferentialAdapter' => 'applications/differential/herald/HeraldDifferentialAdapter.php',
|
||||
|
@ -1438,6 +1442,33 @@ phutil_register_library_map(array(
|
|||
'HeraldTranscriptSearchEngine' => 'applications/herald/query/HeraldTranscriptSearchEngine.php',
|
||||
'HeraldTranscriptTestCase' => 'applications/herald/storage/__tests__/HeraldTranscriptTestCase.php',
|
||||
'HeraldUtilityActionGroup' => 'applications/herald/action/HeraldUtilityActionGroup.php',
|
||||
'HeraldWebhook' => 'applications/herald/storage/HeraldWebhook.php',
|
||||
'HeraldWebhookCallManagementWorkflow' => 'applications/herald/management/HeraldWebhookCallManagementWorkflow.php',
|
||||
'HeraldWebhookController' => 'applications/herald/controller/HeraldWebhookController.php',
|
||||
'HeraldWebhookDatasource' => 'applications/herald/typeahead/HeraldWebhookDatasource.php',
|
||||
'HeraldWebhookEditController' => 'applications/herald/controller/HeraldWebhookEditController.php',
|
||||
'HeraldWebhookEditEngine' => 'applications/herald/editor/HeraldWebhookEditEngine.php',
|
||||
'HeraldWebhookEditor' => 'applications/herald/editor/HeraldWebhookEditor.php',
|
||||
'HeraldWebhookKeyController' => 'applications/herald/controller/HeraldWebhookKeyController.php',
|
||||
'HeraldWebhookListController' => 'applications/herald/controller/HeraldWebhookListController.php',
|
||||
'HeraldWebhookManagementWorkflow' => 'applications/herald/management/HeraldWebhookManagementWorkflow.php',
|
||||
'HeraldWebhookNameTransaction' => 'applications/herald/xaction/HeraldWebhookNameTransaction.php',
|
||||
'HeraldWebhookPHIDType' => 'applications/herald/phid/HeraldWebhookPHIDType.php',
|
||||
'HeraldWebhookQuery' => 'applications/herald/query/HeraldWebhookQuery.php',
|
||||
'HeraldWebhookRequest' => 'applications/herald/storage/HeraldWebhookRequest.php',
|
||||
'HeraldWebhookRequestGarbageCollector' => 'applications/herald/garbagecollector/HeraldWebhookRequestGarbageCollector.php',
|
||||
'HeraldWebhookRequestListView' => 'applications/herald/view/HeraldWebhookRequestListView.php',
|
||||
'HeraldWebhookRequestPHIDType' => 'applications/herald/phid/HeraldWebhookRequestPHIDType.php',
|
||||
'HeraldWebhookRequestQuery' => 'applications/herald/query/HeraldWebhookRequestQuery.php',
|
||||
'HeraldWebhookSearchEngine' => 'applications/herald/query/HeraldWebhookSearchEngine.php',
|
||||
'HeraldWebhookStatusTransaction' => 'applications/herald/xaction/HeraldWebhookStatusTransaction.php',
|
||||
'HeraldWebhookTestController' => 'applications/herald/controller/HeraldWebhookTestController.php',
|
||||
'HeraldWebhookTransaction' => 'applications/herald/storage/HeraldWebhookTransaction.php',
|
||||
'HeraldWebhookTransactionQuery' => 'applications/herald/query/HeraldWebhookTransactionQuery.php',
|
||||
'HeraldWebhookTransactionType' => 'applications/herald/xaction/HeraldWebhookTransactionType.php',
|
||||
'HeraldWebhookURITransaction' => 'applications/herald/xaction/HeraldWebhookURITransaction.php',
|
||||
'HeraldWebhookViewController' => 'applications/herald/controller/HeraldWebhookViewController.php',
|
||||
'HeraldWebhookWorker' => 'applications/herald/worker/HeraldWebhookWorker.php',
|
||||
'Javelin' => 'infrastructure/javelin/Javelin.php',
|
||||
'LegalpadController' => 'applications/legalpad/controller/LegalpadController.php',
|
||||
'LegalpadCreateDocumentsCapability' => 'applications/legalpad/capability/LegalpadCreateDocumentsCapability.php',
|
||||
|
@ -1528,6 +1559,7 @@ phutil_register_library_map(array(
|
|||
'ManiphestGetTaskTransactionsConduitAPIMethod' => 'applications/maniphest/conduit/ManiphestGetTaskTransactionsConduitAPIMethod.php',
|
||||
'ManiphestHovercardEngineExtension' => 'applications/maniphest/engineextension/ManiphestHovercardEngineExtension.php',
|
||||
'ManiphestInfoConduitAPIMethod' => 'applications/maniphest/conduit/ManiphestInfoConduitAPIMethod.php',
|
||||
'ManiphestMailEngineExtension' => 'applications/maniphest/engineextension/ManiphestMailEngineExtension.php',
|
||||
'ManiphestNameIndex' => 'applications/maniphest/storage/ManiphestNameIndex.php',
|
||||
'ManiphestPointsConfigType' => 'applications/maniphest/config/ManiphestPointsConfigType.php',
|
||||
'ManiphestPrioritiesConfigType' => 'applications/maniphest/config/ManiphestPrioritiesConfigType.php',
|
||||
|
@ -1958,6 +1990,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorApplicationEditHTTPParameterHelpView' => 'applications/transactions/view/PhabricatorApplicationEditHTTPParameterHelpView.php',
|
||||
'PhabricatorApplicationEditor' => 'applications/meta/editor/PhabricatorApplicationEditor.php',
|
||||
'PhabricatorApplicationEmailCommandsController' => 'applications/meta/controller/PhabricatorApplicationEmailCommandsController.php',
|
||||
'PhabricatorApplicationObjectMailEngineExtension' => 'applications/transactions/engineextension/PhabricatorApplicationObjectMailEngineExtension.php',
|
||||
'PhabricatorApplicationPanelController' => 'applications/meta/controller/PhabricatorApplicationPanelController.php',
|
||||
'PhabricatorApplicationPolicyChangeTransaction' => 'applications/meta/xactions/PhabricatorApplicationPolicyChangeTransaction.php',
|
||||
'PhabricatorApplicationProfileMenuItem' => 'applications/search/menuitem/PhabricatorApplicationProfileMenuItem.php',
|
||||
|
@ -2219,6 +2252,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorBoardResponseEngine' => 'applications/project/engine/PhabricatorBoardResponseEngine.php',
|
||||
'PhabricatorBoolConfigType' => 'applications/config/type/PhabricatorBoolConfigType.php',
|
||||
'PhabricatorBoolEditField' => 'applications/transactions/editfield/PhabricatorBoolEditField.php',
|
||||
'PhabricatorBoolMailStamp' => 'applications/metamta/stamp/PhabricatorBoolMailStamp.php',
|
||||
'PhabricatorBritishEnglishTranslation' => 'infrastructure/internationalization/translation/PhabricatorBritishEnglishTranslation.php',
|
||||
'PhabricatorBuiltinDraftEngine' => 'applications/transactions/draft/PhabricatorBuiltinDraftEngine.php',
|
||||
'PhabricatorBuiltinFileCachePurger' => 'applications/cache/purger/PhabricatorBuiltinFileCachePurger.php',
|
||||
|
@ -2407,6 +2441,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorClusterExceptionHandler' => 'infrastructure/cluster/exception/PhabricatorClusterExceptionHandler.php',
|
||||
'PhabricatorClusterImpossibleWriteException' => 'infrastructure/cluster/exception/PhabricatorClusterImpossibleWriteException.php',
|
||||
'PhabricatorClusterImproperWriteException' => 'infrastructure/cluster/exception/PhabricatorClusterImproperWriteException.php',
|
||||
'PhabricatorClusterMailersConfigType' => 'infrastructure/cluster/config/PhabricatorClusterMailersConfigType.php',
|
||||
'PhabricatorClusterNoHostForRoleException' => 'infrastructure/cluster/exception/PhabricatorClusterNoHostForRoleException.php',
|
||||
'PhabricatorClusterSearchConfigType' => 'infrastructure/cluster/config/PhabricatorClusterSearchConfigType.php',
|
||||
'PhabricatorClusterServiceHealthRecord' => 'infrastructure/cluster/PhabricatorClusterServiceHealthRecord.php',
|
||||
|
@ -2425,6 +2460,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorCommonPasswords' => 'applications/auth/constants/PhabricatorCommonPasswords.php',
|
||||
'PhabricatorConduitAPIController' => 'applications/conduit/controller/PhabricatorConduitAPIController.php',
|
||||
'PhabricatorConduitApplication' => 'applications/conduit/application/PhabricatorConduitApplication.php',
|
||||
'PhabricatorConduitCallManagementWorkflow' => 'applications/conduit/management/PhabricatorConduitCallManagementWorkflow.php',
|
||||
'PhabricatorConduitCertificateToken' => 'applications/conduit/storage/PhabricatorConduitCertificateToken.php',
|
||||
'PhabricatorConduitConsoleController' => 'applications/conduit/controller/PhabricatorConduitConsoleController.php',
|
||||
'PhabricatorConduitContentSource' => 'infrastructure/contentsource/PhabricatorConduitContentSource.php',
|
||||
|
@ -2435,6 +2471,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorConduitLogController' => 'applications/conduit/controller/PhabricatorConduitLogController.php',
|
||||
'PhabricatorConduitLogQuery' => 'applications/conduit/query/PhabricatorConduitLogQuery.php',
|
||||
'PhabricatorConduitLogSearchEngine' => 'applications/conduit/query/PhabricatorConduitLogSearchEngine.php',
|
||||
'PhabricatorConduitManagementWorkflow' => 'applications/conduit/management/PhabricatorConduitManagementWorkflow.php',
|
||||
'PhabricatorConduitMethodCallLog' => 'applications/conduit/storage/PhabricatorConduitMethodCallLog.php',
|
||||
'PhabricatorConduitMethodQuery' => 'applications/conduit/query/PhabricatorConduitMethodQuery.php',
|
||||
'PhabricatorConduitRequestExceptionHandler' => 'aphront/handler/PhabricatorConduitRequestExceptionHandler.php',
|
||||
|
@ -2810,6 +2847,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorEditPage' => 'applications/transactions/editengine/PhabricatorEditPage.php',
|
||||
'PhabricatorEditType' => 'applications/transactions/edittype/PhabricatorEditType.php',
|
||||
'PhabricatorEditor' => 'infrastructure/PhabricatorEditor.php',
|
||||
'PhabricatorEditorMailEngineExtension' => 'applications/transactions/engineextension/PhabricatorEditorMailEngineExtension.php',
|
||||
'PhabricatorEditorMultipleSetting' => 'applications/settings/setting/PhabricatorEditorMultipleSetting.php',
|
||||
'PhabricatorEditorSetting' => 'applications/settings/setting/PhabricatorEditorSetting.php',
|
||||
'PhabricatorElasticFulltextStorageEngine' => 'applications/search/fulltextstorage/PhabricatorElasticFulltextStorageEngine.php',
|
||||
|
@ -2826,6 +2864,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorEmailPreferencesSettingsPanel' => 'applications/settings/panel/PhabricatorEmailPreferencesSettingsPanel.php',
|
||||
'PhabricatorEmailRePrefixSetting' => 'applications/settings/setting/PhabricatorEmailRePrefixSetting.php',
|
||||
'PhabricatorEmailSelfActionsSetting' => 'applications/settings/setting/PhabricatorEmailSelfActionsSetting.php',
|
||||
'PhabricatorEmailStampsSetting' => 'applications/settings/setting/PhabricatorEmailStampsSetting.php',
|
||||
'PhabricatorEmailTagsSetting' => 'applications/settings/setting/PhabricatorEmailTagsSetting.php',
|
||||
'PhabricatorEmailVarySubjectsSetting' => 'applications/settings/setting/PhabricatorEmailVarySubjectsSetting.php',
|
||||
'PhabricatorEmailVerificationController' => 'applications/auth/controller/PhabricatorEmailVerificationController.php',
|
||||
|
@ -3164,19 +3203,23 @@ phutil_register_library_map(array(
|
|||
'PhabricatorMacroQuery' => 'applications/macro/query/PhabricatorMacroQuery.php',
|
||||
'PhabricatorMacroReplyHandler' => 'applications/macro/mail/PhabricatorMacroReplyHandler.php',
|
||||
'PhabricatorMacroSearchEngine' => 'applications/macro/query/PhabricatorMacroSearchEngine.php',
|
||||
'PhabricatorMacroTestCase' => 'applications/macro/xaction/__tests__/PhabricatorMacroTestCase.php',
|
||||
'PhabricatorMacroTransaction' => 'applications/macro/storage/PhabricatorMacroTransaction.php',
|
||||
'PhabricatorMacroTransactionComment' => 'applications/macro/storage/PhabricatorMacroTransactionComment.php',
|
||||
'PhabricatorMacroTransactionQuery' => 'applications/macro/query/PhabricatorMacroTransactionQuery.php',
|
||||
'PhabricatorMacroTransactionType' => 'applications/macro/xaction/PhabricatorMacroTransactionType.php',
|
||||
'PhabricatorMacroViewController' => 'applications/macro/controller/PhabricatorMacroViewController.php',
|
||||
'PhabricatorMailConfigTestCase' => 'applications/metamta/storage/__tests__/PhabricatorMailConfigTestCase.php',
|
||||
'PhabricatorMailEmailHeraldField' => 'applications/metamta/herald/PhabricatorMailEmailHeraldField.php',
|
||||
'PhabricatorMailEmailHeraldFieldGroup' => 'applications/metamta/herald/PhabricatorMailEmailHeraldFieldGroup.php',
|
||||
'PhabricatorMailEmailSubjectHeraldField' => 'applications/metamta/herald/PhabricatorMailEmailSubjectHeraldField.php',
|
||||
'PhabricatorMailEngineExtension' => 'applications/metamta/engine/PhabricatorMailEngineExtension.php',
|
||||
'PhabricatorMailImplementationAdapter' => 'applications/metamta/adapter/PhabricatorMailImplementationAdapter.php',
|
||||
'PhabricatorMailImplementationAmazonSESAdapter' => 'applications/metamta/adapter/PhabricatorMailImplementationAmazonSESAdapter.php',
|
||||
'PhabricatorMailImplementationMailgunAdapter' => 'applications/metamta/adapter/PhabricatorMailImplementationMailgunAdapter.php',
|
||||
'PhabricatorMailImplementationPHPMailerAdapter' => 'applications/metamta/adapter/PhabricatorMailImplementationPHPMailerAdapter.php',
|
||||
'PhabricatorMailImplementationPHPMailerLiteAdapter' => 'applications/metamta/adapter/PhabricatorMailImplementationPHPMailerLiteAdapter.php',
|
||||
'PhabricatorMailImplementationPostmarkAdapter' => 'applications/metamta/adapter/PhabricatorMailImplementationPostmarkAdapter.php',
|
||||
'PhabricatorMailImplementationSendGridAdapter' => 'applications/metamta/adapter/PhabricatorMailImplementationSendGridAdapter.php',
|
||||
'PhabricatorMailImplementationTestAdapter' => 'applications/metamta/adapter/PhabricatorMailImplementationTestAdapter.php',
|
||||
'PhabricatorMailManagementListInboundWorkflow' => 'applications/metamta/management/PhabricatorMailManagementListInboundWorkflow.php',
|
||||
|
@ -3189,6 +3232,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorMailManagementUnverifyWorkflow' => 'applications/metamta/management/PhabricatorMailManagementUnverifyWorkflow.php',
|
||||
'PhabricatorMailManagementVolumeWorkflow' => 'applications/metamta/management/PhabricatorMailManagementVolumeWorkflow.php',
|
||||
'PhabricatorMailManagementWorkflow' => 'applications/metamta/management/PhabricatorMailManagementWorkflow.php',
|
||||
'PhabricatorMailMustEncryptHeraldAction' => 'applications/metamta/herald/PhabricatorMailMustEncryptHeraldAction.php',
|
||||
'PhabricatorMailOutboundMailHeraldAdapter' => 'applications/metamta/herald/PhabricatorMailOutboundMailHeraldAdapter.php',
|
||||
'PhabricatorMailOutboundRoutingHeraldAction' => 'applications/metamta/herald/PhabricatorMailOutboundRoutingHeraldAction.php',
|
||||
'PhabricatorMailOutboundRoutingSelfEmailHeraldAction' => 'applications/metamta/herald/PhabricatorMailOutboundRoutingSelfEmailHeraldAction.php',
|
||||
|
@ -3199,6 +3243,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorMailReplyHandler' => 'applications/metamta/replyhandler/PhabricatorMailReplyHandler.php',
|
||||
'PhabricatorMailRoutingRule' => 'applications/metamta/constants/PhabricatorMailRoutingRule.php',
|
||||
'PhabricatorMailSetupCheck' => 'applications/config/check/PhabricatorMailSetupCheck.php',
|
||||
'PhabricatorMailStamp' => 'applications/metamta/stamp/PhabricatorMailStamp.php',
|
||||
'PhabricatorMailTarget' => 'applications/metamta/replyhandler/PhabricatorMailTarget.php',
|
||||
'PhabricatorMailgunConfigOptions' => 'applications/config/option/PhabricatorMailgunConfigOptions.php',
|
||||
'PhabricatorMainMenuBarExtension' => 'view/page/menu/PhabricatorMainMenuBarExtension.php',
|
||||
|
@ -3258,6 +3303,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorMetaMTAMailgunReceiveController' => 'applications/metamta/controller/PhabricatorMetaMTAMailgunReceiveController.php',
|
||||
'PhabricatorMetaMTAMemberQuery' => 'applications/metamta/query/PhabricatorMetaMTAMemberQuery.php',
|
||||
'PhabricatorMetaMTAPermanentFailureException' => 'applications/metamta/exception/PhabricatorMetaMTAPermanentFailureException.php',
|
||||
'PhabricatorMetaMTAPostmarkReceiveController' => 'applications/metamta/controller/PhabricatorMetaMTAPostmarkReceiveController.php',
|
||||
'PhabricatorMetaMTAReceivedMail' => 'applications/metamta/storage/PhabricatorMetaMTAReceivedMail.php',
|
||||
'PhabricatorMetaMTAReceivedMailProcessingException' => 'applications/metamta/exception/PhabricatorMetaMTAReceivedMailProcessingException.php',
|
||||
'PhabricatorMetaMTAReceivedMailTestCase' => 'applications/metamta/storage/__tests__/PhabricatorMetaMTAReceivedMailTestCase.php',
|
||||
|
@ -3275,6 +3321,8 @@ phutil_register_library_map(array(
|
|||
'PhabricatorMultiFactorSettingsPanel' => 'applications/settings/panel/PhabricatorMultiFactorSettingsPanel.php',
|
||||
'PhabricatorMultimeterApplication' => 'applications/multimeter/application/PhabricatorMultimeterApplication.php',
|
||||
'PhabricatorMustVerifyEmailController' => 'applications/auth/controller/PhabricatorMustVerifyEmailController.php',
|
||||
'PhabricatorMutedByEdgeType' => 'applications/transactions/edges/PhabricatorMutedByEdgeType.php',
|
||||
'PhabricatorMutedEdgeType' => 'applications/transactions/edges/PhabricatorMutedEdgeType.php',
|
||||
'PhabricatorMySQLConfigOptions' => 'applications/config/option/PhabricatorMySQLConfigOptions.php',
|
||||
'PhabricatorMySQLFileStorageEngine' => 'applications/files/engine/PhabricatorMySQLFileStorageEngine.php',
|
||||
'PhabricatorMySQLSearchHost' => 'infrastructure/cluster/search/PhabricatorMySQLSearchHost.php',
|
||||
|
@ -3433,6 +3481,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorPHIDListEditField' => 'applications/transactions/editfield/PhabricatorPHIDListEditField.php',
|
||||
'PhabricatorPHIDListEditType' => 'applications/transactions/edittype/PhabricatorPHIDListEditType.php',
|
||||
'PhabricatorPHIDListExportField' => 'infrastructure/export/field/PhabricatorPHIDListExportField.php',
|
||||
'PhabricatorPHIDMailStamp' => 'applications/metamta/stamp/PhabricatorPHIDMailStamp.php',
|
||||
'PhabricatorPHIDResolver' => 'applications/phid/resolver/PhabricatorPHIDResolver.php',
|
||||
'PhabricatorPHIDType' => 'applications/phid/type/PhabricatorPHIDType.php',
|
||||
'PhabricatorPHIDTypeTestCase' => 'applications/phid/type/__tests__/PhabricatorPHIDTypeTestCase.php',
|
||||
|
@ -3843,6 +3892,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorProjectsEditField' => 'applications/transactions/editfield/PhabricatorProjectsEditField.php',
|
||||
'PhabricatorProjectsExportEngineExtension' => 'infrastructure/export/engine/PhabricatorProjectsExportEngineExtension.php',
|
||||
'PhabricatorProjectsFulltextEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsFulltextEngineExtension.php',
|
||||
'PhabricatorProjectsMailEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsMailEngineExtension.php',
|
||||
'PhabricatorProjectsMembersSearchEngineAttachment' => 'applications/project/engineextension/PhabricatorProjectsMembersSearchEngineAttachment.php',
|
||||
'PhabricatorProjectsMembershipIndexEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsMembershipIndexEngineExtension.php',
|
||||
'PhabricatorProjectsPolicyRule' => 'applications/project/policyrule/PhabricatorProjectsPolicyRule.php',
|
||||
|
@ -4136,6 +4186,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorSpacesExportEngineExtension' => 'infrastructure/export/engine/PhabricatorSpacesExportEngineExtension.php',
|
||||
'PhabricatorSpacesInterface' => 'applications/spaces/interface/PhabricatorSpacesInterface.php',
|
||||
'PhabricatorSpacesListController' => 'applications/spaces/controller/PhabricatorSpacesListController.php',
|
||||
'PhabricatorSpacesMailEngineExtension' => 'applications/spaces/engineextension/PhabricatorSpacesMailEngineExtension.php',
|
||||
'PhabricatorSpacesNamespace' => 'applications/spaces/storage/PhabricatorSpacesNamespace.php',
|
||||
'PhabricatorSpacesNamespaceArchiveTransaction' => 'applications/spaces/xaction/PhabricatorSpacesNamespaceArchiveTransaction.php',
|
||||
'PhabricatorSpacesNamespaceDatasource' => 'applications/spaces/typeahead/PhabricatorSpacesNamespaceDatasource.php',
|
||||
|
@ -4201,6 +4252,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorStringListConfigType' => 'applications/config/type/PhabricatorStringListConfigType.php',
|
||||
'PhabricatorStringListEditField' => 'applications/transactions/editfield/PhabricatorStringListEditField.php',
|
||||
'PhabricatorStringListExportField' => 'infrastructure/export/field/PhabricatorStringListExportField.php',
|
||||
'PhabricatorStringMailStamp' => 'applications/metamta/stamp/PhabricatorStringMailStamp.php',
|
||||
'PhabricatorStringSetting' => 'applications/settings/setting/PhabricatorStringSetting.php',
|
||||
'PhabricatorSubmitEditField' => 'applications/transactions/editfield/PhabricatorSubmitEditField.php',
|
||||
'PhabricatorSubscribableInterface' => 'applications/subscriptions/interface/PhabricatorSubscribableInterface.php',
|
||||
|
@ -4219,6 +4271,8 @@ phutil_register_library_map(array(
|
|||
'PhabricatorSubscriptionsFulltextEngineExtension' => 'applications/subscriptions/engineextension/PhabricatorSubscriptionsFulltextEngineExtension.php',
|
||||
'PhabricatorSubscriptionsHeraldAction' => 'applications/subscriptions/herald/PhabricatorSubscriptionsHeraldAction.php',
|
||||
'PhabricatorSubscriptionsListController' => 'applications/subscriptions/controller/PhabricatorSubscriptionsListController.php',
|
||||
'PhabricatorSubscriptionsMailEngineExtension' => 'applications/subscriptions/engineextension/PhabricatorSubscriptionsMailEngineExtension.php',
|
||||
'PhabricatorSubscriptionsMuteController' => 'applications/subscriptions/controller/PhabricatorSubscriptionsMuteController.php',
|
||||
'PhabricatorSubscriptionsRemoveSelfHeraldAction' => 'applications/subscriptions/herald/PhabricatorSubscriptionsRemoveSelfHeraldAction.php',
|
||||
'PhabricatorSubscriptionsRemoveSubscribersHeraldAction' => 'applications/subscriptions/herald/PhabricatorSubscriptionsRemoveSubscribersHeraldAction.php',
|
||||
'PhabricatorSubscriptionsSearchEngineAttachment' => 'applications/subscriptions/engineextension/PhabricatorSubscriptionsSearchEngineAttachment.php',
|
||||
|
@ -5596,6 +5650,7 @@ phutil_register_library_map(array(
|
|||
'DifferentialLintField' => 'DifferentialHarbormasterField',
|
||||
'DifferentialLintStatus' => 'Phobject',
|
||||
'DifferentialLocalCommitsView' => 'AphrontView',
|
||||
'DifferentialMailEngineExtension' => 'PhabricatorMailEngineExtension',
|
||||
'DifferentialMailView' => 'Phobject',
|
||||
'DifferentialManiphestTasksField' => 'DifferentialCoreCustomField',
|
||||
'DifferentialModernHunk' => 'DifferentialHunk',
|
||||
|
@ -6568,6 +6623,7 @@ phutil_register_library_map(array(
|
|||
'HarbormasterWaitForPreviousBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
|
||||
'HarbormasterWorker' => 'PhabricatorWorker',
|
||||
'HarbormasterWorkingCopyArtifact' => 'HarbormasterDrydockLeaseArtifact',
|
||||
'HeraldActingUserField' => 'HeraldField',
|
||||
'HeraldAction' => 'Phobject',
|
||||
'HeraldActionGroup' => 'HeraldGroup',
|
||||
'HeraldActionRecord' => 'HeraldDAO',
|
||||
|
@ -6578,6 +6634,7 @@ phutil_register_library_map(array(
|
|||
'HeraldApplyTranscript' => 'Phobject',
|
||||
'HeraldBasicFieldGroup' => 'HeraldFieldGroup',
|
||||
'HeraldBuildableState' => 'HeraldState',
|
||||
'HeraldCallWebhookAction' => 'HeraldAction',
|
||||
'HeraldCommentAction' => 'HeraldAction',
|
||||
'HeraldCommitAdapter' => array(
|
||||
'HeraldAdapter',
|
||||
|
@ -6588,6 +6645,7 @@ phutil_register_library_map(array(
|
|||
'HeraldContentSourceField' => 'HeraldField',
|
||||
'HeraldController' => 'PhabricatorController',
|
||||
'HeraldCoreStateReasons' => 'HeraldStateReasons',
|
||||
'HeraldCreateWebhooksCapability' => 'PhabricatorPolicyCapability',
|
||||
'HeraldDAO' => 'PhabricatorLiskDAO',
|
||||
'HeraldDeprecatedFieldGroup' => 'HeraldFieldGroup',
|
||||
'HeraldDifferentialAdapter' => 'HeraldAdapter',
|
||||
|
@ -6678,6 +6736,43 @@ phutil_register_library_map(array(
|
|||
'HeraldTranscriptSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
'HeraldTranscriptTestCase' => 'PhabricatorTestCase',
|
||||
'HeraldUtilityActionGroup' => 'HeraldActionGroup',
|
||||
'HeraldWebhook' => array(
|
||||
'HeraldDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
'PhabricatorApplicationTransactionInterface',
|
||||
'PhabricatorDestructibleInterface',
|
||||
'PhabricatorProjectInterface',
|
||||
),
|
||||
'HeraldWebhookCallManagementWorkflow' => 'HeraldWebhookManagementWorkflow',
|
||||
'HeraldWebhookController' => 'HeraldController',
|
||||
'HeraldWebhookDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||
'HeraldWebhookEditController' => 'HeraldWebhookController',
|
||||
'HeraldWebhookEditEngine' => 'PhabricatorEditEngine',
|
||||
'HeraldWebhookEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||
'HeraldWebhookKeyController' => 'HeraldWebhookController',
|
||||
'HeraldWebhookListController' => 'HeraldWebhookController',
|
||||
'HeraldWebhookManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
||||
'HeraldWebhookNameTransaction' => 'HeraldWebhookTransactionType',
|
||||
'HeraldWebhookPHIDType' => 'PhabricatorPHIDType',
|
||||
'HeraldWebhookQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'HeraldWebhookRequest' => array(
|
||||
'HeraldDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
'PhabricatorExtendedPolicyInterface',
|
||||
),
|
||||
'HeraldWebhookRequestGarbageCollector' => 'PhabricatorGarbageCollector',
|
||||
'HeraldWebhookRequestListView' => 'AphrontView',
|
||||
'HeraldWebhookRequestPHIDType' => 'PhabricatorPHIDType',
|
||||
'HeraldWebhookRequestQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'HeraldWebhookSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
'HeraldWebhookStatusTransaction' => 'HeraldWebhookTransactionType',
|
||||
'HeraldWebhookTestController' => 'HeraldWebhookController',
|
||||
'HeraldWebhookTransaction' => 'PhabricatorModularTransaction',
|
||||
'HeraldWebhookTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||
'HeraldWebhookTransactionType' => 'PhabricatorModularTransactionType',
|
||||
'HeraldWebhookURITransaction' => 'HeraldWebhookTransactionType',
|
||||
'HeraldWebhookViewController' => 'HeraldWebhookController',
|
||||
'HeraldWebhookWorker' => 'PhabricatorWorker',
|
||||
'Javelin' => 'Phobject',
|
||||
'LegalpadController' => 'PhabricatorController',
|
||||
'LegalpadCreateDocumentsCapability' => 'PhabricatorPolicyCapability',
|
||||
|
@ -6783,6 +6878,7 @@ phutil_register_library_map(array(
|
|||
'ManiphestGetTaskTransactionsConduitAPIMethod' => 'ManiphestConduitAPIMethod',
|
||||
'ManiphestHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension',
|
||||
'ManiphestInfoConduitAPIMethod' => 'ManiphestConduitAPIMethod',
|
||||
'ManiphestMailEngineExtension' => 'PhabricatorMailEngineExtension',
|
||||
'ManiphestNameIndex' => 'ManiphestDAO',
|
||||
'ManiphestPointsConfigType' => 'PhabricatorJSONConfigType',
|
||||
'ManiphestPrioritiesConfigType' => 'PhabricatorJSONConfigType',
|
||||
|
@ -7266,6 +7362,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorApplicationEditHTTPParameterHelpView' => 'AphrontView',
|
||||
'PhabricatorApplicationEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||
'PhabricatorApplicationEmailCommandsController' => 'PhabricatorApplicationsController',
|
||||
'PhabricatorApplicationObjectMailEngineExtension' => 'PhabricatorMailEngineExtension',
|
||||
'PhabricatorApplicationPanelController' => 'PhabricatorApplicationsController',
|
||||
'PhabricatorApplicationPolicyChangeTransaction' => 'PhabricatorApplicationTransactionType',
|
||||
'PhabricatorApplicationProfileMenuItem' => 'PhabricatorProfileMenuItem',
|
||||
|
@ -7573,6 +7670,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorBoardResponseEngine' => 'Phobject',
|
||||
'PhabricatorBoolConfigType' => 'PhabricatorTextConfigType',
|
||||
'PhabricatorBoolEditField' => 'PhabricatorEditField',
|
||||
'PhabricatorBoolMailStamp' => 'PhabricatorMailStamp',
|
||||
'PhabricatorBritishEnglishTranslation' => 'PhutilTranslation',
|
||||
'PhabricatorBuiltinDraftEngine' => 'PhabricatorDraftEngine',
|
||||
'PhabricatorBuiltinFileCachePurger' => 'PhabricatorCachePurger',
|
||||
|
@ -7804,6 +7902,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorClusterExceptionHandler' => 'PhabricatorRequestExceptionHandler',
|
||||
'PhabricatorClusterImpossibleWriteException' => 'PhabricatorClusterException',
|
||||
'PhabricatorClusterImproperWriteException' => 'PhabricatorClusterException',
|
||||
'PhabricatorClusterMailersConfigType' => 'PhabricatorJSONConfigType',
|
||||
'PhabricatorClusterNoHostForRoleException' => 'Exception',
|
||||
'PhabricatorClusterSearchConfigType' => 'PhabricatorJSONConfigType',
|
||||
'PhabricatorClusterServiceHealthRecord' => 'Phobject',
|
||||
|
@ -7821,6 +7920,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorCommonPasswords' => 'Phobject',
|
||||
'PhabricatorConduitAPIController' => 'PhabricatorConduitController',
|
||||
'PhabricatorConduitApplication' => 'PhabricatorApplication',
|
||||
'PhabricatorConduitCallManagementWorkflow' => 'PhabricatorConduitManagementWorkflow',
|
||||
'PhabricatorConduitCertificateToken' => 'PhabricatorConduitDAO',
|
||||
'PhabricatorConduitConsoleController' => 'PhabricatorConduitController',
|
||||
'PhabricatorConduitContentSource' => 'PhabricatorContentSource',
|
||||
|
@ -7831,6 +7931,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorConduitLogController' => 'PhabricatorConduitController',
|
||||
'PhabricatorConduitLogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhabricatorConduitLogSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
'PhabricatorConduitManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
||||
'PhabricatorConduitMethodCallLog' => array(
|
||||
'PhabricatorConduitDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
|
@ -8256,6 +8357,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorEditPage' => 'Phobject',
|
||||
'PhabricatorEditType' => 'Phobject',
|
||||
'PhabricatorEditor' => 'Phobject',
|
||||
'PhabricatorEditorMailEngineExtension' => 'PhabricatorMailEngineExtension',
|
||||
'PhabricatorEditorMultipleSetting' => 'PhabricatorSelectSetting',
|
||||
'PhabricatorEditorSetting' => 'PhabricatorStringSetting',
|
||||
'PhabricatorElasticFulltextStorageEngine' => 'PhabricatorFulltextStorageEngine',
|
||||
|
@ -8271,6 +8373,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorEmailPreferencesSettingsPanel' => 'PhabricatorSettingsPanel',
|
||||
'PhabricatorEmailRePrefixSetting' => 'PhabricatorSelectSetting',
|
||||
'PhabricatorEmailSelfActionsSetting' => 'PhabricatorSelectSetting',
|
||||
'PhabricatorEmailStampsSetting' => 'PhabricatorSelectSetting',
|
||||
'PhabricatorEmailTagsSetting' => 'PhabricatorInternalSetting',
|
||||
'PhabricatorEmailVarySubjectsSetting' => 'PhabricatorSelectSetting',
|
||||
'PhabricatorEmailVerificationController' => 'PhabricatorAuthController',
|
||||
|
@ -8649,19 +8752,23 @@ phutil_register_library_map(array(
|
|||
'PhabricatorMacroQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhabricatorMacroReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
|
||||
'PhabricatorMacroSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
'PhabricatorMacroTestCase' => 'PhabricatorTestCase',
|
||||
'PhabricatorMacroTransaction' => 'PhabricatorModularTransaction',
|
||||
'PhabricatorMacroTransactionComment' => 'PhabricatorApplicationTransactionComment',
|
||||
'PhabricatorMacroTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||
'PhabricatorMacroTransactionType' => 'PhabricatorModularTransactionType',
|
||||
'PhabricatorMacroViewController' => 'PhabricatorMacroController',
|
||||
'PhabricatorMailConfigTestCase' => 'PhabricatorTestCase',
|
||||
'PhabricatorMailEmailHeraldField' => 'HeraldField',
|
||||
'PhabricatorMailEmailHeraldFieldGroup' => 'HeraldFieldGroup',
|
||||
'PhabricatorMailEmailSubjectHeraldField' => 'PhabricatorMailEmailHeraldField',
|
||||
'PhabricatorMailEngineExtension' => 'Phobject',
|
||||
'PhabricatorMailImplementationAdapter' => 'Phobject',
|
||||
'PhabricatorMailImplementationAmazonSESAdapter' => 'PhabricatorMailImplementationPHPMailerLiteAdapter',
|
||||
'PhabricatorMailImplementationMailgunAdapter' => 'PhabricatorMailImplementationAdapter',
|
||||
'PhabricatorMailImplementationPHPMailerAdapter' => 'PhabricatorMailImplementationAdapter',
|
||||
'PhabricatorMailImplementationPHPMailerLiteAdapter' => 'PhabricatorMailImplementationAdapter',
|
||||
'PhabricatorMailImplementationPostmarkAdapter' => 'PhabricatorMailImplementationAdapter',
|
||||
'PhabricatorMailImplementationSendGridAdapter' => 'PhabricatorMailImplementationAdapter',
|
||||
'PhabricatorMailImplementationTestAdapter' => 'PhabricatorMailImplementationAdapter',
|
||||
'PhabricatorMailManagementListInboundWorkflow' => 'PhabricatorMailManagementWorkflow',
|
||||
|
@ -8674,6 +8781,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorMailManagementUnverifyWorkflow' => 'PhabricatorMailManagementWorkflow',
|
||||
'PhabricatorMailManagementVolumeWorkflow' => 'PhabricatorMailManagementWorkflow',
|
||||
'PhabricatorMailManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
||||
'PhabricatorMailMustEncryptHeraldAction' => 'HeraldAction',
|
||||
'PhabricatorMailOutboundMailHeraldAdapter' => 'HeraldAdapter',
|
||||
'PhabricatorMailOutboundRoutingHeraldAction' => 'HeraldAction',
|
||||
'PhabricatorMailOutboundRoutingSelfEmailHeraldAction' => 'PhabricatorMailOutboundRoutingHeraldAction',
|
||||
|
@ -8684,6 +8792,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorMailReplyHandler' => 'Phobject',
|
||||
'PhabricatorMailRoutingRule' => 'Phobject',
|
||||
'PhabricatorMailSetupCheck' => 'PhabricatorSetupCheck',
|
||||
'PhabricatorMailStamp' => 'Phobject',
|
||||
'PhabricatorMailTarget' => 'Phobject',
|
||||
'PhabricatorMailgunConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
'PhabricatorMainMenuBarExtension' => 'Phobject',
|
||||
|
@ -8737,6 +8846,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorMetaMTAMail' => array(
|
||||
'PhabricatorMetaMTADAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
'PhabricatorDestructibleInterface',
|
||||
),
|
||||
'PhabricatorMetaMTAMailBody' => 'Phobject',
|
||||
'PhabricatorMetaMTAMailBodyTestCase' => 'PhabricatorTestCase',
|
||||
|
@ -8753,6 +8863,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorMetaMTAMailgunReceiveController' => 'PhabricatorMetaMTAController',
|
||||
'PhabricatorMetaMTAMemberQuery' => 'PhabricatorQuery',
|
||||
'PhabricatorMetaMTAPermanentFailureException' => 'Exception',
|
||||
'PhabricatorMetaMTAPostmarkReceiveController' => 'PhabricatorMetaMTAController',
|
||||
'PhabricatorMetaMTAReceivedMail' => 'PhabricatorMetaMTADAO',
|
||||
'PhabricatorMetaMTAReceivedMailProcessingException' => 'Exception',
|
||||
'PhabricatorMetaMTAReceivedMailTestCase' => 'PhabricatorTestCase',
|
||||
|
@ -8770,6 +8881,8 @@ phutil_register_library_map(array(
|
|||
'PhabricatorMultiFactorSettingsPanel' => 'PhabricatorSettingsPanel',
|
||||
'PhabricatorMultimeterApplication' => 'PhabricatorApplication',
|
||||
'PhabricatorMustVerifyEmailController' => 'PhabricatorAuthController',
|
||||
'PhabricatorMutedByEdgeType' => 'PhabricatorEdgeType',
|
||||
'PhabricatorMutedEdgeType' => 'PhabricatorEdgeType',
|
||||
'PhabricatorMySQLConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
'PhabricatorMySQLFileStorageEngine' => 'PhabricatorFileStorageEngine',
|
||||
'PhabricatorMySQLSearchHost' => 'PhabricatorSearchHost',
|
||||
|
@ -8957,6 +9070,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorPHIDListEditField' => 'PhabricatorEditField',
|
||||
'PhabricatorPHIDListEditType' => 'PhabricatorEditType',
|
||||
'PhabricatorPHIDListExportField' => 'PhabricatorListExportField',
|
||||
'PhabricatorPHIDMailStamp' => 'PhabricatorMailStamp',
|
||||
'PhabricatorPHIDResolver' => 'Phobject',
|
||||
'PhabricatorPHIDType' => 'Phobject',
|
||||
'PhabricatorPHIDTypeTestCase' => 'PhutilTestCase',
|
||||
|
@ -9459,6 +9573,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorProjectsEditField' => 'PhabricatorTokenizerEditField',
|
||||
'PhabricatorProjectsExportEngineExtension' => 'PhabricatorExportEngineExtension',
|
||||
'PhabricatorProjectsFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension',
|
||||
'PhabricatorProjectsMailEngineExtension' => 'PhabricatorMailEngineExtension',
|
||||
'PhabricatorProjectsMembersSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
|
||||
'PhabricatorProjectsMembershipIndexEngineExtension' => 'PhabricatorIndexEngineExtension',
|
||||
'PhabricatorProjectsPolicyRule' => 'PhabricatorPolicyRule',
|
||||
|
@ -9828,6 +9943,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorSpacesExportEngineExtension' => 'PhabricatorExportEngineExtension',
|
||||
'PhabricatorSpacesInterface' => 'PhabricatorPHIDInterface',
|
||||
'PhabricatorSpacesListController' => 'PhabricatorSpacesController',
|
||||
'PhabricatorSpacesMailEngineExtension' => 'PhabricatorMailEngineExtension',
|
||||
'PhabricatorSpacesNamespace' => array(
|
||||
'PhabricatorSpacesDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
|
@ -9900,6 +10016,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorStringListConfigType' => 'PhabricatorTextListConfigType',
|
||||
'PhabricatorStringListEditField' => 'PhabricatorEditField',
|
||||
'PhabricatorStringListExportField' => 'PhabricatorListExportField',
|
||||
'PhabricatorStringMailStamp' => 'PhabricatorMailStamp',
|
||||
'PhabricatorStringSetting' => 'PhabricatorSetting',
|
||||
'PhabricatorSubmitEditField' => 'PhabricatorEditField',
|
||||
'PhabricatorSubscribedToObjectEdgeType' => 'PhabricatorEdgeType',
|
||||
|
@ -9917,6 +10034,8 @@ phutil_register_library_map(array(
|
|||
'PhabricatorSubscriptionsFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension',
|
||||
'PhabricatorSubscriptionsHeraldAction' => 'HeraldAction',
|
||||
'PhabricatorSubscriptionsListController' => 'PhabricatorController',
|
||||
'PhabricatorSubscriptionsMailEngineExtension' => 'PhabricatorMailEngineExtension',
|
||||
'PhabricatorSubscriptionsMuteController' => 'PhabricatorController',
|
||||
'PhabricatorSubscriptionsRemoveSelfHeraldAction' => 'PhabricatorSubscriptionsHeraldAction',
|
||||
'PhabricatorSubscriptionsRemoveSubscribersHeraldAction' => 'PhabricatorSubscriptionsHeraldAction',
|
||||
'PhabricatorSubscriptionsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
|
||||
|
|
|
@ -81,6 +81,8 @@ final class AlmanacManagementTrustKeyWorkflow
|
|||
$key->setIsTrusted(1);
|
||||
$key->save();
|
||||
|
||||
PhabricatorAuthSSHKeyQuery::deleteSSHKeyCache();
|
||||
|
||||
$console->writeOut(
|
||||
"**<bg:green> %s </bg>** %s\n",
|
||||
pht('TRUSTED'),
|
||||
|
|
|
@ -43,6 +43,8 @@ final class AlmanacManagementUntrustKeyWorkflow
|
|||
$key->setIsTrusted(0);
|
||||
$key->save();
|
||||
|
||||
PhabricatorAuthSSHKeyQuery::deleteSSHKeyCache();
|
||||
|
||||
$console->writeOut(
|
||||
"**<bg:green> %s </bg>** %s\n",
|
||||
pht('TRUST REVOKED'),
|
||||
|
|
|
@ -473,17 +473,14 @@ final class PhabricatorAuditEditor
|
|||
protected function buildMailTemplate(PhabricatorLiskDAO $object) {
|
||||
$identifier = $object->getCommitIdentifier();
|
||||
$repository = $object->getRepository();
|
||||
$monogram = $repository->getMonogram();
|
||||
|
||||
$summary = $object->getSummary();
|
||||
$name = $repository->formatCommitName($identifier);
|
||||
|
||||
$subject = "{$name}: {$summary}";
|
||||
$thread_topic = "Commit {$monogram}{$identifier}";
|
||||
|
||||
$template = id(new PhabricatorMetaMTAMail())
|
||||
->setSubject($subject)
|
||||
->addHeader('Thread-Topic', $thread_topic);
|
||||
->setSubject($subject);
|
||||
|
||||
$this->attachPatch(
|
||||
$template,
|
||||
|
@ -493,13 +490,14 @@ final class PhabricatorAuditEditor
|
|||
}
|
||||
|
||||
protected function getMailTo(PhabricatorLiskDAO $object) {
|
||||
$this->requireAuditors($object);
|
||||
|
||||
$phids = array();
|
||||
|
||||
if ($object->getAuthorPHID()) {
|
||||
$phids[] = $object->getAuthorPHID();
|
||||
}
|
||||
|
||||
$status_resigned = PhabricatorAuditStatusConstants::RESIGNED;
|
||||
foreach ($object->getAudits() as $audit) {
|
||||
if (!$audit->isInteresting()) {
|
||||
// Don't send mail to uninteresting auditors, like packages which
|
||||
|
@ -507,7 +505,7 @@ final class PhabricatorAuditEditor
|
|||
continue;
|
||||
}
|
||||
|
||||
if ($audit->getAuditStatus() != $status_resigned) {
|
||||
if (!$audit->isResigned()) {
|
||||
$phids[] = $audit->getAuditorPHID();
|
||||
}
|
||||
}
|
||||
|
@ -517,6 +515,20 @@ final class PhabricatorAuditEditor
|
|||
return $phids;
|
||||
}
|
||||
|
||||
protected function newMailUnexpandablePHIDs(PhabricatorLiskDAO $object) {
|
||||
$this->requireAuditors($object);
|
||||
|
||||
$phids = array();
|
||||
|
||||
foreach ($object->getAudits() as $auditor) {
|
||||
if ($auditor->isResigned()) {
|
||||
$phids[] = $auditor->getAuditorPHID();
|
||||
}
|
||||
}
|
||||
|
||||
return $phids;
|
||||
}
|
||||
|
||||
protected function buildMailBody(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
|
@ -848,4 +860,24 @@ final class PhabricatorAuditEditor
|
|||
->executeOne();
|
||||
}
|
||||
|
||||
private function requireAuditors(PhabricatorRepositoryCommit $commit) {
|
||||
if ($commit->hasAttachedAudits()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$with_auditors = id(new DiffusionCommitQuery())
|
||||
->setViewer($this->getActor())
|
||||
->needAuditRequests(true)
|
||||
->withPHIDs(array($commit->getPHID()))
|
||||
->executeOne();
|
||||
if (!$with_auditors) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Failed to reload commit ("%s").',
|
||||
$commit->getPHID()));
|
||||
}
|
||||
|
||||
$commit->attachAudits($with_auditors->getAudits());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -255,11 +255,9 @@ final class PhabricatorAuthSSHKeyEditor
|
|||
protected function buildMailTemplate(PhabricatorLiskDAO $object) {
|
||||
$id = $object->getID();
|
||||
$name = $object->getName();
|
||||
$phid = $object->getPHID();
|
||||
|
||||
$mail = id(new PhabricatorMetaMTAMail())
|
||||
->setSubject(pht('SSH Key %d: %s', $id, $name))
|
||||
->addHeader('Thread-Topic', $phid);
|
||||
->setSubject(pht('SSH Key %d: %s', $id, $name));
|
||||
|
||||
// The primary value of this mail is alerting users to account compromises,
|
||||
// so force delivery. In particular, this mail should still be delivered
|
||||
|
|
|
@ -87,12 +87,10 @@ final class PhabricatorBadgesEditor
|
|||
protected function buildMailTemplate(PhabricatorLiskDAO $object) {
|
||||
$name = $object->getName();
|
||||
$id = $object->getID();
|
||||
$topic = pht('Badge %d', $id);
|
||||
$subject = pht('Badge %d: %s', $id, $name);
|
||||
|
||||
return id(new PhabricatorMetaMTAMail())
|
||||
->setSubject($subject)
|
||||
->addHeader('Thread-Topic', $topic);
|
||||
->setSubject($subject);
|
||||
}
|
||||
|
||||
protected function getMailTo(PhabricatorLiskDAO $object) {
|
||||
|
|
|
@ -309,13 +309,11 @@ final class PhabricatorCalendarEventEditor
|
|||
}
|
||||
|
||||
protected function buildMailTemplate(PhabricatorLiskDAO $object) {
|
||||
$id = $object->getID();
|
||||
$name = $object->getName();
|
||||
$monogram = $object->getMonogram();
|
||||
|
||||
return id(new PhabricatorMetaMTAMail())
|
||||
->setSubject("{$monogram}: {$name}")
|
||||
->addHeader('Thread-Topic', $monogram);
|
||||
->setSubject("{$monogram}: {$name}");
|
||||
}
|
||||
|
||||
protected function buildMailBody(
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorConduitCallManagementWorkflow
|
||||
extends PhabricatorConduitManagementWorkflow {
|
||||
|
||||
protected function didConstruct() {
|
||||
$this
|
||||
->setName('call')
|
||||
->setSynopsis(pht('Call a Conduit method..'))
|
||||
->setArguments(
|
||||
array(
|
||||
array(
|
||||
'name' => 'method',
|
||||
'param' => 'method',
|
||||
'help' => pht('Method to call.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'input',
|
||||
'param' => 'input',
|
||||
'help' => pht(
|
||||
'File to read parameters from, or "-" to read from '.
|
||||
'stdin.'),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
public function execute(PhutilArgumentParser $args) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$method = $args->getArg('method');
|
||||
if (!strlen($method)) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht('Specify a method to call with "--method".'));
|
||||
}
|
||||
|
||||
$input = $args->getArg('input');
|
||||
if (!strlen($input)) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht('Specify a file to read parameters from with "--input".'));
|
||||
}
|
||||
|
||||
if ($input === '-') {
|
||||
fprintf(STDERR, tsprintf("%s\n", pht('Reading input from stdin...')));
|
||||
$input_json = file_get_contents('php://stdin');
|
||||
} else {
|
||||
$input_json = Filesystem::readFile($input);
|
||||
}
|
||||
|
||||
$params = phutil_json_decode($input_json);
|
||||
|
||||
$result = id(new ConduitCall($method, $params))
|
||||
->setUser($viewer)
|
||||
->execute();
|
||||
|
||||
$output = array(
|
||||
'result' => $result,
|
||||
);
|
||||
|
||||
echo tsprintf(
|
||||
"%B\n",
|
||||
id(new PhutilJSON())->encodeFormatted($output));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorConduitManagementWorkflow
|
||||
extends PhabricatorManagementWorkflow {}
|
|
@ -7,6 +7,10 @@ final class PhabricatorMailSetupCheck extends PhabricatorSetupCheck {
|
|||
}
|
||||
|
||||
protected function executeChecks() {
|
||||
if (PhabricatorEnv::getEnvConfig('cluster.mailers')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$adapter = PhabricatorEnv::getEnvConfig('metamta.mail-adapter');
|
||||
|
||||
switch ($adapter) {
|
||||
|
|
|
@ -6,7 +6,9 @@ final class PhabricatorConfigManagementSetWorkflow
|
|||
protected function didConstruct() {
|
||||
$this
|
||||
->setName('set')
|
||||
->setExamples('**set** __key__ __value__')
|
||||
->setExamples(
|
||||
"**set** __key__ __value__\n".
|
||||
"**set** __key__ --stdin < value.json")
|
||||
->setSynopsis(pht('Set a local configuration value.'))
|
||||
->setArguments(
|
||||
array(
|
||||
|
@ -16,6 +18,10 @@ final class PhabricatorConfigManagementSetWorkflow
|
|||
'Update configuration in the database instead of '.
|
||||
'in local configuration.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'stdin',
|
||||
'help' => pht('Read option value from stdin.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'args',
|
||||
'wildcard' => true,
|
||||
|
@ -31,22 +37,36 @@ final class PhabricatorConfigManagementSetWorkflow
|
|||
pht('Specify a configuration key and a value to set it to.'));
|
||||
}
|
||||
|
||||
$is_stdin = $args->getArg('stdin');
|
||||
|
||||
$key = $argv[0];
|
||||
|
||||
if (count($argv) == 1) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
"Specify a value to set the key '%s' to.",
|
||||
$key));
|
||||
if ($is_stdin) {
|
||||
if (count($argv) > 1) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Too many arguments: expected only a key when using "--stdin".'));
|
||||
}
|
||||
|
||||
fprintf(STDERR, tsprintf("%s\n", pht('Reading value from stdin...')));
|
||||
$value = file_get_contents('php://stdin');
|
||||
} else {
|
||||
if (count($argv) == 1) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
"Specify a value to set the key '%s' to.",
|
||||
$key));
|
||||
}
|
||||
|
||||
if (count($argv) > 2) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Too many arguments: expected one key and one value.'));
|
||||
}
|
||||
|
||||
$value = $argv[1];
|
||||
}
|
||||
|
||||
$value = $argv[1];
|
||||
|
||||
if (count($argv) > 2) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Too many arguments: expected one key and one value.'));
|
||||
}
|
||||
|
||||
$options = PhabricatorApplicationConfigOptions::loadAllOptions();
|
||||
if (empty($options[$key])) {
|
||||
|
|
|
@ -66,7 +66,9 @@ of each approach are:
|
|||
received a similar message, but can not prevent all stray email arising
|
||||
from "Reply All".
|
||||
- Not supported with a private reply-to address.
|
||||
- Mails are sent in the server default translation.
|
||||
- Mail messages are sent in the server default translation.
|
||||
- Mail that must be delivered over secure channels will leak the recipient
|
||||
list in the "To" and "Cc" headers.
|
||||
- One mail to each user:
|
||||
- Policy controls work correctly and are enforced per-user.
|
||||
- Recipients need to look in the mail body to see To/Cc.
|
||||
|
@ -77,7 +79,7 @@ of each approach are:
|
|||
- "Reply All" will never send extra mail to other users involved in the
|
||||
thread.
|
||||
- Required if private reply-to addresses are configured.
|
||||
- Mails are sent in the language of user preference.
|
||||
- Mail messages are sent in the language of user preference.
|
||||
|
||||
EODOC
|
||||
));
|
||||
|
@ -138,24 +140,19 @@ EODOC
|
|||
,
|
||||
'metamta.public-replies'));
|
||||
|
||||
$adapter_doc_href = PhabricatorEnv::getDoclink(
|
||||
'Configuring Outbound Email');
|
||||
$adapter_doc_name = pht('Configuring Outbound Email');
|
||||
$adapter_description = $this->deformat(pht(<<<EODOC
|
||||
Adapter class to use to transmit mail to the MTA. The default uses
|
||||
PHPMailerLite, which will invoke "sendmail". This is appropriate if sendmail
|
||||
actually works on your host, but if you haven't configured mail it may not be so
|
||||
great. A number of other mailers are available (e.g., SES, SendGrid, SMTP,
|
||||
custom mailers) - consult [[ %s | %s ]] for details.
|
||||
custom mailers). This option is deprecated in favor of 'cluster.mailers'.
|
||||
EODOC
|
||||
,
|
||||
$adapter_doc_href,
|
||||
$adapter_doc_name));
|
||||
));
|
||||
|
||||
$placeholder_description = $this->deformat(pht(<<<EODOC
|
||||
When sending a message that has no To recipient (i.e. all recipients are CC'd,
|
||||
for example when multiplexing mail), set the To field to the following value. If
|
||||
no value is set, messages with no To will have their CCs upgraded to To.
|
||||
When sending a message that has no To recipient (i.e. all recipients are CC'd),
|
||||
set the To field to the following value. If no value is set, messages with no
|
||||
To will have their CCs upgraded to To.
|
||||
EODOC
|
||||
));
|
||||
|
||||
|
@ -197,7 +194,18 @@ The default is `full`.
|
|||
EODOC
|
||||
));
|
||||
|
||||
$mailers_description = $this->deformat(pht(<<<EODOC
|
||||
Define one or more mail transmission services. For help with configuring
|
||||
mailers, see **[[ %s | %s ]]** in the documentation.
|
||||
EODOC
|
||||
,
|
||||
PhabricatorEnv::getDoclink('Configuring Outbound Email'),
|
||||
pht('Configuring Outbound Email')));
|
||||
|
||||
return array(
|
||||
$this->newOption('cluster.mailers', 'cluster.mailers', null)
|
||||
->setHidden(true)
|
||||
->setDescription($mailers_description),
|
||||
$this->newOption(
|
||||
'metamta.default-address',
|
||||
'string',
|
||||
|
|
|
@ -227,11 +227,9 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
|
|||
'%s sent you a message.',
|
||||
$this->getActor()->getUserName());
|
||||
}
|
||||
$phid = $object->getPHID();
|
||||
|
||||
return id(new PhabricatorMetaMTAMail())
|
||||
->setSubject("Z{$id}: {$title}")
|
||||
->addHeader('Thread-Topic', "Z{$id}: {$phid}");
|
||||
->setSubject("Z{$id}: {$title}");
|
||||
}
|
||||
|
||||
protected function getMailTo(PhabricatorLiskDAO $object) {
|
||||
|
|
|
@ -45,8 +45,7 @@ final class PhabricatorCountdownEditor
|
|||
$name = $object->getTitle();
|
||||
|
||||
return id(new PhabricatorMetaMTAMail())
|
||||
->setSubject("{$monogram}: {$name}")
|
||||
->addHeader('Thread-Topic', $monogram);
|
||||
->setSubject("{$monogram}: {$name}");
|
||||
}
|
||||
|
||||
protected function buildMailBody(
|
||||
|
|
|
@ -632,6 +632,8 @@ final class DifferentialTransactionEditor
|
|||
}
|
||||
|
||||
protected function getMailTo(PhabricatorLiskDAO $object) {
|
||||
$this->requireReviewers($object);
|
||||
|
||||
$phids = array();
|
||||
$phids[] = $object->getAuthorPHID();
|
||||
foreach ($object->getReviewers() as $reviewer) {
|
||||
|
@ -644,6 +646,20 @@ final class DifferentialTransactionEditor
|
|||
return $phids;
|
||||
}
|
||||
|
||||
protected function newMailUnexpandablePHIDs(PhabricatorLiskDAO $object) {
|
||||
$this->requireReviewers($object);
|
||||
|
||||
$phids = array();
|
||||
|
||||
foreach ($object->getReviewers() as $reviewer) {
|
||||
if ($reviewer->isResigned()) {
|
||||
$phids[] = $reviewer->getReviewerPHID();
|
||||
}
|
||||
}
|
||||
|
||||
return $phids;
|
||||
}
|
||||
|
||||
protected function getMailAction(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
|
@ -689,15 +705,10 @@ final class DifferentialTransactionEditor
|
|||
protected function buildMailTemplate(PhabricatorLiskDAO $object) {
|
||||
$id = $object->getID();
|
||||
$title = $object->getTitle();
|
||||
|
||||
$original_title = $object->getOriginalTitle();
|
||||
|
||||
$subject = "D{$id}: {$title}";
|
||||
$thread_topic = "D{$id}: {$original_title}";
|
||||
|
||||
return id(new PhabricatorMetaMTAMail())
|
||||
->setSubject($subject)
|
||||
->addHeader('Thread-Topic', $thread_topic);
|
||||
->setSubject($subject);
|
||||
}
|
||||
|
||||
protected function getTransactionsForMail(
|
||||
|
@ -1730,4 +1741,25 @@ final class DifferentialTransactionEditor
|
|||
}
|
||||
}
|
||||
|
||||
private function requireReviewers(DifferentialRevision $revision) {
|
||||
if ($revision->hasAttachedReviewers()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$with_reviewers = id(new DifferentialRevisionQuery())
|
||||
->setViewer($this->getActor())
|
||||
->needReviewers(true)
|
||||
->withPHIDs(array($revision->getPHID()))
|
||||
->executeOne();
|
||||
if (!$with_reviewers) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Failed to reload revision ("%s").',
|
||||
$revision->getPHID()));
|
||||
}
|
||||
|
||||
$revision->attachReviewers($with_reviewers->getReviewers());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
|
||||
final class DifferentialMailEngineExtension
|
||||
extends PhabricatorMailEngineExtension {
|
||||
|
||||
const EXTENSIONKEY = 'differential';
|
||||
|
||||
public function supportsObject($object) {
|
||||
return ($object instanceof DifferentialRevision);
|
||||
}
|
||||
|
||||
public function newMailStampTemplates($object) {
|
||||
return array(
|
||||
id(new PhabricatorPHIDMailStamp())
|
||||
->setKey('author')
|
||||
->setLabel(pht('Author')),
|
||||
id(new PhabricatorPHIDMailStamp())
|
||||
->setKey('reviewer')
|
||||
->setLabel(pht('Reviewer')),
|
||||
id(new PhabricatorPHIDMailStamp())
|
||||
->setKey('blocking-reviewer')
|
||||
->setLabel(pht('Reviewer')),
|
||||
id(new PhabricatorPHIDMailStamp())
|
||||
->setKey('resigned-reviewer')
|
||||
->setLabel(pht('Reviewer')),
|
||||
id(new PhabricatorPHIDMailStamp())
|
||||
->setKey('revision-repository')
|
||||
->setLabel(pht('Revision Repository')),
|
||||
id(new PhabricatorPHIDMailStamp())
|
||||
->setKey('revision-status')
|
||||
->setLabel(pht('Revision Status')),
|
||||
);
|
||||
}
|
||||
|
||||
public function newMailStamps($object, array $xactions) {
|
||||
$editor = $this->getEditor();
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$revision = id(new DifferentialRevisionQuery())
|
||||
->setViewer($viewer)
|
||||
->needReviewers(true)
|
||||
->withPHIDs(array($object->getPHID()))
|
||||
->executeOne();
|
||||
|
||||
$reviewers = array();
|
||||
$blocking = array();
|
||||
$resigned = array();
|
||||
foreach ($revision->getReviewers() as $reviewer) {
|
||||
$reviewer_phid = $reviewer->getReviewerPHID();
|
||||
|
||||
if ($reviewer->isResigned()) {
|
||||
$resigned[] = $reviewer_phid;
|
||||
} else {
|
||||
$reviewers[] = $reviewer_phid;
|
||||
if ($reviewer->isBlocking()) {
|
||||
$reviewers[] = $blocking;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->getMailStamp('author')
|
||||
->setValue($revision->getAuthorPHID());
|
||||
|
||||
$this->getMailStamp('reviewer')
|
||||
->setValue($reviewers);
|
||||
|
||||
$this->getMailStamp('blocking-reviewer')
|
||||
->setValue($blocking);
|
||||
|
||||
$this->getMailStamp('resigned-reviewer')
|
||||
->setValue($resigned);
|
||||
|
||||
$this->getMailStamp('revision-repository')
|
||||
->setValue($revision->getRepositoryPHID());
|
||||
|
||||
$this->getMailStamp('revision-status')
|
||||
->setValue($revision->getModernRevisionStatus());
|
||||
}
|
||||
|
||||
}
|
|
@ -221,6 +221,51 @@ final class DifferentialChangeset
|
|||
return $this->assertAttached($this->diff);
|
||||
}
|
||||
|
||||
public function newFileTreeIcon() {
|
||||
$file_type = $this->getFileType();
|
||||
$change_type = $this->getChangeType();
|
||||
|
||||
$change_icons = array(
|
||||
DifferentialChangeType::TYPE_DELETE => 'fa-file-o',
|
||||
);
|
||||
|
||||
if (isset($change_icons[$change_type])) {
|
||||
$icon = $change_icons[$change_type];
|
||||
} else {
|
||||
$icon = DifferentialChangeType::getIconForFileType($file_type);
|
||||
}
|
||||
|
||||
$change_colors = array(
|
||||
DifferentialChangeType::TYPE_ADD => 'green',
|
||||
DifferentialChangeType::TYPE_DELETE => 'red',
|
||||
DifferentialChangeType::TYPE_MOVE_AWAY => 'orange',
|
||||
DifferentialChangeType::TYPE_MOVE_HERE => 'orange',
|
||||
DifferentialChangeType::TYPE_COPY_HERE => 'orange',
|
||||
DifferentialChangeType::TYPE_MULTICOPY => 'orange',
|
||||
);
|
||||
|
||||
$color = idx($change_colors, $change_type, 'bluetext');
|
||||
|
||||
return id(new PHUIIconView())
|
||||
->setIcon($icon.' '.$color);
|
||||
}
|
||||
|
||||
public function getFileTreeClass() {
|
||||
switch ($this->getChangeType()) {
|
||||
case DifferentialChangeType::TYPE_ADD:
|
||||
return 'filetree-added';
|
||||
case DifferentialChangeType::TYPE_DELETE:
|
||||
return 'filetree-deleted';
|
||||
case DifferentialChangeType::TYPE_MOVE_AWAY:
|
||||
case DifferentialChangeType::TYPE_MOVE_HERE:
|
||||
case DifferentialChangeType::TYPE_COPY_HERE:
|
||||
case DifferentialChangeType::TYPE_MULTICOPY:
|
||||
return 'filetree-movecopy';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
|
|
|
@ -69,6 +69,11 @@ final class DifferentialReviewer
|
|||
return ($this->getReviewerStatus() == $status_resigned);
|
||||
}
|
||||
|
||||
public function isBlocking() {
|
||||
$status_blocking = DifferentialReviewerStatus::STATUS_BLOCKING;
|
||||
return ($this->getReviewerStatus() == $status_blocking);
|
||||
}
|
||||
|
||||
public function isRejected($diff_phid) {
|
||||
$status_rejected = DifferentialReviewerStatus::STATUS_REJECTED;
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@ final class DifferentialRevision extends DifferentialDAO
|
|||
PhabricatorDraftInterface {
|
||||
|
||||
protected $title = '';
|
||||
protected $originalTitle;
|
||||
protected $status;
|
||||
|
||||
protected $summary = '';
|
||||
|
@ -98,7 +97,6 @@ final class DifferentialRevision extends DifferentialDAO
|
|||
),
|
||||
self::CONFIG_COLUMN_SCHEMA => array(
|
||||
'title' => 'text255',
|
||||
'originalTitle' => 'text255',
|
||||
'status' => 'text32',
|
||||
'summary' => 'text',
|
||||
'testPlan' => 'text',
|
||||
|
@ -155,14 +153,6 @@ final class DifferentialRevision extends DifferentialDAO
|
|||
return '/'.$this->getMonogram();
|
||||
}
|
||||
|
||||
public function setTitle($title) {
|
||||
$this->title = $title;
|
||||
if (!$this->getID()) {
|
||||
$this->originalTitle = $title;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function loadIDsByCommitPHIDs($phids) {
|
||||
if (!$phids) {
|
||||
return array();
|
||||
|
@ -593,6 +583,10 @@ final class DifferentialRevision extends DifferentialDAO
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function hasAttachedReviewers() {
|
||||
return ($this->reviewerStatus !== self::ATTACHABLE);
|
||||
}
|
||||
|
||||
public function getReviewerPHIDs() {
|
||||
$reviewers = $this->getReviewers();
|
||||
return mpull($reviewers, 'getReviewerPHID');
|
||||
|
@ -830,9 +824,15 @@ final class DifferentialRevision extends DifferentialDAO
|
|||
}
|
||||
|
||||
foreach ($reviewers as $reviewer) {
|
||||
if ($reviewer->getReviewerPHID() == $phid) {
|
||||
return true;
|
||||
if ($reviewer->getReviewerPHID() !== $phid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($reviewer->isResigned()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -206,6 +206,7 @@ final class DifferentialChangesetDetailView extends AphrontView {
|
|||
'displayPath' => hsprintf('%s', $display_parts),
|
||||
'path' => $display_filename,
|
||||
'icon' => $display_icon,
|
||||
'treeNodeID' => 'tree-node-'.$changeset->getAnchorName(),
|
||||
),
|
||||
'class' => $class,
|
||||
'id' => $id,
|
||||
|
|
|
@ -83,6 +83,9 @@ final class DifferentialChangesetFileTreeSideNavBuilder extends Phobject {
|
|||
while (($path = $path->getNextNode())) {
|
||||
$data = $path->getData();
|
||||
|
||||
$classes = array();
|
||||
$classes[] = 'phabricator-filetree-item';
|
||||
|
||||
$name = $path->getName();
|
||||
$style = 'padding-left: '.(2 + (3 * $path->getDepth())).'px';
|
||||
|
||||
|
@ -90,13 +93,23 @@ final class DifferentialChangesetFileTreeSideNavBuilder extends Phobject {
|
|||
if ($data) {
|
||||
$href = '#'.$data->getAnchorName();
|
||||
$title = $name;
|
||||
$icon = id(new PHUIIconView())
|
||||
->setIcon('fa-file-text-o bluetext');
|
||||
|
||||
$icon = $data->newFileTreeIcon();
|
||||
$classes[] = $data->getFileTreeClass();
|
||||
|
||||
$count = phutil_tag(
|
||||
'span',
|
||||
array(
|
||||
'class' => 'filetree-progress-hint',
|
||||
'id' => 'tree-node-'.$data->getAnchorName(),
|
||||
));
|
||||
} else {
|
||||
$name .= '/';
|
||||
$title = $path->getFullPath().'/';
|
||||
$icon = id(new PHUIIconView())
|
||||
->setIcon('fa-folder-open blue');
|
||||
|
||||
$count = null;
|
||||
}
|
||||
|
||||
$name_element = phutil_tag(
|
||||
|
@ -106,15 +119,16 @@ final class DifferentialChangesetFileTreeSideNavBuilder extends Phobject {
|
|||
),
|
||||
$name);
|
||||
|
||||
|
||||
$filetree[] = javelin_tag(
|
||||
$href ? 'a' : 'span',
|
||||
array(
|
||||
'href' => $href,
|
||||
'style' => $style,
|
||||
'title' => $title,
|
||||
'class' => 'phabricator-filetree-item',
|
||||
'class' => implode(' ', $classes),
|
||||
),
|
||||
array($icon, $name_element));
|
||||
array($count, $icon, $name_element));
|
||||
}
|
||||
$tree->destroy();
|
||||
|
||||
|
|
|
@ -37,7 +37,11 @@ final class DiffusionQueryPathsConduitAPIMethod
|
|||
$commit = $request->getValue('commit');
|
||||
$repository = $drequest->getRepository();
|
||||
|
||||
// http://comments.gmane.org/gmane.comp.version-control.git/197735
|
||||
// Recent versions of Git don't work if you pass the empty string, and
|
||||
// require "." to list everything.
|
||||
if (!strlen($path)) {
|
||||
$path = '.';
|
||||
}
|
||||
|
||||
$future = $repository->getLocalCommandFuture(
|
||||
'ls-tree --name-only -r -z %s -- %s',
|
||||
|
|
|
@ -23,14 +23,10 @@ final class DiffusionRepositoryURIViewController
|
|||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
// For display, reload the URI by loading it through the repository. This
|
||||
// For display, access the URI by loading it through the repository. This
|
||||
// may adjust builtin URIs for repository configuration, so we may end up
|
||||
// with a different view of builtin URIs than we'd see if we loaded them
|
||||
// directly from the database. See T12884.
|
||||
$repository_with_uris = id(new PhabricatorRepositoryQuery())
|
||||
->setViewer($viewer)
|
||||
->needURIs(true)
|
||||
->execute();
|
||||
|
||||
$repository_uris = $repository->getURIs();
|
||||
$repository_uris = mpull($repository_uris, null, 'getID');
|
||||
|
|
|
@ -297,7 +297,11 @@ final class DiffusionCommitHookEngine extends Phobject {
|
|||
return;
|
||||
}
|
||||
|
||||
$adapter_template->setHookEngine($this);
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$adapter_template
|
||||
->setHookEngine($this)
|
||||
->setActingAsPHID($viewer->getPHID());
|
||||
|
||||
$engine = new HeraldEngine();
|
||||
$rules = null;
|
||||
|
|
|
@ -47,8 +47,7 @@ final class PhabricatorFileEditor
|
|||
$name = $object->getName();
|
||||
|
||||
return id(new PhabricatorMetaMTAMail())
|
||||
->setSubject("F{$id}: {$name}")
|
||||
->addHeader('Thread-Topic', "F{$id}");
|
||||
->setSubject("F{$id}: {$name}");
|
||||
}
|
||||
|
||||
protected function buildMailBody(
|
||||
|
|
|
@ -50,8 +50,7 @@ final class FundInitiativeEditor
|
|||
$name = $object->getName();
|
||||
|
||||
return id(new PhabricatorMetaMTAMail())
|
||||
->setSubject("{$monogram}: {$name}")
|
||||
->addHeader('Thread-Topic', $monogram);
|
||||
->setSubject("{$monogram}: {$name}");
|
||||
}
|
||||
|
||||
protected function buildMailBody(
|
||||
|
|
62
src/applications/herald/action/HeraldCallWebhookAction.php
Normal file
62
src/applications/herald/action/HeraldCallWebhookAction.php
Normal file
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
final class HeraldCallWebhookAction extends HeraldAction {
|
||||
|
||||
const ACTIONCONST = 'webhook';
|
||||
const DO_WEBHOOK = 'do.call-webhook';
|
||||
|
||||
public function getHeraldActionName() {
|
||||
return pht('Call webhooks');
|
||||
}
|
||||
|
||||
public function getActionGroupKey() {
|
||||
return HeraldUtilityActionGroup::ACTIONGROUPKEY;
|
||||
}
|
||||
|
||||
public function supportsObject($object) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function supportsRuleType($rule_type) {
|
||||
return ($rule_type !== HeraldRuleTypeConfig::RULE_TYPE_PERSONAL);
|
||||
}
|
||||
|
||||
public function applyEffect($object, HeraldEffect $effect) {
|
||||
$adapter = $this->getAdapter();
|
||||
$rule = $effect->getRule();
|
||||
$target = $effect->getTarget();
|
||||
|
||||
foreach ($target as $webhook_phid) {
|
||||
$adapter->queueWebhook($webhook_phid, $rule->getPHID());
|
||||
}
|
||||
|
||||
$this->logEffect(self::DO_WEBHOOK, $target);
|
||||
}
|
||||
|
||||
public function getHeraldActionStandardType() {
|
||||
return self::STANDARD_PHID_LIST;
|
||||
}
|
||||
|
||||
protected function getActionEffectMap() {
|
||||
return array(
|
||||
self::DO_WEBHOOK => array(
|
||||
'icon' => 'fa-cloud-upload',
|
||||
'color' => 'green',
|
||||
'name' => pht('Called Webhooks'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function renderActionDescription($value) {
|
||||
return pht('Call webhooks: %s.', $this->renderHandleList($value));
|
||||
}
|
||||
|
||||
protected function renderActionEffectDescription($type, $data) {
|
||||
return pht('Called webhooks: %s.', $this->renderHandleList($data));
|
||||
}
|
||||
|
||||
protected function getDatasource() {
|
||||
return new HeraldWebhookDatasource();
|
||||
}
|
||||
|
||||
}
|
|
@ -39,6 +39,9 @@ abstract class HeraldAdapter extends Phobject {
|
|||
private $edgeCache = array();
|
||||
private $forbiddenActions = array();
|
||||
private $viewer;
|
||||
private $mustEncryptReasons = array();
|
||||
private $actingAsPHID;
|
||||
private $webhookMap = array();
|
||||
|
||||
public function getEmailPHIDs() {
|
||||
return array_values($this->emailPHIDs);
|
||||
|
@ -48,6 +51,15 @@ abstract class HeraldAdapter extends Phobject {
|
|||
return array_values($this->forcedEmailPHIDs);
|
||||
}
|
||||
|
||||
final public function setActingAsPHID($acting_as_phid) {
|
||||
$this->actingAsPHID = $acting_as_phid;
|
||||
return $this;
|
||||
}
|
||||
|
||||
final public function getActingAsPHID() {
|
||||
return $this->actingAsPHID;
|
||||
}
|
||||
|
||||
public function addEmailPHID($phid, $force) {
|
||||
$this->emailPHIDs[$phid] = $phid;
|
||||
if ($force) {
|
||||
|
@ -1182,4 +1194,30 @@ abstract class HeraldAdapter extends Phobject {
|
|||
return $this->forbiddenActions[$action];
|
||||
}
|
||||
|
||||
|
||||
/* -( Must Encrypt )------------------------------------------------------- */
|
||||
|
||||
|
||||
final public function addMustEncryptReason($reason) {
|
||||
$this->mustEncryptReasons[] = $reason;
|
||||
return $this;
|
||||
}
|
||||
|
||||
final public function getMustEncryptReasons() {
|
||||
return $this->mustEncryptReasons;
|
||||
}
|
||||
|
||||
|
||||
/* -( Webhooks )----------------------------------------------------------- */
|
||||
|
||||
|
||||
final public function queueWebhook($webhook_phid, $rule_phid) {
|
||||
$this->webhookMap[$webhook_phid][] = $rule_phid;
|
||||
return $this;
|
||||
}
|
||||
|
||||
final public function getWebhookMap() {
|
||||
return $this->webhookMap;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -28,6 +28,10 @@ final class PhabricatorHeraldApplication extends PhabricatorApplication {
|
|||
'name' => pht('Herald User Guide'),
|
||||
'href' => PhabricatorEnv::getDoclink('Herald User Guide'),
|
||||
),
|
||||
array(
|
||||
'name' => pht('User Guide: Webhooks'),
|
||||
'href' => PhabricatorEnv::getDoclink('User Guide: Webhooks'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -62,6 +66,15 @@ final class PhabricatorHeraldApplication extends PhabricatorApplication {
|
|||
'(?P<id>[1-9]\d*)/'
|
||||
=> 'HeraldTranscriptController',
|
||||
),
|
||||
'webhook/' => array(
|
||||
$this->getQueryRoutePattern() => 'HeraldWebhookListController',
|
||||
'view/(?P<id>\d+)/(?:request/(?P<requestID>[^/]+)/)?' =>
|
||||
'HeraldWebhookViewController',
|
||||
$this->getEditRoutePattern('edit/') => 'HeraldWebhookEditController',
|
||||
'test/(?P<id>\d+)/' => 'HeraldWebhookTestController',
|
||||
'key/(?P<action>view|cycle)/(?P<id>\d+)/' =>
|
||||
'HeraldWebhookKeyController',
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -72,6 +85,9 @@ final class PhabricatorHeraldApplication extends PhabricatorApplication {
|
|||
'caption' => pht('Global rules can bypass access controls.'),
|
||||
'default' => PhabricatorPolicies::POLICY_ADMIN,
|
||||
),
|
||||
HeraldCreateWebhooksCapability::CAPABILITY => array(
|
||||
'default' => PhabricatorPolicies::POLICY_ADMIN,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
final class HeraldCreateWebhooksCapability
|
||||
extends PhabricatorPolicyCapability {
|
||||
|
||||
const CAPABILITY = 'herald.webhooks';
|
||||
|
||||
public function getCapabilityName() {
|
||||
return pht('Can Create Webhooks');
|
||||
}
|
||||
|
||||
public function describeCapabilityRejection() {
|
||||
return pht('You do not have permission to create webhooks.');
|
||||
}
|
||||
|
||||
}
|
|
@ -6,18 +6,6 @@ abstract class HeraldController extends PhabricatorController {
|
|||
return $this->buildSideNavView()->getMenu();
|
||||
}
|
||||
|
||||
protected function buildApplicationCrumbs() {
|
||||
$crumbs = parent::buildApplicationCrumbs();
|
||||
|
||||
$crumbs->addAction(
|
||||
id(new PHUIListItemView())
|
||||
->setName(pht('Create Herald Rule'))
|
||||
->setHref($this->getApplicationURI('create/'))
|
||||
->setIcon('fa-plus-square'));
|
||||
|
||||
return $crumbs;
|
||||
}
|
||||
|
||||
public function buildSideNavView() {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
|
@ -29,8 +17,11 @@ abstract class HeraldController extends PhabricatorController {
|
|||
->addNavigationItems($nav->getMenu());
|
||||
|
||||
$nav->addLabel(pht('Utilities'))
|
||||
->addFilter('test', pht('Test Console'))
|
||||
->addFilter('transcript', pht('Transcripts'));
|
||||
->addFilter('test', pht('Test Console'))
|
||||
->addFilter('transcript', pht('Transcripts'));
|
||||
|
||||
$nav->addLabel(pht('Webhooks'))
|
||||
->addFilter('webhook', pht('Webhooks'));
|
||||
|
||||
$nav->selectFilter(null);
|
||||
|
||||
|
|
|
@ -17,5 +17,16 @@ final class HeraldRuleListController extends HeraldController {
|
|||
return $this->delegateToController($controller);
|
||||
}
|
||||
|
||||
protected function buildApplicationCrumbs() {
|
||||
$crumbs = parent::buildApplicationCrumbs();
|
||||
|
||||
$crumbs->addAction(
|
||||
id(new PHUIListItemView())
|
||||
->setName(pht('Create Herald Rule'))
|
||||
->setHref($this->getApplicationURI('create/'))
|
||||
->setIcon('fa-plus-square'));
|
||||
|
||||
return $crumbs;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ final class HeraldTestConsoleController extends HeraldController {
|
|||
|
||||
$adapter
|
||||
->setIsNewObject(false)
|
||||
->setActingAsPHID($viewer->getPHID())
|
||||
->setViewer($viewer);
|
||||
|
||||
$rules = id(new HeraldRuleQuery())
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
abstract class HeraldWebhookController extends HeraldController {
|
||||
|
||||
protected function buildApplicationCrumbs() {
|
||||
$crumbs = parent::buildApplicationCrumbs();
|
||||
|
||||
$crumbs->addTextCrumb(
|
||||
pht('Webhooks'),
|
||||
$this->getApplicationURI('webhook/'));
|
||||
|
||||
return $crumbs;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
final class HeraldWebhookEditController
|
||||
extends HeraldWebhookController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
return id(new HeraldWebhookEditEngine())
|
||||
->setController($this)
|
||||
->buildResponse();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
final class HeraldWebhookKeyController
|
||||
extends HeraldWebhookController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$hook = id(new HeraldWebhookQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($request->getURIData('id')))
|
||||
->requireCapabilities(
|
||||
array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
))
|
||||
->executeOne();
|
||||
if (!$hook) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$action = $request->getURIData('action');
|
||||
if ($action === 'cycle') {
|
||||
if (!$request->isFormPost()) {
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Regenerate HMAC Key'))
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'Regenerate the HMAC key used to sign requests made by this '.
|
||||
'webhook?'))
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'Requests which are currently authenticated with the old key '.
|
||||
'may fail.'))
|
||||
->addCancelButton($hook->getURI())
|
||||
->addSubmitButton(pht('Regnerate Key'));
|
||||
} else {
|
||||
$hook->regenerateHMACKey()->save();
|
||||
}
|
||||
}
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setViewer($viewer)
|
||||
->appendControl(
|
||||
id(new AphrontFormTextControl())
|
||||
->setLabel(pht('HMAC Key'))
|
||||
->setValue($hook->getHMACKey()));
|
||||
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Webhook HMAC Key'))
|
||||
->appendForm($form)
|
||||
->addCancelButton($hook->getURI(), pht('Done'));
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
final class HeraldWebhookListController
|
||||
extends HeraldWebhookController {
|
||||
|
||||
public function shouldAllowPublic() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
return id(new HeraldWebhookSearchEngine())
|
||||
->setController($this)
|
||||
->buildResponse();
|
||||
}
|
||||
|
||||
protected function buildApplicationCrumbs() {
|
||||
$crumbs = parent::buildApplicationCrumbs();
|
||||
|
||||
id(new HeraldWebhookEditEngine())
|
||||
->setViewer($this->getViewer())
|
||||
->addActionToCrumbs($crumbs);
|
||||
|
||||
return $crumbs;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
|
||||
final class HeraldWebhookTestController
|
||||
extends HeraldWebhookController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$hook = id(new HeraldWebhookQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($request->getURIData('id')))
|
||||
->requireCapabilities(
|
||||
array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
))
|
||||
->executeOne();
|
||||
if (!$hook) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$v_object = null;
|
||||
$e_object = null;
|
||||
$errors = array();
|
||||
if ($request->isFormPost()) {
|
||||
|
||||
$v_object = $request->getStr('object');
|
||||
if (!strlen($v_object)) {
|
||||
$object = $hook;
|
||||
} else {
|
||||
$objects = id(new PhabricatorObjectQuery())
|
||||
->setViewer($viewer)
|
||||
->withNames(array($v_object))
|
||||
->execute();
|
||||
if ($objects) {
|
||||
$object = head($objects);
|
||||
} else {
|
||||
$e_object = pht('Invalid');
|
||||
$errors[] = pht('Specified object could not be loaded.');
|
||||
}
|
||||
}
|
||||
|
||||
if (!$errors) {
|
||||
$xaction_query =
|
||||
PhabricatorApplicationTransactionQuery::newQueryForObject($object);
|
||||
|
||||
$xactions = $xaction_query
|
||||
->withObjectPHIDs(array($object->getPHID()))
|
||||
->setViewer($viewer)
|
||||
->setLimit(10)
|
||||
->execute();
|
||||
|
||||
$request = HeraldWebhookRequest::initializeNewWebhookRequest($hook)
|
||||
->setObjectPHID($object->getPHID())
|
||||
->setTriggerPHIDs(array($viewer->getPHID()))
|
||||
->setIsTestAction(true)
|
||||
->setTransactionPHIDs(mpull($xactions, 'getPHID'))
|
||||
->save();
|
||||
|
||||
$request->queueCall();
|
||||
|
||||
$next_uri = $hook->getURI().'request/'.$request->getID().'/';
|
||||
|
||||
return id(new AphrontRedirectResponse())->setURI($next_uri);
|
||||
}
|
||||
}
|
||||
|
||||
$instructions = <<<EOREMARKUP
|
||||
Optionally, choose an object to generate test data for (like `D123` or `T234`).
|
||||
|
||||
The 10 most recent transactions for the object will be submitted to the webhook.
|
||||
EOREMARKUP;
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setViewer($viewer)
|
||||
->appendControl(
|
||||
id(new AphrontFormTextControl())
|
||||
->setLabel(pht('Object'))
|
||||
->setName('object')
|
||||
->setError($e_object)
|
||||
->setValue($v_object));
|
||||
|
||||
return $this->newDialog()
|
||||
->setErrors($errors)
|
||||
->setWidth(AphrontDialogView::WIDTH_FORM)
|
||||
->setTitle(pht('New Test Request'))
|
||||
->appendParagraph(new PHUIRemarkupView($viewer, $instructions))
|
||||
->appendForm($form)
|
||||
->addCancelButton($hook->getURI())
|
||||
->addSubmitButton(pht('Test Webhook'));
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,184 @@
|
|||
<?php
|
||||
|
||||
final class HeraldWebhookViewController
|
||||
extends HeraldWebhookController {
|
||||
|
||||
public function shouldAllowPublic() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$hook = id(new HeraldWebhookQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($request->getURIData('id')))
|
||||
->executeOne();
|
||||
if (!$hook) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$header = $this->buildHeaderView($hook);
|
||||
|
||||
$warnings = null;
|
||||
if ($hook->isInErrorBackoff($viewer)) {
|
||||
$message = pht(
|
||||
'Many requests to this webhook have failed recently (at least %s '.
|
||||
'errors in the last %s seconds). New requests are temporarily paused.',
|
||||
$hook->getErrorBackoffThreshold(),
|
||||
$hook->getErrorBackoffWindow());
|
||||
|
||||
$warnings = id(new PHUIInfoView())
|
||||
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
|
||||
->setErrors(
|
||||
array(
|
||||
$message,
|
||||
));
|
||||
}
|
||||
|
||||
$curtain = $this->buildCurtain($hook);
|
||||
$properties_view = $this->buildPropertiesView($hook);
|
||||
|
||||
$timeline = $this->buildTransactionTimeline(
|
||||
$hook,
|
||||
new HeraldWebhookTransactionQuery());
|
||||
$timeline->setShouldTerminate(true);
|
||||
|
||||
$requests = id(new HeraldWebhookRequestQuery())
|
||||
->setViewer($viewer)
|
||||
->withWebhookPHIDs(array($hook->getPHID()))
|
||||
->setLimit(20)
|
||||
->execute();
|
||||
|
||||
$requests_table = id(new HeraldWebhookRequestListView())
|
||||
->setViewer($viewer)
|
||||
->setRequests($requests)
|
||||
->setHighlightID($request->getURIData('requestID'));
|
||||
|
||||
$requests_view = id(new PHUIObjectBoxView())
|
||||
->setHeaderText(pht('Recent Requests'))
|
||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||
->setTable($requests_table);
|
||||
|
||||
$hook_view = id(new PHUITwoColumnView())
|
||||
->setHeader($header)
|
||||
->setMainColumn(
|
||||
array(
|
||||
$warnings,
|
||||
$properties_view,
|
||||
$requests_view,
|
||||
$timeline,
|
||||
))
|
||||
->setCurtain($curtain);
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs()
|
||||
->addTextCrumb(pht('Webhook %d', $hook->getID()))
|
||||
->setBorder(true);
|
||||
|
||||
return $this->newPage()
|
||||
->setTitle(
|
||||
array(
|
||||
pht('Webhook %d', $hook->getID()),
|
||||
$hook->getName(),
|
||||
))
|
||||
->setCrumbs($crumbs)
|
||||
->setPageObjectPHIDs(
|
||||
array(
|
||||
$hook->getPHID(),
|
||||
))
|
||||
->appendChild($hook_view);
|
||||
}
|
||||
|
||||
private function buildHeaderView(HeraldWebhook $hook) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$title = $hook->getName();
|
||||
|
||||
$status_icon = $hook->getStatusIcon();
|
||||
$status_color = $hook->getStatusColor();
|
||||
$status_name = $hook->getStatusDisplayName();
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader($title)
|
||||
->setViewer($viewer)
|
||||
->setPolicyObject($hook)
|
||||
->setStatus($status_icon, $status_color, $status_name)
|
||||
->setHeaderIcon('fa-cloud-upload');
|
||||
|
||||
return $header;
|
||||
}
|
||||
|
||||
|
||||
private function buildCurtain(HeraldWebhook $hook) {
|
||||
$viewer = $this->getViewer();
|
||||
$curtain = $this->newCurtainView($hook);
|
||||
|
||||
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||
$viewer,
|
||||
$hook,
|
||||
PhabricatorPolicyCapability::CAN_EDIT);
|
||||
|
||||
$id = $hook->getID();
|
||||
$edit_uri = $this->getApplicationURI("webhook/edit/{$id}/");
|
||||
$test_uri = $this->getApplicationURI("webhook/test/{$id}/");
|
||||
|
||||
$key_view_uri = $this->getApplicationURI("webhook/key/view/{$id}/");
|
||||
$key_cycle_uri = $this->getApplicationURI("webhook/key/cycle/{$id}/");
|
||||
|
||||
$curtain->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Edit Webhook'))
|
||||
->setIcon('fa-pencil')
|
||||
->setDisabled(!$can_edit)
|
||||
->setWorkflow(!$can_edit)
|
||||
->setHref($edit_uri));
|
||||
|
||||
$curtain->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('New Test Request'))
|
||||
->setIcon('fa-cloud-upload')
|
||||
->setDisabled(!$can_edit)
|
||||
->setWorkflow(true)
|
||||
->setHref($test_uri));
|
||||
|
||||
$curtain->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('View HMAC Key'))
|
||||
->setIcon('fa-key')
|
||||
->setDisabled(!$can_edit)
|
||||
->setWorkflow(true)
|
||||
->setHref($key_view_uri));
|
||||
|
||||
$curtain->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Regenerate HMAC Key'))
|
||||
->setIcon('fa-refresh')
|
||||
->setDisabled(!$can_edit)
|
||||
->setWorkflow(true)
|
||||
->setHref($key_cycle_uri));
|
||||
|
||||
return $curtain;
|
||||
}
|
||||
|
||||
|
||||
private function buildPropertiesView(HeraldWebhook $hook) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$properties = id(new PHUIPropertyListView())
|
||||
->setViewer($viewer);
|
||||
|
||||
$properties->addProperty(
|
||||
pht('URI'),
|
||||
$hook->getWebhookURI());
|
||||
|
||||
$properties->addProperty(
|
||||
pht('Status'),
|
||||
$hook->getStatusDisplayName());
|
||||
|
||||
return id(new PHUIObjectBoxView())
|
||||
->setHeaderText(pht('Details'))
|
||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||
->appendChild($properties);
|
||||
}
|
||||
|
||||
}
|
105
src/applications/herald/editor/HeraldWebhookEditEngine.php
Normal file
105
src/applications/herald/editor/HeraldWebhookEditEngine.php
Normal file
|
@ -0,0 +1,105 @@
|
|||
<?php
|
||||
|
||||
final class HeraldWebhookEditEngine
|
||||
extends PhabricatorEditEngine {
|
||||
|
||||
const ENGINECONST = 'herald.webhook';
|
||||
|
||||
public function isEngineConfigurable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getEngineName() {
|
||||
return pht('Webhooks');
|
||||
}
|
||||
|
||||
public function getSummaryHeader() {
|
||||
return pht('Edit Webhook Configurations');
|
||||
}
|
||||
|
||||
public function getSummaryText() {
|
||||
return pht('This engine is used to edit webhooks.');
|
||||
}
|
||||
|
||||
public function getEngineApplicationClass() {
|
||||
return 'PhabricatorHeraldApplication';
|
||||
}
|
||||
|
||||
protected function newEditableObject() {
|
||||
$viewer = $this->getViewer();
|
||||
return HeraldWebhook::initializeNewWebhook($viewer);
|
||||
}
|
||||
|
||||
protected function newObjectQuery() {
|
||||
return new HeraldWebhookQuery();
|
||||
}
|
||||
|
||||
protected function getObjectCreateTitleText($object) {
|
||||
return pht('Create Webhook');
|
||||
}
|
||||
|
||||
protected function getObjectCreateButtonText($object) {
|
||||
return pht('Create Webhook');
|
||||
}
|
||||
|
||||
protected function getObjectEditTitleText($object) {
|
||||
return pht('Edit Webhook: %s', $object->getName());
|
||||
}
|
||||
|
||||
protected function getObjectEditShortText($object) {
|
||||
return pht('Edit Webhook');
|
||||
}
|
||||
|
||||
protected function getObjectCreateShortText() {
|
||||
return pht('Create Webhook');
|
||||
}
|
||||
|
||||
protected function getObjectName() {
|
||||
return pht('Webhook');
|
||||
}
|
||||
|
||||
protected function getEditorURI() {
|
||||
return '/herald/webhook/edit/';
|
||||
}
|
||||
|
||||
protected function getObjectCreateCancelURI($object) {
|
||||
return '/herald/webhook/';
|
||||
}
|
||||
|
||||
protected function getObjectViewURI($object) {
|
||||
return $object->getURI();
|
||||
}
|
||||
|
||||
protected function getCreateNewObjectPolicy() {
|
||||
return $this->getApplication()->getPolicy(
|
||||
HeraldCreateWebhooksCapability::CAPABILITY);
|
||||
}
|
||||
|
||||
protected function buildCustomEditFields($object) {
|
||||
return array(
|
||||
id(new PhabricatorTextEditField())
|
||||
->setKey('name')
|
||||
->setLabel(pht('Name'))
|
||||
->setDescription(pht('Name of the webhook.'))
|
||||
->setTransactionType(HeraldWebhookNameTransaction::TRANSACTIONTYPE)
|
||||
->setIsRequired(true)
|
||||
->setValue($object->getName()),
|
||||
id(new PhabricatorTextEditField())
|
||||
->setKey('uri')
|
||||
->setLabel(pht('URI'))
|
||||
->setDescription(pht('URI for the webhook.'))
|
||||
->setTransactionType(HeraldWebhookURITransaction::TRANSACTIONTYPE)
|
||||
->setIsRequired(true)
|
||||
->setValue($object->getWebhookURI()),
|
||||
id(new PhabricatorSelectEditField())
|
||||
->setKey('status')
|
||||
->setLabel(pht('Status'))
|
||||
->setDescription(pht('Status mode for the webhook.'))
|
||||
->setTransactionType(HeraldWebhookStatusTransaction::TRANSACTIONTYPE)
|
||||
->setOptions(HeraldWebhook::getStatusDisplayNameMap())
|
||||
->setValue($object->getStatus()),
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
}
|
31
src/applications/herald/editor/HeraldWebhookEditor.php
Normal file
31
src/applications/herald/editor/HeraldWebhookEditor.php
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
final class HeraldWebhookEditor
|
||||
extends PhabricatorApplicationTransactionEditor {
|
||||
|
||||
public function getEditorApplicationClass() {
|
||||
return 'PhabricatorHeraldApplication';
|
||||
}
|
||||
|
||||
public function getEditorObjectsDescription() {
|
||||
return pht('Webhooks');
|
||||
}
|
||||
|
||||
public function getCreateObjectTitle($author, $object) {
|
||||
return pht('%s created this webhook.', $author);
|
||||
}
|
||||
|
||||
public function getCreateObjectTitleForFeed($author, $object) {
|
||||
return pht('%s created %s.', $author, $object);
|
||||
}
|
||||
|
||||
public function getTransactionTypes() {
|
||||
$types = parent::getTransactionTypes();
|
||||
|
||||
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
|
||||
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
}
|
32
src/applications/herald/field/HeraldActingUserField.php
Normal file
32
src/applications/herald/field/HeraldActingUserField.php
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
final class HeraldActingUserField
|
||||
extends HeraldField {
|
||||
|
||||
const FIELDCONST = 'herald.acting-user';
|
||||
|
||||
public function getHeraldFieldName() {
|
||||
return pht('Acting user');
|
||||
}
|
||||
|
||||
public function getHeraldFieldValue($object) {
|
||||
return $this->getAdapter()->getActingAsPHID();
|
||||
}
|
||||
|
||||
protected function getHeraldFieldStandardType() {
|
||||
return self::STANDARD_PHID;
|
||||
}
|
||||
|
||||
protected function getDatasource() {
|
||||
return new PhabricatorPeopleDatasource();
|
||||
}
|
||||
|
||||
public function supportsObject($object) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getFieldGroupKey() {
|
||||
return HeraldEditFieldGroup::FIELDGROUPKEY;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
final class HeraldWebhookRequestGarbageCollector
|
||||
extends PhabricatorGarbageCollector {
|
||||
|
||||
const COLLECTORCONST = 'herald.webhooks';
|
||||
|
||||
public function getCollectorName() {
|
||||
return pht('Herald Webhooks');
|
||||
}
|
||||
|
||||
public function getDefaultRetentionPolicy() {
|
||||
return phutil_units('7 days in seconds');
|
||||
}
|
||||
|
||||
protected function collectGarbage() {
|
||||
$table = new HeraldWebhookRequest();
|
||||
$conn_w = $table->establishConnection('w');
|
||||
|
||||
queryfx(
|
||||
$conn_w,
|
||||
'DELETE FROM %T WHERE dateCreated < %d LIMIT 100',
|
||||
$table->getTableName(),
|
||||
$this->getGarbageEpoch());
|
||||
|
||||
return ($conn_w->getAffectedRows() == 100);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
<?php
|
||||
|
||||
final class HeraldWebhookCallManagementWorkflow
|
||||
extends HeraldWebhookManagementWorkflow {
|
||||
|
||||
protected function didConstruct() {
|
||||
$this
|
||||
->setName('call')
|
||||
->setExamples('**call** --id __id__ [--object __object__]')
|
||||
->setSynopsis(pht('Call a webhook.'))
|
||||
->setArguments(
|
||||
array(
|
||||
array(
|
||||
'name' => 'id',
|
||||
'param' => 'id',
|
||||
'help' => pht('Webhook ID to call'),
|
||||
),
|
||||
array(
|
||||
'name' => 'object',
|
||||
'param' => 'object',
|
||||
'help' => pht('Submit transactions for a particular object.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'silent',
|
||||
'help' => pht('Set the "silent" flag on the request.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'secure',
|
||||
'help' => pht('Set the "secure" flag on the request.'),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
public function execute(PhutilArgumentParser $args) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$id = $args->getArg('id');
|
||||
if (!$id) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Specify a webhook to call with "--id".'));
|
||||
}
|
||||
|
||||
$hook = id(new HeraldWebhookQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($id))
|
||||
->executeOne();
|
||||
if (!$hook) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Unable to load specified webhook ("%s").',
|
||||
$id));
|
||||
}
|
||||
|
||||
$object_name = $args->getArg('object');
|
||||
if ($object_name === null) {
|
||||
$object = $hook;
|
||||
} else {
|
||||
$objects = id(new PhabricatorObjectQuery())
|
||||
->setViewer($viewer)
|
||||
->withNames(array($object_name))
|
||||
->execute();
|
||||
if (!$objects) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Unable to load specified object ("%s").',
|
||||
$object_name));
|
||||
}
|
||||
$object = head($objects);
|
||||
}
|
||||
|
||||
$xaction_query =
|
||||
PhabricatorApplicationTransactionQuery::newQueryForObject($object);
|
||||
|
||||
$xactions = $xaction_query
|
||||
->withObjectPHIDs(array($object->getPHID()))
|
||||
->setViewer($viewer)
|
||||
->setLimit(10)
|
||||
->execute();
|
||||
|
||||
$application_phid = id(new PhabricatorHeraldApplication())->getPHID();
|
||||
|
||||
$request = HeraldWebhookRequest::initializeNewWebhookRequest($hook)
|
||||
->setObjectPHID($object->getPHID())
|
||||
->setIsTestAction(true)
|
||||
->setIsSilentAction((bool)$args->getArg('silent'))
|
||||
->setIsSecureAction((bool)$args->getArg('secure'))
|
||||
->setTriggerPHIDs(array($application_phid))
|
||||
->setTransactionPHIDs(mpull($xactions, 'getPHID'))
|
||||
->save();
|
||||
|
||||
PhabricatorWorker::setRunAllTasksInProcess(true);
|
||||
$request->queueCall();
|
||||
|
||||
$request->reload();
|
||||
|
||||
echo tsprintf(
|
||||
"%s\n",
|
||||
pht(
|
||||
'Success, got HTTP %s from webhook.',
|
||||
$request->getErrorCode()));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<?php
|
||||
|
||||
abstract class HeraldWebhookManagementWorkflow
|
||||
extends PhabricatorManagementWorkflow {}
|
49
src/applications/herald/phid/HeraldWebhookPHIDType.php
Normal file
49
src/applications/herald/phid/HeraldWebhookPHIDType.php
Normal file
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
final class HeraldWebhookPHIDType extends PhabricatorPHIDType {
|
||||
|
||||
const TYPECONST = 'HWBH';
|
||||
|
||||
public function getTypeName() {
|
||||
return pht('Webhook');
|
||||
}
|
||||
|
||||
public function newObject() {
|
||||
return new HeraldWebhook();
|
||||
}
|
||||
|
||||
public function getPHIDTypeApplicationClass() {
|
||||
return 'PhabricatorHeraldApplication';
|
||||
}
|
||||
|
||||
protected function buildQueryForObjects(
|
||||
PhabricatorObjectQuery $query,
|
||||
array $phids) {
|
||||
|
||||
return id(new HeraldWebhookQuery())
|
||||
->withPHIDs($phids);
|
||||
}
|
||||
|
||||
public function loadHandles(
|
||||
PhabricatorHandleQuery $query,
|
||||
array $handles,
|
||||
array $objects) {
|
||||
|
||||
foreach ($handles as $phid => $handle) {
|
||||
$hook = $objects[$phid];
|
||||
|
||||
$name = $hook->getName();
|
||||
$id = $hook->getID();
|
||||
|
||||
$handle
|
||||
->setName($name)
|
||||
->setURI($hook->getURI())
|
||||
->setFullName(pht('Webhook %d %s', $id, $name));
|
||||
|
||||
if ($hook->isDisabled()) {
|
||||
$handle->setStatus(PhabricatorObjectHandle::STATUS_CLOSED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
final class HeraldWebhookRequestPHIDType extends PhabricatorPHIDType {
|
||||
|
||||
const TYPECONST = 'HWBR';
|
||||
|
||||
public function getTypeName() {
|
||||
return pht('Webhook Request');
|
||||
}
|
||||
|
||||
public function newObject() {
|
||||
return new HeraldWebhook();
|
||||
}
|
||||
|
||||
public function getPHIDTypeApplicationClass() {
|
||||
return 'PhabricatorHeraldApplication';
|
||||
}
|
||||
|
||||
protected function buildQueryForObjects(
|
||||
PhabricatorObjectQuery $query,
|
||||
array $phids) {
|
||||
|
||||
return id(new HeraldWebhookRequestQuery())
|
||||
->withPHIDs($phids);
|
||||
}
|
||||
|
||||
public function loadHandles(
|
||||
PhabricatorHandleQuery $query,
|
||||
array $handles,
|
||||
array $objects) {
|
||||
|
||||
foreach ($handles as $phid => $handle) {
|
||||
$request = $objects[$phid];
|
||||
$handle->setName(pht('Webhook Request %d', $request->getID()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
64
src/applications/herald/query/HeraldWebhookQuery.php
Normal file
64
src/applications/herald/query/HeraldWebhookQuery.php
Normal file
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
final class HeraldWebhookQuery
|
||||
extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||
|
||||
private $ids;
|
||||
private $phids;
|
||||
private $statuses;
|
||||
|
||||
public function withIDs(array $ids) {
|
||||
$this->ids = $ids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withPHIDs(array $phids) {
|
||||
$this->phids = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withStatuses(array $statuses) {
|
||||
$this->statuses = $statuses;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function newResultObject() {
|
||||
return new HeraldWebhook();
|
||||
}
|
||||
|
||||
protected function loadPage() {
|
||||
return $this->loadStandardPage($this->newResultObject());
|
||||
}
|
||||
|
||||
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
|
||||
$where = parent::buildWhereClauseParts($conn);
|
||||
|
||||
if ($this->ids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'id IN (%Ld)',
|
||||
$this->ids);
|
||||
}
|
||||
|
||||
if ($this->phids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'phid IN (%Ls)',
|
||||
$this->phids);
|
||||
}
|
||||
|
||||
if ($this->statuses !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'status IN (%Ls)',
|
||||
$this->statuses);
|
||||
}
|
||||
|
||||
return $where;
|
||||
}
|
||||
|
||||
public function getQueryApplicationClass() {
|
||||
return 'PhabricatorHeraldApplication';
|
||||
}
|
||||
|
||||
}
|
126
src/applications/herald/query/HeraldWebhookRequestQuery.php
Normal file
126
src/applications/herald/query/HeraldWebhookRequestQuery.php
Normal file
|
@ -0,0 +1,126 @@
|
|||
<?php
|
||||
|
||||
final class HeraldWebhookRequestQuery
|
||||
extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||
|
||||
private $ids;
|
||||
private $phids;
|
||||
private $webhookPHIDs;
|
||||
private $lastRequestEpochMin;
|
||||
private $lastRequestEpochMax;
|
||||
private $lastRequestResults;
|
||||
|
||||
public function withIDs(array $ids) {
|
||||
$this->ids = $ids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withPHIDs(array $phids) {
|
||||
$this->phids = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withWebhookPHIDs(array $phids) {
|
||||
$this->webhookPHIDs = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function newResultObject() {
|
||||
return new HeraldWebhookRequest();
|
||||
}
|
||||
|
||||
protected function loadPage() {
|
||||
return $this->loadStandardPage($this->newResultObject());
|
||||
}
|
||||
|
||||
public function withLastRequestEpochBetween($epoch_min, $epoch_max) {
|
||||
$this->lastRequestEpochMin = $epoch_min;
|
||||
$this->lastRequestEpochMax = $epoch_max;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withLastRequestResults(array $results) {
|
||||
$this->lastRequestResults = $results;
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
|
||||
$where = parent::buildWhereClauseParts($conn);
|
||||
|
||||
if ($this->ids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'id IN (%Ld)',
|
||||
$this->ids);
|
||||
}
|
||||
|
||||
if ($this->phids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'phid IN (%Ls)',
|
||||
$this->phids);
|
||||
}
|
||||
|
||||
if ($this->webhookPHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'webhookPHID IN (%Ls)',
|
||||
$this->webhookPHIDs);
|
||||
}
|
||||
|
||||
if ($this->lastRequestEpochMin !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'lastRequestEpoch >= %d',
|
||||
$this->lastRequestEpochMin);
|
||||
}
|
||||
|
||||
if ($this->lastRequestEpochMax !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'lastRequestEpoch <= %d',
|
||||
$this->lastRequestEpochMax);
|
||||
}
|
||||
|
||||
if ($this->lastRequestResults !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'lastRequestResult IN (%Ls)',
|
||||
$this->lastRequestResults);
|
||||
}
|
||||
|
||||
return $where;
|
||||
}
|
||||
|
||||
protected function willFilterPage(array $requests) {
|
||||
$hook_phids = mpull($requests, 'getWebhookPHID');
|
||||
|
||||
$hooks = id(new HeraldWebhookQuery())
|
||||
->setViewer($this->getViewer())
|
||||
->setParentQuery($this)
|
||||
->withPHIDs($hook_phids)
|
||||
->execute();
|
||||
$hooks = mpull($hooks, null, 'getPHID');
|
||||
|
||||
foreach ($requests as $key => $request) {
|
||||
$hook_phid = $request->getWebhookPHID();
|
||||
$hook = idx($hooks, $hook_phid);
|
||||
|
||||
if (!$hook) {
|
||||
unset($requests[$key]);
|
||||
$this->didRejectResult($request);
|
||||
continue;
|
||||
}
|
||||
|
||||
$request->attachWebhook($hook);
|
||||
}
|
||||
|
||||
return $requests;
|
||||
}
|
||||
|
||||
|
||||
public function getQueryApplicationClass() {
|
||||
return 'PhabricatorHeraldApplication';
|
||||
}
|
||||
|
||||
}
|
102
src/applications/herald/query/HeraldWebhookSearchEngine.php
Normal file
102
src/applications/herald/query/HeraldWebhookSearchEngine.php
Normal file
|
@ -0,0 +1,102 @@
|
|||
<?php
|
||||
|
||||
final class HeraldWebhookSearchEngine
|
||||
extends PhabricatorApplicationSearchEngine {
|
||||
|
||||
public function getResultTypeDescription() {
|
||||
return pht('Webhooks');
|
||||
}
|
||||
|
||||
public function getApplicationClassName() {
|
||||
return 'PhabricatorHeraldApplication';
|
||||
}
|
||||
|
||||
public function newQuery() {
|
||||
return new HeraldWebhookQuery();
|
||||
}
|
||||
|
||||
protected function buildQueryFromParameters(array $map) {
|
||||
$query = $this->newQuery();
|
||||
|
||||
if ($map['statuses']) {
|
||||
$query->withStatuses($map['statuses']);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
protected function buildCustomSearchFields() {
|
||||
return array(
|
||||
id(new PhabricatorSearchCheckboxesField())
|
||||
->setKey('statuses')
|
||||
->setLabel(pht('Status'))
|
||||
->setDescription(
|
||||
pht('Search for archived or active pastes.'))
|
||||
->setOptions(HeraldWebhook::getStatusDisplayNameMap()),
|
||||
);
|
||||
}
|
||||
|
||||
protected function getURI($path) {
|
||||
return '/herald/webhook/'.$path;
|
||||
}
|
||||
|
||||
protected function getBuiltinQueryNames() {
|
||||
$names = array();
|
||||
|
||||
$names['active'] = pht('Active');
|
||||
$names['all'] = pht('All');
|
||||
|
||||
return $names;
|
||||
}
|
||||
|
||||
public function buildSavedQueryFromBuiltin($query_key) {
|
||||
$query = $this->newSavedQuery();
|
||||
$query->setQueryKey($query_key);
|
||||
|
||||
switch ($query_key) {
|
||||
case 'all':
|
||||
return $query;
|
||||
case 'active':
|
||||
return $query->setParameter(
|
||||
'statuses',
|
||||
array(
|
||||
HeraldWebhook::HOOKSTATUS_FIREHOSE,
|
||||
HeraldWebhook::HOOKSTATUS_ENABLED,
|
||||
));
|
||||
}
|
||||
|
||||
return parent::buildSavedQueryFromBuiltin($query_key);
|
||||
}
|
||||
|
||||
protected function renderResultList(
|
||||
array $hooks,
|
||||
PhabricatorSavedQuery $query,
|
||||
array $handles) {
|
||||
assert_instances_of($hooks, 'HeraldWebhook');
|
||||
|
||||
$viewer = $this->requireViewer();
|
||||
|
||||
$list = id(new PHUIObjectItemListView())
|
||||
->setViewer($viewer);
|
||||
foreach ($hooks as $hook) {
|
||||
$item = id(new PHUIObjectItemView())
|
||||
->setObjectName(pht('Webhook %d', $hook->getID()))
|
||||
->setHeader($hook->getName())
|
||||
->setHref($hook->getURI())
|
||||
->addAttribute($hook->getWebhookURI());
|
||||
|
||||
$item->addIcon($hook->getStatusIcon(), $hook->getStatusDisplayName());
|
||||
|
||||
if ($hook->isDisabled()) {
|
||||
$item->setDisabled(true);
|
||||
}
|
||||
|
||||
$list->addItem($item);
|
||||
}
|
||||
|
||||
return id(new PhabricatorApplicationSearchResultView())
|
||||
->setObjectList($list)
|
||||
->setNoDataString(pht('No webhooks found.'));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
final class HeraldWebhookTransactionQuery
|
||||
extends PhabricatorApplicationTransactionQuery {
|
||||
|
||||
public function getTemplateApplicationTransaction() {
|
||||
return new HeraldWebhookTransaction();
|
||||
}
|
||||
|
||||
}
|
244
src/applications/herald/storage/HeraldWebhook.php
Normal file
244
src/applications/herald/storage/HeraldWebhook.php
Normal file
|
@ -0,0 +1,244 @@
|
|||
<?php
|
||||
|
||||
final class HeraldWebhook
|
||||
extends HeraldDAO
|
||||
implements
|
||||
PhabricatorPolicyInterface,
|
||||
PhabricatorApplicationTransactionInterface,
|
||||
PhabricatorDestructibleInterface,
|
||||
PhabricatorProjectInterface {
|
||||
|
||||
protected $name;
|
||||
protected $webhookURI;
|
||||
protected $viewPolicy;
|
||||
protected $editPolicy;
|
||||
protected $status;
|
||||
protected $hmacKey;
|
||||
|
||||
const HOOKSTATUS_FIREHOSE = 'firehose';
|
||||
const HOOKSTATUS_ENABLED = 'enabled';
|
||||
const HOOKSTATUS_DISABLED = 'disabled';
|
||||
|
||||
protected function getConfiguration() {
|
||||
return array(
|
||||
self::CONFIG_AUX_PHID => true,
|
||||
self::CONFIG_COLUMN_SCHEMA => array(
|
||||
'name' => 'text128',
|
||||
'webhookURI' => 'text255',
|
||||
'status' => 'text32',
|
||||
'hmacKey' => 'text32',
|
||||
),
|
||||
self::CONFIG_KEY_SCHEMA => array(
|
||||
'key_status' => array(
|
||||
'columns' => array('status'),
|
||||
),
|
||||
),
|
||||
) + parent::getConfiguration();
|
||||
}
|
||||
|
||||
public function getPHIDType() {
|
||||
return HeraldWebhookPHIDType::TYPECONST;
|
||||
}
|
||||
|
||||
public static function initializeNewWebhook(PhabricatorUser $viewer) {
|
||||
return id(new self())
|
||||
->setStatus(self::HOOKSTATUS_ENABLED)
|
||||
->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy())
|
||||
->setEditPolicy($viewer->getPHID())
|
||||
->regenerateHMACKey();
|
||||
}
|
||||
|
||||
public function getURI() {
|
||||
return '/herald/webhook/view/'.$this->getID().'/';
|
||||
}
|
||||
|
||||
public function isDisabled() {
|
||||
return ($this->getStatus() === self::HOOKSTATUS_DISABLED);
|
||||
}
|
||||
|
||||
public static function getStatusDisplayNameMap() {
|
||||
$specs = self::getStatusSpecifications();
|
||||
return ipull($specs, 'name', 'key');
|
||||
}
|
||||
|
||||
private static function getStatusSpecifications() {
|
||||
$specs = array(
|
||||
array(
|
||||
'key' => self::HOOKSTATUS_FIREHOSE,
|
||||
'name' => pht('Firehose'),
|
||||
'color' => 'orange',
|
||||
'icon' => 'fa-star-o',
|
||||
),
|
||||
array(
|
||||
'key' => self::HOOKSTATUS_ENABLED,
|
||||
'name' => pht('Enabled'),
|
||||
'color' => 'bluegrey',
|
||||
'icon' => 'fa-check',
|
||||
),
|
||||
array(
|
||||
'key' => self::HOOKSTATUS_DISABLED,
|
||||
'name' => pht('Disabled'),
|
||||
'color' => 'dark',
|
||||
'icon' => 'fa-ban',
|
||||
),
|
||||
);
|
||||
|
||||
return ipull($specs, null, 'key');
|
||||
}
|
||||
|
||||
|
||||
private static function getSpecificationForStatus($status) {
|
||||
$specs = self::getStatusSpecifications();
|
||||
|
||||
if (isset($specs[$status])) {
|
||||
return $specs[$status];
|
||||
}
|
||||
|
||||
return array(
|
||||
'key' => $status,
|
||||
'name' => pht('Unknown ("%s")', $status),
|
||||
'icon' => 'fa-question',
|
||||
'color' => 'indigo',
|
||||
);
|
||||
}
|
||||
|
||||
public static function getDisplayNameForStatus($status) {
|
||||
$spec = self::getSpecificationForStatus($status);
|
||||
return $spec['name'];
|
||||
}
|
||||
|
||||
public static function getIconForStatus($status) {
|
||||
$spec = self::getSpecificationForStatus($status);
|
||||
return $spec['icon'];
|
||||
}
|
||||
|
||||
public static function getColorForStatus($status) {
|
||||
$spec = self::getSpecificationForStatus($status);
|
||||
return $spec['color'];
|
||||
}
|
||||
|
||||
public function getStatusDisplayName() {
|
||||
$status = $this->getStatus();
|
||||
return self::getDisplayNameForStatus($status);
|
||||
}
|
||||
|
||||
public function getStatusIcon() {
|
||||
$status = $this->getStatus();
|
||||
return self::getIconForStatus($status);
|
||||
}
|
||||
|
||||
public function getStatusColor() {
|
||||
$status = $this->getStatus();
|
||||
return self::getColorForStatus($status);
|
||||
}
|
||||
|
||||
public function getErrorBackoffWindow() {
|
||||
return phutil_units('5 minutes in seconds');
|
||||
}
|
||||
|
||||
public function getErrorBackoffThreshold() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
public function isInErrorBackoff(PhabricatorUser $viewer) {
|
||||
$backoff_window = $this->getErrorBackoffWindow();
|
||||
$backoff_threshold = $this->getErrorBackoffThreshold();
|
||||
|
||||
$now = PhabricatorTime::getNow();
|
||||
|
||||
$window_start = ($now - $backoff_window);
|
||||
|
||||
$requests = id(new HeraldWebhookRequestQuery())
|
||||
->setViewer($viewer)
|
||||
->withWebhookPHIDs(array($this->getPHID()))
|
||||
->withLastRequestEpochBetween($window_start, null)
|
||||
->withLastRequestResults(
|
||||
array(
|
||||
HeraldWebhookRequest::RESULT_FAIL,
|
||||
))
|
||||
->execute();
|
||||
|
||||
if (count($requests) >= $backoff_threshold) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function regenerateHMACKey() {
|
||||
return $this->setHMACKey(Filesystem::readRandomCharacters(32));
|
||||
}
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
|
||||
public function getCapabilities() {
|
||||
return array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
);
|
||||
}
|
||||
|
||||
public function getPolicy($capability) {
|
||||
switch ($capability) {
|
||||
case PhabricatorPolicyCapability::CAN_VIEW:
|
||||
return $this->getViewPolicy();
|
||||
case PhabricatorPolicyCapability::CAN_EDIT:
|
||||
return $this->getEditPolicy();
|
||||
}
|
||||
}
|
||||
|
||||
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
|
||||
|
||||
|
||||
public function getApplicationTransactionEditor() {
|
||||
return new HeraldWebhookEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new HeraldWebhookTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorDestructibleInterface )----------------------------------- */
|
||||
|
||||
|
||||
public function destroyObjectPermanently(
|
||||
PhabricatorDestructionEngine $engine) {
|
||||
|
||||
while (true) {
|
||||
$requests = id(new HeraldWebhookRequestQuery())
|
||||
->setViewer($engine->getViewer())
|
||||
->withWebhookPHIDs(array($this->getPHID()))
|
||||
->setLimit(100)
|
||||
->execute();
|
||||
|
||||
if (!$requests) {
|
||||
break;
|
||||
}
|
||||
|
||||
foreach ($requests as $request) {
|
||||
$request->delete();
|
||||
}
|
||||
}
|
||||
|
||||
$this->delete();
|
||||
}
|
||||
|
||||
|
||||
}
|
223
src/applications/herald/storage/HeraldWebhookRequest.php
Normal file
223
src/applications/herald/storage/HeraldWebhookRequest.php
Normal file
|
@ -0,0 +1,223 @@
|
|||
<?php
|
||||
|
||||
final class HeraldWebhookRequest
|
||||
extends HeraldDAO
|
||||
implements
|
||||
PhabricatorPolicyInterface,
|
||||
PhabricatorExtendedPolicyInterface {
|
||||
|
||||
protected $webhookPHID;
|
||||
protected $objectPHID;
|
||||
protected $status;
|
||||
protected $properties = array();
|
||||
protected $lastRequestResult;
|
||||
protected $lastRequestEpoch;
|
||||
|
||||
private $webhook = self::ATTACHABLE;
|
||||
|
||||
const RETRY_NEVER = 'never';
|
||||
const RETRY_FOREVER = 'forever';
|
||||
|
||||
const STATUS_QUEUED = 'queued';
|
||||
const STATUS_FAILED = 'failed';
|
||||
const STATUS_SENT = 'sent';
|
||||
|
||||
const RESULT_NONE = 'none';
|
||||
const RESULT_OKAY = 'okay';
|
||||
const RESULT_FAIL = 'fail';
|
||||
|
||||
protected function getConfiguration() {
|
||||
return array(
|
||||
self::CONFIG_AUX_PHID => true,
|
||||
self::CONFIG_SERIALIZATION => array(
|
||||
'properties' => self::SERIALIZATION_JSON,
|
||||
),
|
||||
self::CONFIG_COLUMN_SCHEMA => array(
|
||||
'status' => 'text32',
|
||||
'lastRequestResult' => 'text32',
|
||||
'lastRequestEpoch' => 'epoch',
|
||||
),
|
||||
self::CONFIG_KEY_SCHEMA => array(
|
||||
'key_ratelimit' => array(
|
||||
'columns' => array(
|
||||
'webhookPHID',
|
||||
'lastRequestResult',
|
||||
'lastRequestEpoch',
|
||||
),
|
||||
),
|
||||
'key_collect' => array(
|
||||
'columns' => array('dateCreated'),
|
||||
),
|
||||
),
|
||||
) + parent::getConfiguration();
|
||||
}
|
||||
|
||||
public function getPHIDType() {
|
||||
return HeraldWebhookRequestPHIDType::TYPECONST;
|
||||
}
|
||||
|
||||
public static function initializeNewWebhookRequest(HeraldWebhook $hook) {
|
||||
return id(new self())
|
||||
->setWebhookPHID($hook->getPHID())
|
||||
->attachWebhook($hook)
|
||||
->setStatus(self::STATUS_QUEUED)
|
||||
->setRetryMode(self::RETRY_NEVER)
|
||||
->setLastRequestResult(self::RESULT_NONE)
|
||||
->setLastRequestEpoch(0);
|
||||
}
|
||||
|
||||
public function getWebhook() {
|
||||
return $this->assertAttached($this->webhook);
|
||||
}
|
||||
|
||||
public function attachWebhook(HeraldWebhook $hook) {
|
||||
$this->webhook = $hook;
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function setProperty($key, $value) {
|
||||
$this->properties[$key] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function getProperty($key, $default = null) {
|
||||
return idx($this->properties, $key, $default);
|
||||
}
|
||||
|
||||
public function setRetryMode($mode) {
|
||||
return $this->setProperty('retry', $mode);
|
||||
}
|
||||
|
||||
public function getRetryMode() {
|
||||
return $this->getProperty('retry');
|
||||
}
|
||||
|
||||
public function setErrorType($error_type) {
|
||||
return $this->setProperty('errorType', $error_type);
|
||||
}
|
||||
|
||||
public function getErrorType() {
|
||||
return $this->getProperty('errorType');
|
||||
}
|
||||
|
||||
public function setErrorCode($error_code) {
|
||||
return $this->setProperty('errorCode', $error_code);
|
||||
}
|
||||
|
||||
public function getErrorCode() {
|
||||
return $this->getProperty('errorCode');
|
||||
}
|
||||
|
||||
public function setTransactionPHIDs(array $phids) {
|
||||
return $this->setProperty('transactionPHIDs', $phids);
|
||||
}
|
||||
|
||||
public function getTransactionPHIDs() {
|
||||
return $this->getProperty('transactionPHIDs', array());
|
||||
}
|
||||
|
||||
public function setTriggerPHIDs(array $phids) {
|
||||
return $this->setProperty('triggerPHIDs', $phids);
|
||||
}
|
||||
|
||||
public function getTriggerPHIDs() {
|
||||
return $this->getProperty('triggerPHIDs', array());
|
||||
}
|
||||
|
||||
public function setIsSilentAction($bool) {
|
||||
return $this->setProperty('silent', $bool);
|
||||
}
|
||||
|
||||
public function getIsSilentAction() {
|
||||
return $this->getProperty('silent', false);
|
||||
}
|
||||
|
||||
public function setIsTestAction($bool) {
|
||||
return $this->setProperty('test', $bool);
|
||||
}
|
||||
|
||||
public function getIsTestAction() {
|
||||
return $this->getProperty('test', false);
|
||||
}
|
||||
|
||||
public function setIsSecureAction($bool) {
|
||||
return $this->setProperty('secure', $bool);
|
||||
}
|
||||
|
||||
public function getIsSecureAction() {
|
||||
return $this->getProperty('secure', false);
|
||||
}
|
||||
|
||||
public function queueCall() {
|
||||
PhabricatorWorker::scheduleTask(
|
||||
'HeraldWebhookWorker',
|
||||
array(
|
||||
'webhookRequestPHID' => $this->getPHID(),
|
||||
),
|
||||
array(
|
||||
'objectPHID' => $this->getPHID(),
|
||||
));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function newStatusIcon() {
|
||||
switch ($this->getStatus()) {
|
||||
case self::STATUS_QUEUED:
|
||||
$icon = 'fa-refresh';
|
||||
$color = 'blue';
|
||||
$tooltip = pht('Queued');
|
||||
break;
|
||||
case self::STATUS_SENT:
|
||||
$icon = 'fa-check';
|
||||
$color = 'green';
|
||||
$tooltip = pht('Sent');
|
||||
break;
|
||||
case self::STATUS_FAILED:
|
||||
default:
|
||||
$icon = 'fa-times';
|
||||
$color = 'red';
|
||||
$tooltip = pht('Failed');
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return id(new PHUIIconView())
|
||||
->setIcon($icon, $color)
|
||||
->setTooltip($tooltip);
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
|
||||
public function getCapabilities() {
|
||||
return array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
);
|
||||
}
|
||||
|
||||
public function getPolicy($capability) {
|
||||
switch ($capability) {
|
||||
case PhabricatorPolicyCapability::CAN_VIEW:
|
||||
return PhabricatorPolicies::getMostOpenPolicy();
|
||||
}
|
||||
}
|
||||
|
||||
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorExtendedPolicyInterface )--------------------------------- */
|
||||
|
||||
|
||||
public function getExtendedPolicy($capability, PhabricatorUser $viewer) {
|
||||
return array(
|
||||
array($this->getWebhook(), PhabricatorPolicyCapability::CAN_VIEW),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
22
src/applications/herald/storage/HeraldWebhookTransaction.php
Normal file
22
src/applications/herald/storage/HeraldWebhookTransaction.php
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
final class HeraldWebhookTransaction
|
||||
extends PhabricatorModularTransaction {
|
||||
|
||||
public function getApplicationName() {
|
||||
return 'herald';
|
||||
}
|
||||
|
||||
public function getApplicationTransactionType() {
|
||||
return HeraldWebhookPHIDType::TYPECONST;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionCommentObject() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getBaseTransactionClass() {
|
||||
return 'HeraldWebhookTransactionType';
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
final class HeraldWebhookDatasource
|
||||
extends PhabricatorTypeaheadDatasource {
|
||||
|
||||
public function getPlaceholderText() {
|
||||
return pht('Type a webhook name...');
|
||||
}
|
||||
|
||||
public function getBrowseTitle() {
|
||||
return pht('Browse Webhooks');
|
||||
}
|
||||
|
||||
public function getDatasourceApplicationClass() {
|
||||
return 'PhabricatorHeraldApplication';
|
||||
}
|
||||
|
||||
public function loadResults() {
|
||||
$viewer = $this->getViewer();
|
||||
$raw_query = $this->getRawQuery();
|
||||
|
||||
$hooks = id(new HeraldWebhookQuery())
|
||||
->setViewer($viewer)
|
||||
->execute();
|
||||
|
||||
$handles = id(new PhabricatorHandleQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs(mpull($hooks, 'getPHID'))
|
||||
->execute();
|
||||
|
||||
$results = array();
|
||||
foreach ($hooks as $hook) {
|
||||
$handle = $handles[$hook->getPHID()];
|
||||
|
||||
$result = id(new PhabricatorTypeaheadResult())
|
||||
->setName($handle->getFullName())
|
||||
->setPHID($handle->getPHID());
|
||||
|
||||
if ($hook->isDisabled()) {
|
||||
$result->setClosed(pht('Disabled'));
|
||||
}
|
||||
|
||||
$results[] = $result;
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
final class HeraldWebhookRequestListView
|
||||
extends AphrontView {
|
||||
|
||||
private $requests;
|
||||
private $highlightID;
|
||||
|
||||
public function setRequests(array $requests) {
|
||||
assert_instances_of($requests, 'HeraldWebhookRequest');
|
||||
$this->requests = $requests;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setHighlightID($highlight_id) {
|
||||
$this->highlightID = $highlight_id;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getHighlightID() {
|
||||
return $this->highlightID;
|
||||
}
|
||||
|
||||
public function render() {
|
||||
$viewer = $this->getViewer();
|
||||
$requests = $this->requests;
|
||||
|
||||
$handle_phids = array();
|
||||
foreach ($requests as $request) {
|
||||
$handle_phids[] = $request->getObjectPHID();
|
||||
}
|
||||
$handles = $viewer->loadHandles($handle_phids);
|
||||
|
||||
$highlight_id = $this->getHighlightID();
|
||||
|
||||
$rows = array();
|
||||
$rowc = array();
|
||||
foreach ($requests as $request) {
|
||||
$icon = $request->newStatusIcon();
|
||||
|
||||
if ($highlight_id == $request->getID()) {
|
||||
$rowc[] = 'highlighted';
|
||||
} else {
|
||||
$rowc[] = null;
|
||||
}
|
||||
|
||||
$last_epoch = $request->getLastRequestEpoch();
|
||||
if ($request->getLastRequestEpoch()) {
|
||||
$last_request = phabricator_datetime($last_epoch, $viewer);
|
||||
} else {
|
||||
$last_request = null;
|
||||
}
|
||||
|
||||
$rows[] = array(
|
||||
$request->getID(),
|
||||
$icon,
|
||||
$handles[$request->getObjectPHID()]->renderLink(),
|
||||
$request->getErrorType(),
|
||||
$request->getErrorCode(),
|
||||
$last_request,
|
||||
);
|
||||
}
|
||||
|
||||
$table = id(new AphrontTableView($rows))
|
||||
->setRowClasses($rowc)
|
||||
->setHeaders(
|
||||
array(
|
||||
pht('ID'),
|
||||
'',
|
||||
pht('Object'),
|
||||
pht('Type'),
|
||||
pht('Code'),
|
||||
pht('Requested At'),
|
||||
))
|
||||
->setColumnClasses(
|
||||
array(
|
||||
'n',
|
||||
'',
|
||||
'wide',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
));
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
}
|
243
src/applications/herald/worker/HeraldWebhookWorker.php
Normal file
243
src/applications/herald/worker/HeraldWebhookWorker.php
Normal file
|
@ -0,0 +1,243 @@
|
|||
<?php
|
||||
|
||||
final class HeraldWebhookWorker
|
||||
extends PhabricatorWorker {
|
||||
|
||||
protected function doWork() {
|
||||
$viewer = PhabricatorUser::getOmnipotentUser();
|
||||
|
||||
$data = $this->getTaskData();
|
||||
$request_phid = idx($data, 'webhookRequestPHID');
|
||||
|
||||
$request = id(new HeraldWebhookRequestQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs(array($request_phid))
|
||||
->executeOne();
|
||||
if (!$request) {
|
||||
throw new PhabricatorWorkerPermanentFailureException(
|
||||
pht(
|
||||
'Unable to load webhook request ("%s"). It may have been '.
|
||||
'garbage collected.',
|
||||
$request_phid));
|
||||
}
|
||||
|
||||
$status = $request->getStatus();
|
||||
if ($status !== HeraldWebhookRequest::STATUS_QUEUED) {
|
||||
throw new PhabricatorWorkerPermanentFailureException(
|
||||
pht(
|
||||
'Webhook request ("%s") is not in "%s" status (actual '.
|
||||
'status is "%s"). Declining call to hook.',
|
||||
$request_phid,
|
||||
HeraldWebhookRequest::STATUS_QUEUED,
|
||||
$status));
|
||||
}
|
||||
|
||||
$hook = $request->getWebhook();
|
||||
|
||||
if ($hook->isDisabled()) {
|
||||
$this->failRequest($request, 'hook', 'disabled');
|
||||
throw new PhabricatorWorkerPermanentFailureException(
|
||||
pht(
|
||||
'Associated hook ("%s") for webhook request ("%s") is disabled.',
|
||||
$hook->getPHID(),
|
||||
$request_phid));
|
||||
}
|
||||
|
||||
$uri = $hook->getWebhookURI();
|
||||
try {
|
||||
PhabricatorEnv::requireValidRemoteURIForFetch(
|
||||
$uri,
|
||||
array(
|
||||
'http',
|
||||
'https',
|
||||
));
|
||||
} catch (Exception $ex) {
|
||||
$this->failRequest($request, 'hook', 'uri');
|
||||
throw new PhabricatorWorkerPermanentFailureException(
|
||||
pht(
|
||||
'Associated hook ("%s") for webhook request ("%s") has invalid '.
|
||||
'fetch URI: %s',
|
||||
$hook->getPHID(),
|
||||
$request_phid,
|
||||
$ex->getMessage()));
|
||||
}
|
||||
|
||||
$object_phid = $request->getObjectPHID();
|
||||
|
||||
$object = id(new PhabricatorObjectQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs(array($object_phid))
|
||||
->executeOne();
|
||||
if (!$object) {
|
||||
$this->failRequest($request, 'hook', 'object');
|
||||
throw new PhabricatorWorkerPermanentFailureException(
|
||||
pht(
|
||||
'Unable to load object ("%s") for webhook request ("%s").',
|
||||
$object_phid,
|
||||
$request_phid));
|
||||
}
|
||||
|
||||
$xaction_query = PhabricatorApplicationTransactionQuery::newQueryForObject(
|
||||
$object);
|
||||
$xaction_phids = $request->getTransactionPHIDs();
|
||||
if ($xaction_phids) {
|
||||
$xactions = $xaction_query
|
||||
->setViewer($viewer)
|
||||
->withObjectPHIDs(array($object_phid))
|
||||
->withPHIDs($xaction_phids)
|
||||
->execute();
|
||||
$xactions = mpull($xactions, null, 'getPHID');
|
||||
} else {
|
||||
$xactions = array();
|
||||
}
|
||||
|
||||
// To prevent thundering herd issues for high volume webhooks (where
|
||||
// a large number of workers might try to work through a request backlog
|
||||
// simultaneously, before the error backoff can catch up), we never
|
||||
// parallelize requests to a particular webhook.
|
||||
|
||||
$lock_key = 'webhook('.$hook->getPHID().')';
|
||||
$lock = PhabricatorGlobalLock::newLock($lock_key);
|
||||
|
||||
try {
|
||||
$lock->lock();
|
||||
} catch (Exception $ex) {
|
||||
phlog($ex);
|
||||
throw new PhabricatorWorkerYieldException(15);
|
||||
}
|
||||
|
||||
$caught = null;
|
||||
try {
|
||||
$this->callWebhookWithLock($hook, $request, $object, $xactions);
|
||||
} catch (Exception $ex) {
|
||||
$caught = $ex;
|
||||
}
|
||||
|
||||
$lock->unlock();
|
||||
|
||||
if ($caught) {
|
||||
throw $caught;
|
||||
}
|
||||
}
|
||||
|
||||
private function callWebhookWithLock(
|
||||
HeraldWebhook $hook,
|
||||
HeraldWebhookRequest $request,
|
||||
$object,
|
||||
array $xactions) {
|
||||
$viewer = PhabricatorUser::getOmnipotentUser();
|
||||
|
||||
if ($hook->isInErrorBackoff($viewer)) {
|
||||
throw new PhabricatorWorkerYieldException($hook->getErrorBackoffWindow());
|
||||
}
|
||||
|
||||
$xaction_data = array();
|
||||
foreach ($xactions as $xaction) {
|
||||
$xaction_data[] = array(
|
||||
'phid' => $xaction->getPHID(),
|
||||
);
|
||||
}
|
||||
|
||||
$trigger_data = array();
|
||||
foreach ($request->getTriggerPHIDs() as $trigger_phid) {
|
||||
$trigger_data[] = array(
|
||||
'phid' => $trigger_phid,
|
||||
);
|
||||
}
|
||||
|
||||
$payload = array(
|
||||
'object' => array(
|
||||
'type' => phid_get_type($object->getPHID()),
|
||||
'phid' => $object->getPHID(),
|
||||
),
|
||||
'triggers' => $trigger_data,
|
||||
'action' => array(
|
||||
'test' => $request->getIsTestAction(),
|
||||
'silent' => $request->getIsSilentAction(),
|
||||
'secure' => $request->getIsSecureAction(),
|
||||
'epoch' => (int)$request->getDateCreated(),
|
||||
),
|
||||
'transactions' => $xaction_data,
|
||||
);
|
||||
|
||||
$payload = id(new PhutilJSON())->encodeFormatted($payload);
|
||||
$key = $hook->getHmacKey();
|
||||
$signature = PhabricatorHash::digestHMACSHA256($payload, $key);
|
||||
$uri = $hook->getWebhookURI();
|
||||
|
||||
$future = id(new HTTPSFuture($uri))
|
||||
->setMethod('POST')
|
||||
->addHeader('Content-Type', 'application/json')
|
||||
->addHeader('X-Phabricator-Webhook-Signature', $signature)
|
||||
->setTimeout(15)
|
||||
->setData($payload);
|
||||
|
||||
list($status) = $future->resolve();
|
||||
|
||||
if ($status->isTimeout()) {
|
||||
$error_type = 'timeout';
|
||||
} else {
|
||||
$error_type = 'http';
|
||||
}
|
||||
$error_code = $status->getStatusCode();
|
||||
|
||||
$request
|
||||
->setErrorType($error_type)
|
||||
->setErrorCode($error_code)
|
||||
->setLastRequestEpoch(PhabricatorTime::getNow());
|
||||
|
||||
$retry_forever = HeraldWebhookRequest::RETRY_FOREVER;
|
||||
if ($status->isTimeout() || $status->isError()) {
|
||||
$should_retry = ($request->getRetryMode() === $retry_forever);
|
||||
|
||||
$request
|
||||
->setLastRequestResult(HeraldWebhookRequest::RESULT_FAIL);
|
||||
|
||||
if ($should_retry) {
|
||||
$request->save();
|
||||
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Webhook request ("%s", to "%s") failed (%s / %s). The request '.
|
||||
'will be retried.',
|
||||
$request->getPHID(),
|
||||
$uri,
|
||||
$error_type,
|
||||
$error_code));
|
||||
} else {
|
||||
$request
|
||||
->setStatus(HeraldWebhookRequest::STATUS_FAILED)
|
||||
->save();
|
||||
|
||||
throw new PhabricatorWorkerPermanentFailureException(
|
||||
pht(
|
||||
'Webhook request ("%s", to "%s") failed (%s / %s). The request '.
|
||||
'will not be retried.',
|
||||
$request->getPHID(),
|
||||
$uri,
|
||||
$error_type,
|
||||
$error_code));
|
||||
}
|
||||
} else {
|
||||
$request
|
||||
->setLastRequestResult(HeraldWebhookRequest::RESULT_OKAY)
|
||||
->setStatus(HeraldWebhookRequest::STATUS_SENT)
|
||||
->save();
|
||||
}
|
||||
}
|
||||
|
||||
private function failRequest(
|
||||
HeraldWebhookRequest $request,
|
||||
$error_type,
|
||||
$error_code) {
|
||||
|
||||
$request
|
||||
->setStatus(HeraldWebhookRequest::STATUS_FAILED)
|
||||
->setErrorType($error_type)
|
||||
->setErrorCode($error_code)
|
||||
->setLastRequestResult(HeraldWebhookRequest::RESULT_NONE)
|
||||
->setLastRequestEpoch(0)
|
||||
->save();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
final class HeraldWebhookNameTransaction
|
||||
extends HeraldWebhookTransactionType {
|
||||
|
||||
const TRANSACTIONTYPE = 'name';
|
||||
|
||||
public function generateOldValue($object) {
|
||||
return $object->getName();
|
||||
}
|
||||
|
||||
public function applyInternalEffects($object, $value) {
|
||||
$object->setName($value);
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
return pht(
|
||||
'%s renamed this webhook from %s to %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderOldValue(),
|
||||
$this->renderNewValue());
|
||||
}
|
||||
|
||||
public function getTitleForFeed() {
|
||||
return pht(
|
||||
'%s renamed %s from %s to %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject(),
|
||||
$this->renderOldValue(),
|
||||
$this->renderNewValue());
|
||||
}
|
||||
|
||||
public function validateTransactions($object, array $xactions) {
|
||||
$errors = array();
|
||||
$viewer = $this->getActor();
|
||||
|
||||
if ($this->isEmptyTextTransaction($object->getName(), $xactions)) {
|
||||
$errors[] = $this->newRequiredError(
|
||||
pht('Webhooks must have a name.'));
|
||||
return $errors;
|
||||
}
|
||||
|
||||
$max_length = $object->getColumnMaximumByteLength('name');
|
||||
foreach ($xactions as $xaction) {
|
||||
$old_value = $this->generateOldValue($object);
|
||||
$new_value = $xaction->getNewValue();
|
||||
|
||||
$new_length = strlen($new_value);
|
||||
if ($new_length > $max_length) {
|
||||
$errors[] = $this->newInvalidError(
|
||||
pht(
|
||||
'Webhook names can be no longer than %s characters.',
|
||||
new PhutilNumber($max_length)));
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
final class HeraldWebhookStatusTransaction
|
||||
extends HeraldWebhookTransactionType {
|
||||
|
||||
const TRANSACTIONTYPE = 'status';
|
||||
|
||||
public function generateOldValue($object) {
|
||||
return $object->getStatus();
|
||||
}
|
||||
|
||||
public function applyInternalEffects($object, $value) {
|
||||
$object->setStatus($value);
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
$old_value = $this->getOldValue();
|
||||
$new_value = $this->getNewValue();
|
||||
|
||||
$old_status = HeraldWebhook::getDisplayNameForStatus($old_value);
|
||||
$new_status = HeraldWebhook::getDisplayNameForStatus($new_value);
|
||||
|
||||
return pht(
|
||||
'%s changed hook status from %s to %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderValue($old_status),
|
||||
$this->renderValue($new_status));
|
||||
}
|
||||
|
||||
public function getTitleForFeed() {
|
||||
$old_value = $this->getOldValue();
|
||||
$new_value = $this->getNewValue();
|
||||
|
||||
$old_status = HeraldWebhook::getDisplayNameForStatus($old_value);
|
||||
$new_status = HeraldWebhook::getDisplayNameForStatus($new_value);
|
||||
|
||||
return pht(
|
||||
'%s changed %s from %s to %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject(),
|
||||
$this->renderValue($old_status),
|
||||
$this->renderValue($new_status));
|
||||
}
|
||||
|
||||
public function validateTransactions($object, array $xactions) {
|
||||
$errors = array();
|
||||
$viewer = $this->getActor();
|
||||
|
||||
$options = HeraldWebhook::getStatusDisplayNameMap();
|
||||
|
||||
foreach ($xactions as $xaction) {
|
||||
$new_value = $xaction->getNewValue();
|
||||
|
||||
if (!isset($options[$new_value])) {
|
||||
$errors[] = $this->newInvalidError(
|
||||
pht(
|
||||
'Webhook status "%s" is not valid. Valid statuses are: %s.',
|
||||
$new_value,
|
||||
implode(', ', array_keys($options))),
|
||||
$xaction);
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<?php
|
||||
|
||||
abstract class HeraldWebhookTransactionType
|
||||
extends PhabricatorModularTransactionType {}
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
final class HeraldWebhookURITransaction
|
||||
extends HeraldWebhookTransactionType {
|
||||
|
||||
const TRANSACTIONTYPE = 'uri';
|
||||
|
||||
public function generateOldValue($object) {
|
||||
return $object->getWebhookURI();
|
||||
}
|
||||
|
||||
public function applyInternalEffects($object, $value) {
|
||||
$object->setWebhookURI($value);
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
return pht(
|
||||
'%s changed the URI for this webhook from %s to %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderOldValue(),
|
||||
$this->renderNewValue());
|
||||
}
|
||||
|
||||
public function getTitleForFeed() {
|
||||
return pht(
|
||||
'%s changed the URI for %s from %s to %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject(),
|
||||
$this->renderOldValue(),
|
||||
$this->renderNewValue());
|
||||
}
|
||||
|
||||
public function validateTransactions($object, array $xactions) {
|
||||
$errors = array();
|
||||
$viewer = $this->getActor();
|
||||
|
||||
if ($this->isEmptyTextTransaction($object->getName(), $xactions)) {
|
||||
$errors[] = $this->newRequiredError(
|
||||
pht('Webhooks must have a URI.'));
|
||||
return $errors;
|
||||
}
|
||||
|
||||
$max_length = $object->getColumnMaximumByteLength('webhookURI');
|
||||
foreach ($xactions as $xaction) {
|
||||
$old_value = $this->generateOldValue($object);
|
||||
$new_value = $xaction->getNewValue();
|
||||
|
||||
$new_length = strlen($new_value);
|
||||
if ($new_length > $max_length) {
|
||||
$errors[] = $this->newInvalidError(
|
||||
pht(
|
||||
'Webhook URIs can be no longer than %s characters.',
|
||||
new PhutilNumber($max_length)),
|
||||
$xaction);
|
||||
}
|
||||
|
||||
try {
|
||||
PhabricatorEnv::requireValidRemoteURIForFetch(
|
||||
$new_value,
|
||||
array(
|
||||
'http',
|
||||
'https',
|
||||
));
|
||||
} catch (Exception $ex) {
|
||||
$errors[] = $this->newInvalidError(
|
||||
$ex->getMessage(),
|
||||
$xaction);
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
}
|
|
@ -124,12 +124,10 @@ final class LegalpadDocumentEditor
|
|||
|
||||
protected function buildMailTemplate(PhabricatorLiskDAO $object) {
|
||||
$id = $object->getID();
|
||||
$phid = $object->getPHID();
|
||||
$title = $object->getDocumentBody()->getTitle();
|
||||
|
||||
return id(new PhabricatorMetaMTAMail())
|
||||
->setSubject("L{$id}: {$title}")
|
||||
->addHeader('Thread-Topic', "L{$id}: {$phid}");
|
||||
->setSubject("L{$id}: {$title}");
|
||||
}
|
||||
|
||||
protected function getMailTo(PhabricatorLiskDAO $object) {
|
||||
|
|
|
@ -176,7 +176,7 @@ final class LegalpadDocumentSearchEngine
|
|||
$create_button = id(new PHUIButtonView())
|
||||
->setTag('a')
|
||||
->setText(pht('Create a Document'))
|
||||
->setHref('/legalpad/create/')
|
||||
->setHref('/legalpad/edit/')
|
||||
->setColor(PHUIButtonView::GREEN);
|
||||
|
||||
$icon = $this->getApplication()->getIcon();
|
||||
|
|
|
@ -35,8 +35,7 @@ final class PhabricatorMacroEditor
|
|||
$name = 'Image Macro "'.$name.'"';
|
||||
|
||||
return id(new PhabricatorMetaMTAMail())
|
||||
->setSubject($name)
|
||||
->addHeader('Thread-Topic', $name);
|
||||
->setSubject($name);
|
||||
}
|
||||
|
||||
protected function getMailTo(PhabricatorLiskDAO $object) {
|
||||
|
|
|
@ -8,7 +8,7 @@ final class PhabricatorImageMacroRemarkupRule extends PhutilRemarkupRule {
|
|||
|
||||
public function apply($text) {
|
||||
return preg_replace_callback(
|
||||
'@^\s*([a-zA-Z0-9:_\-]+)$@m',
|
||||
'@^\s*([a-zA-Z0-9:_\x7f-\xff-]+)$@m',
|
||||
array($this, 'markupImageMacro'),
|
||||
$text);
|
||||
}
|
||||
|
|
|
@ -52,12 +52,16 @@ final class PhabricatorMacroNameTransaction
|
|||
new PhutilNumber($max_length)));
|
||||
}
|
||||
|
||||
if (!preg_match('/^[a-z0-9:_-]{3,}\z/', $new_value)) {
|
||||
$errors[] = $this->newInvalidError(
|
||||
pht('Macro name "%s" be at least three characters long and contain '.
|
||||
'only lowercase letters, digits, hyphens, colons and '.
|
||||
'underscores.',
|
||||
$new_value));
|
||||
if (!self::isValidMacroName($new_value)) {
|
||||
// This says "emoji", but the actual rule we implement is "all other
|
||||
// unicode characters are also fine".
|
||||
$errors[] = $this->newInvalidError(
|
||||
pht(
|
||||
'Macro name "%s" be: at least three characters long; and contain '.
|
||||
'only lowercase letters, digits, hyphens, colons, underscores, '.
|
||||
'and emoji; and not be composed entirely of latin symbols.',
|
||||
$new_value),
|
||||
$xaction);
|
||||
}
|
||||
|
||||
// Check name is unique when updating / creating
|
||||
|
@ -78,4 +82,35 @@ final class PhabricatorMacroNameTransaction
|
|||
return $errors;
|
||||
}
|
||||
|
||||
public static function isValidMacroName($name) {
|
||||
if (preg_match('/^[:_-]+\z/', $name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Accept trivial macro names.
|
||||
if (preg_match('/^[a-z0-9:_-]{3,}\z/', $name)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Reject names with fewer than 3 glyphs.
|
||||
$length = phutil_utf8v_combined($name);
|
||||
if (count($length) < 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check character-by-character for any symbols that we don't want.
|
||||
$characters = phutil_utf8v($name);
|
||||
foreach ($characters as $character) {
|
||||
if (ord($character[0]) > 0x7F) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (preg_match('/^[^a-z0-9:_-]/', $character)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorMacroTestCase
|
||||
extends PhabricatorTestCase {
|
||||
|
||||
public function testMacroNames() {
|
||||
$lit = "\xF0\x9F\x94\xA5";
|
||||
$combining_diaeresis = "\xCC\x88";
|
||||
|
||||
$cases = array(
|
||||
// Only 2 glyphs long.
|
||||
"u{$combining_diaeresis}n" => false,
|
||||
"{$lit}{$lit}" => false,
|
||||
|
||||
// Too short.
|
||||
'a' => false,
|
||||
'' => false,
|
||||
|
||||
// Bad characters.
|
||||
'yes!' => false,
|
||||
"{$lit} {$lit} {$lit}" => false,
|
||||
"aaa\nbbb" => false,
|
||||
'aaa~' => false,
|
||||
'aaa`' => false,
|
||||
|
||||
// Special rejections for only latin symbols.
|
||||
'---' => false,
|
||||
'___' => false,
|
||||
'-_-' => false,
|
||||
':::' => false,
|
||||
'-_:' => false,
|
||||
|
||||
"{$lit}{$lit}{$lit}" => true,
|
||||
'bwahahaha' => true,
|
||||
"u{$combining_diaeresis}nt" => true,
|
||||
'a-a-a-a' => true,
|
||||
);
|
||||
|
||||
foreach ($cases as $input => $expect) {
|
||||
$this->assertEqual(
|
||||
$expect,
|
||||
PhabricatorMacroNameTransaction::isValidMacroName($input),
|
||||
pht('Validity of macro "%s"', $input));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -206,8 +206,7 @@ final class ManiphestTransactionEditor
|
|||
$title = $object->getTitle();
|
||||
|
||||
return id(new PhabricatorMetaMTAMail())
|
||||
->setSubject("T{$id}: {$title}")
|
||||
->addHeader('Thread-Topic', "T{$id}: ".$object->getOriginalTitle());
|
||||
->setSubject("T{$id}: {$title}");
|
||||
}
|
||||
|
||||
protected function buildMailBody(
|
||||
|
@ -523,7 +522,6 @@ final class ManiphestTransactionEditor
|
|||
'status' => '""',
|
||||
'priority' => 0,
|
||||
'title' => '""',
|
||||
'originalTitle' => '""',
|
||||
'description' => '""',
|
||||
'dateCreated' => 0,
|
||||
'dateModified' => 0,
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
final class ManiphestMailEngineExtension
|
||||
extends PhabricatorMailEngineExtension {
|
||||
|
||||
const EXTENSIONKEY = 'maniphest';
|
||||
|
||||
public function supportsObject($object) {
|
||||
return ($object instanceof ManiphestTask);
|
||||
}
|
||||
|
||||
public function newMailStampTemplates($object) {
|
||||
return array(
|
||||
id(new PhabricatorPHIDMailStamp())
|
||||
->setKey('author')
|
||||
->setLabel(pht('Author')),
|
||||
id(new PhabricatorPHIDMailStamp())
|
||||
->setKey('task-owner')
|
||||
->setLabel(pht('Task Owner')),
|
||||
id(new PhabricatorBoolMailStamp())
|
||||
->setKey('task-unassigned')
|
||||
->setLabel(pht('Task Unassigned')),
|
||||
id(new PhabricatorStringMailStamp())
|
||||
->setKey('task-priority')
|
||||
->setLabel(pht('Task Priority')),
|
||||
id(new PhabricatorStringMailStamp())
|
||||
->setKey('task-status')
|
||||
->setLabel(pht('Task Status')),
|
||||
id(new PhabricatorStringMailStamp())
|
||||
->setKey('subtype')
|
||||
->setLabel(pht('Subtype')),
|
||||
);
|
||||
}
|
||||
|
||||
public function newMailStamps($object, array $xactions) {
|
||||
$editor = $this->getEditor();
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$this->getMailStamp('author')
|
||||
->setValue($object->getAuthorPHID());
|
||||
|
||||
$this->getMailStamp('task-owner')
|
||||
->setValue($object->getOwnerPHID());
|
||||
|
||||
$this->getMailStamp('task-unassigned')
|
||||
->setValue(!$object->getOwnerPHID());
|
||||
|
||||
$this->getMailStamp('task-priority')
|
||||
->setValue($object->getPriority());
|
||||
|
||||
$this->getMailStamp('task-status')
|
||||
->setValue($object->getStatus());
|
||||
|
||||
$this->getMailStamp('subtype')
|
||||
->setValue($object->getSubtype());
|
||||
}
|
||||
|
||||
}
|
|
@ -23,6 +23,9 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
|||
private $parentTaskIDs;
|
||||
private $subtaskIDs;
|
||||
private $subtypes;
|
||||
private $closedEpochMin;
|
||||
private $closedEpochMax;
|
||||
private $closerPHIDs;
|
||||
|
||||
private $status = 'status-any';
|
||||
const STATUS_ANY = 'status-any';
|
||||
|
@ -179,6 +182,17 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function withClosedEpochBetween($min, $max) {
|
||||
$this->closedEpochMin = $min;
|
||||
$this->closedEpochMax = $max;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withCloserPHIDs(array $phids) {
|
||||
$this->closerPHIDs = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function needSubscriberPHIDs($bool) {
|
||||
$this->needSubscriberPHIDs = $bool;
|
||||
return $this;
|
||||
|
@ -379,6 +393,27 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
|||
$this->dateModifiedBefore);
|
||||
}
|
||||
|
||||
if ($this->closedEpochMin !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'task.closedEpoch >= %d',
|
||||
$this->closedEpochMin);
|
||||
}
|
||||
|
||||
if ($this->closedEpochMax !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'task.closedEpoch <= %d',
|
||||
$this->closedEpochMax);
|
||||
}
|
||||
|
||||
if ($this->closerPHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'task.closerPHID IN (%Ls)',
|
||||
$this->closerPHIDs);
|
||||
}
|
||||
|
||||
if ($this->priorities !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
|
@ -722,7 +757,11 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
|||
'outdated' => array(
|
||||
'vector' => array('-updated', '-id'),
|
||||
'name' => pht('Date Updated (Oldest First)'),
|
||||
),
|
||||
),
|
||||
'closed' => array(
|
||||
'vector' => array('closed', 'id'),
|
||||
'name' => pht('Date Closed (Latest First)'),
|
||||
),
|
||||
'title' => array(
|
||||
'vector' => array('title', 'id'),
|
||||
'name' => pht('Title'),
|
||||
|
@ -741,6 +780,7 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
|||
'outdated',
|
||||
'newest',
|
||||
'oldest',
|
||||
'closed',
|
||||
'title',
|
||||
)) + $orders;
|
||||
|
||||
|
@ -790,6 +830,12 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
|||
'column' => 'dateModified',
|
||||
'type' => 'int',
|
||||
),
|
||||
'closed' => array(
|
||||
'table' => 'task',
|
||||
'column' => 'closedEpoch',
|
||||
'type' => 'int',
|
||||
'null' => 'tail',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -808,6 +854,7 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
|||
'status' => $task->getStatus(),
|
||||
'title' => $task->getTitle(),
|
||||
'updated' => $task->getDateModified(),
|
||||
'closed' => $task->getClosedEpoch(),
|
||||
);
|
||||
|
||||
foreach ($keys as $key) {
|
||||
|
|
|
@ -126,6 +126,17 @@ final class ManiphestTaskSearchEngine
|
|||
id(new PhabricatorSearchDateField())
|
||||
->setLabel(pht('Updated Before'))
|
||||
->setKey('modifiedEnd'),
|
||||
id(new PhabricatorSearchDateField())
|
||||
->setLabel(pht('Closed After'))
|
||||
->setKey('closedStart'),
|
||||
id(new PhabricatorSearchDateField())
|
||||
->setLabel(pht('Closed Before'))
|
||||
->setKey('closedEnd'),
|
||||
id(new PhabricatorUsersSearchField())
|
||||
->setLabel(pht('Closed By'))
|
||||
->setKey('closerPHIDs')
|
||||
->setAliases(array('closer', 'closerPHID', 'closers'))
|
||||
->setDescription(pht('Search for tasks closed by certain users.')),
|
||||
id(new PhabricatorSearchTextField())
|
||||
->setLabel(pht('Page Size'))
|
||||
->setKey('limit'),
|
||||
|
@ -153,6 +164,9 @@ final class ManiphestTaskSearchEngine
|
|||
'createdEnd',
|
||||
'modifiedStart',
|
||||
'modifiedEnd',
|
||||
'closedStart',
|
||||
'closedEnd',
|
||||
'closerPHIDs',
|
||||
'limit',
|
||||
);
|
||||
}
|
||||
|
@ -208,6 +222,14 @@ final class ManiphestTaskSearchEngine
|
|||
$query->withDateModifiedBefore($map['modifiedEnd']);
|
||||
}
|
||||
|
||||
if ($map['closedStart'] || $map['closedEnd']) {
|
||||
$query->withClosedEpochBetween($map['closedStart'], $map['closedEnd']);
|
||||
}
|
||||
|
||||
if ($map['closerPHIDs']) {
|
||||
$query->withCloserPHIDs($map['closerPHIDs']);
|
||||
}
|
||||
|
||||
if ($map['hasParents'] !== null) {
|
||||
$query->withOpenParents($map['hasParents']);
|
||||
}
|
||||
|
@ -456,6 +478,15 @@ final class ManiphestTaskSearchEngine
|
|||
id(new PhabricatorStringExportField())
|
||||
->setKey('statusName')
|
||||
->setLabel(pht('Status Name')),
|
||||
id(new PhabricatorEpochExportField())
|
||||
->setKey('dateClosed')
|
||||
->setLabel(pht('Date Closed')),
|
||||
id(new PhabricatorPHIDExportField())
|
||||
->setKey('closerPHID')
|
||||
->setLabel(pht('Closer PHID')),
|
||||
id(new PhabricatorStringExportField())
|
||||
->setKey('closer')
|
||||
->setLabel(pht('Closer')),
|
||||
id(new PhabricatorStringExportField())
|
||||
->setKey('priority')
|
||||
->setLabel(pht('Priority')),
|
||||
|
@ -492,6 +523,7 @@ final class ManiphestTaskSearchEngine
|
|||
foreach ($tasks as $task) {
|
||||
$phids[] = $task->getAuthorPHID();
|
||||
$phids[] = $task->getOwnerPHID();
|
||||
$phids[] = $task->getCloserPHID();
|
||||
}
|
||||
$handles = $viewer->loadHandles($phids);
|
||||
|
||||
|
@ -512,6 +544,13 @@ final class ManiphestTaskSearchEngine
|
|||
$owner_name = null;
|
||||
}
|
||||
|
||||
$closer_phid = $task->getCloserPHID();
|
||||
if ($closer_phid) {
|
||||
$closer_name = $handles[$closer_phid]->getName();
|
||||
} else {
|
||||
$closer_name = null;
|
||||
}
|
||||
|
||||
$status_value = $task->getStatus();
|
||||
$status_name = ManiphestTaskStatus::getTaskStatusName($status_value);
|
||||
|
||||
|
@ -534,6 +573,9 @@ final class ManiphestTaskSearchEngine
|
|||
'title' => $task->getTitle(),
|
||||
'uri' => PhabricatorEnv::getProductionURI($task->getURI()),
|
||||
'description' => $task->getDescription(),
|
||||
'dateClosed' => $task->getClosedEpoch(),
|
||||
'closerPHID' => $closer_phid,
|
||||
'closer' => $closer_name,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,6 @@ final class ManiphestTask extends ManiphestDAO
|
|||
protected $subpriority = 0;
|
||||
|
||||
protected $title = '';
|
||||
protected $originalTitle = '';
|
||||
protected $description = '';
|
||||
protected $originalEmailSource;
|
||||
protected $mailKey;
|
||||
|
@ -45,6 +44,9 @@ final class ManiphestTask extends ManiphestDAO
|
|||
protected $points;
|
||||
protected $subtype;
|
||||
|
||||
protected $closedEpoch;
|
||||
protected $closerPHID;
|
||||
|
||||
private $subscriberPHIDs = self::ATTACHABLE;
|
||||
private $groupByProjectPHID = self::ATTACHABLE;
|
||||
private $customFields = self::ATTACHABLE;
|
||||
|
@ -83,7 +85,6 @@ final class ManiphestTask extends ManiphestDAO
|
|||
'status' => 'text64',
|
||||
'priority' => 'uint32',
|
||||
'title' => 'sort',
|
||||
'originalTitle' => 'text',
|
||||
'description' => 'text',
|
||||
'mailKey' => 'bytes20',
|
||||
'ownerOrdering' => 'text64?',
|
||||
|
@ -92,6 +93,8 @@ final class ManiphestTask extends ManiphestDAO
|
|||
'points' => 'double?',
|
||||
'bridgedObjectPHID' => 'phid?',
|
||||
'subtype' => 'text64',
|
||||
'closedEpoch' => 'epoch?',
|
||||
'closerPHID' => 'phid?',
|
||||
),
|
||||
self::CONFIG_KEY_SCHEMA => array(
|
||||
'key_phid' => null,
|
||||
|
@ -133,6 +136,12 @@ final class ManiphestTask extends ManiphestDAO
|
|||
'key_subtype' => array(
|
||||
'columns' => array('subtype'),
|
||||
),
|
||||
'key_closed' => array(
|
||||
'columns' => array('closedEpoch'),
|
||||
),
|
||||
'key_closer' => array(
|
||||
'columns' => array('closerPHID', 'closedEpoch'),
|
||||
),
|
||||
),
|
||||
) + parent::getConfiguration();
|
||||
}
|
||||
|
@ -176,14 +185,6 @@ final class ManiphestTask extends ManiphestDAO
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function setTitle($title) {
|
||||
$this->title = $title;
|
||||
if (!$this->getID()) {
|
||||
$this->originalTitle = $title;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMonogram() {
|
||||
return 'T'.$this->getID();
|
||||
}
|
||||
|
@ -512,6 +513,16 @@ final class ManiphestTask extends ManiphestDAO
|
|||
->setKey('subtype')
|
||||
->setType('string')
|
||||
->setDescription(pht('Subtype of the task.')),
|
||||
id(new PhabricatorConduitSearchFieldSpecification())
|
||||
->setKey('closerPHID')
|
||||
->setType('phid?')
|
||||
->setDescription(
|
||||
pht('User who closed the task, if the task is closed.')),
|
||||
id(new PhabricatorConduitSearchFieldSpecification())
|
||||
->setKey('dateClosed')
|
||||
->setType('int?')
|
||||
->setDescription(
|
||||
pht('Epoch timestamp when the task was closed.')),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -531,6 +542,11 @@ final class ManiphestTask extends ManiphestDAO
|
|||
'color' => ManiphestTaskPriority::getTaskPriorityColor($priority_value),
|
||||
);
|
||||
|
||||
$closed_epoch = $this->getClosedEpoch();
|
||||
if ($closed_epoch !== null) {
|
||||
$closed_epoch = (int)$closed_epoch;
|
||||
}
|
||||
|
||||
return array(
|
||||
'name' => $this->getTitle(),
|
||||
'description' => array(
|
||||
|
@ -542,6 +558,8 @@ final class ManiphestTask extends ManiphestDAO
|
|||
'priority' => $priority_info,
|
||||
'points' => $this->getPoints(),
|
||||
'subtype' => $this->getSubtype(),
|
||||
'closerPHID' => $this->getCloserPHID(),
|
||||
'dateClosed' => $closed_epoch,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -86,9 +86,24 @@ final class ManiphestTaskListView extends ManiphestView {
|
|||
|
||||
$item->setStatusIcon($icon.' '.$color, $tooltip);
|
||||
|
||||
$item->addIcon(
|
||||
'none',
|
||||
phabricator_datetime($task->getDateModified(), $this->getUser()));
|
||||
if ($task->isClosed()) {
|
||||
$closed_epoch = $task->getClosedEpoch();
|
||||
|
||||
// We don't expect a task to be closed without a closed epoch, but
|
||||
// recover if we find one. This can happen with older objects or with
|
||||
// lipsum test data.
|
||||
if (!$closed_epoch) {
|
||||
$closed_epoch = $task->getDateModified();
|
||||
}
|
||||
|
||||
$item->addIcon(
|
||||
'fa-check-square-o grey',
|
||||
phabricator_datetime($closed_epoch, $this->getUser()));
|
||||
} else {
|
||||
$item->addIcon(
|
||||
'none',
|
||||
phabricator_datetime($task->getDateModified(), $this->getUser()));
|
||||
}
|
||||
|
||||
if ($this->showSubpriorityControls) {
|
||||
$item->setGrippable(true);
|
||||
|
|
|
@ -10,7 +10,7 @@ final class ManiphestTaskMergedIntoTransaction
|
|||
}
|
||||
|
||||
public function applyInternalEffects($object, $value) {
|
||||
$object->setStatus(ManiphestTaskStatus::getDuplicateStatus());
|
||||
$this->updateStatus($object, ManiphestTaskStatus::getDuplicateStatus());
|
||||
}
|
||||
|
||||
public function getActionName() {
|
||||
|
|
|
@ -10,7 +10,7 @@ final class ManiphestTaskStatusTransaction
|
|||
}
|
||||
|
||||
public function applyInternalEffects($object, $value) {
|
||||
$object->setStatus($value);
|
||||
$this->updateStatus($object, $value);
|
||||
}
|
||||
|
||||
public function shouldHide() {
|
||||
|
|
|
@ -3,4 +3,27 @@
|
|||
abstract class ManiphestTaskTransactionType
|
||||
extends PhabricatorModularTransactionType {
|
||||
|
||||
protected function updateStatus($object, $new_value) {
|
||||
$old_value = $object->getStatus();
|
||||
$object->setStatus($new_value);
|
||||
|
||||
// If this status change closes or opens the task, update the closed
|
||||
// date and actor PHID.
|
||||
$old_closed = ManiphestTaskStatus::isClosedStatus($old_value);
|
||||
$new_closed = ManiphestTaskStatus::isClosedStatus($new_value);
|
||||
|
||||
$is_close = ($new_closed && !$old_closed);
|
||||
$is_open = (!$new_closed && $old_closed);
|
||||
|
||||
if ($is_close) {
|
||||
$object
|
||||
->setClosedEpoch(PhabricatorTime::getNow())
|
||||
->setCloserPHID($this->getActingAsPHID());
|
||||
} else if ($is_open) {
|
||||
$object
|
||||
->setClosedEpoch(null)
|
||||
->setCloserPHID(null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,6 +2,22 @@
|
|||
|
||||
abstract class PhabricatorMailImplementationAdapter extends Phobject {
|
||||
|
||||
private $key;
|
||||
private $priority;
|
||||
private $options = array();
|
||||
|
||||
final public function getAdapterType() {
|
||||
return $this->getPhobjectClassConstant('ADAPTERTYPE');
|
||||
}
|
||||
|
||||
final public static function getAllAdapters() {
|
||||
return id(new PhutilClassMapQuery())
|
||||
->setAncestorClass(__CLASS__)
|
||||
->setUniqueMethod('getAdapterType')
|
||||
->execute();
|
||||
}
|
||||
|
||||
|
||||
abstract public function setFrom($email, $name = '');
|
||||
abstract public function addReplyTo($email, $name = '');
|
||||
abstract public function addTos(array $emails);
|
||||
|
@ -12,6 +28,7 @@ abstract class PhabricatorMailImplementationAdapter extends Phobject {
|
|||
abstract public function setHTMLBody($html_body);
|
||||
abstract public function setSubject($subject);
|
||||
|
||||
|
||||
/**
|
||||
* Some mailers, notably Amazon SES, do not support us setting a specific
|
||||
* Message-ID header.
|
||||
|
@ -32,4 +49,59 @@ abstract class PhabricatorMailImplementationAdapter extends Phobject {
|
|||
*/
|
||||
abstract public function send();
|
||||
|
||||
final public function setKey($key) {
|
||||
$this->key = $key;
|
||||
return $this;
|
||||
}
|
||||
|
||||
final public function getKey() {
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
final public function setPriority($priority) {
|
||||
$this->priority = $priority;
|
||||
return $this;
|
||||
}
|
||||
|
||||
final public function getPriority() {
|
||||
return $this->priority;
|
||||
}
|
||||
|
||||
final public function getOption($key) {
|
||||
if (!array_key_exists($key, $this->options)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Mailer ("%s") is attempting to access unknown option ("%s").',
|
||||
get_class($this),
|
||||
$key));
|
||||
}
|
||||
|
||||
return $this->options[$key];
|
||||
}
|
||||
|
||||
final public function setOptions(array $options) {
|
||||
$this->validateOptions($options);
|
||||
$this->options = $options;
|
||||
return $this;
|
||||
}
|
||||
|
||||
abstract protected function validateOptions(array $options);
|
||||
|
||||
abstract public function newDefaultOptions();
|
||||
abstract public function newLegacyOptions();
|
||||
|
||||
public function prepareForSend() {
|
||||
return;
|
||||
}
|
||||
|
||||
protected function renderAddress($email, $name = null) {
|
||||
if (strlen($name)) {
|
||||
return (string)id(new PhutilEmailAddress())
|
||||
->setDisplayName($name)
|
||||
->setAddress($email);
|
||||
} else {
|
||||
return $email;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,11 +3,13 @@
|
|||
final class PhabricatorMailImplementationAmazonSESAdapter
|
||||
extends PhabricatorMailImplementationPHPMailerLiteAdapter {
|
||||
|
||||
const ADAPTERTYPE = 'ses';
|
||||
|
||||
private $message;
|
||||
private $isHTML;
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
public function prepareForSend() {
|
||||
parent::prepareForSend();
|
||||
$this->mailer->Mailer = 'amazon-ses';
|
||||
$this->mailer->customMailer = $this;
|
||||
}
|
||||
|
@ -17,13 +19,39 @@ final class PhabricatorMailImplementationAmazonSESAdapter
|
|||
return false;
|
||||
}
|
||||
|
||||
protected function validateOptions(array $options) {
|
||||
PhutilTypeSpec::checkMap(
|
||||
$options,
|
||||
array(
|
||||
'access-key' => 'string',
|
||||
'secret-key' => 'string',
|
||||
'endpoint' => 'string',
|
||||
));
|
||||
}
|
||||
|
||||
public function newDefaultOptions() {
|
||||
return array(
|
||||
'access-key' => null,
|
||||
'secret-key' => null,
|
||||
'endpoint' => null,
|
||||
);
|
||||
}
|
||||
|
||||
public function newLegacyOptions() {
|
||||
return array(
|
||||
'access-key' => PhabricatorEnv::getEnvConfig('amazon-ses.access-key'),
|
||||
'secret-key' => PhabricatorEnv::getEnvConfig('amazon-ses.secret-key'),
|
||||
'endpoint' => PhabricatorEnv::getEnvConfig('amazon-ses.endpoint'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @phutil-external-symbol class SimpleEmailService
|
||||
*/
|
||||
public function executeSend($body) {
|
||||
$key = PhabricatorEnv::getEnvConfig('amazon-ses.access-key');
|
||||
$secret = PhabricatorEnv::getEnvConfig('amazon-ses.secret-key');
|
||||
$endpoint = PhabricatorEnv::getEnvConfig('amazon-ses.endpoint');
|
||||
$key = $this->getOption('access-key');
|
||||
$secret = $this->getOption('secret-key');
|
||||
$endpoint = $this->getOption('endpoint');
|
||||
|
||||
$root = phutil_get_library_root('phabricator');
|
||||
$root = dirname($root);
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
final class PhabricatorMailImplementationMailgunAdapter
|
||||
extends PhabricatorMailImplementationAdapter {
|
||||
|
||||
const ADAPTERTYPE = 'mailgun';
|
||||
|
||||
private $params = array();
|
||||
private $attachments = array();
|
||||
|
||||
|
@ -19,7 +21,7 @@ final class PhabricatorMailImplementationMailgunAdapter
|
|||
if (empty($this->params['reply-to'])) {
|
||||
$this->params['reply-to'] = array();
|
||||
}
|
||||
$this->params['reply-to'][] = "{$name} <{$email}>";
|
||||
$this->params['reply-to'][] = $this->renderAddress($email, $name);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -71,9 +73,32 @@ final class PhabricatorMailImplementationMailgunAdapter
|
|||
return true;
|
||||
}
|
||||
|
||||
protected function validateOptions(array $options) {
|
||||
PhutilTypeSpec::checkMap(
|
||||
$options,
|
||||
array(
|
||||
'api-key' => 'string',
|
||||
'domain' => 'string',
|
||||
));
|
||||
}
|
||||
|
||||
public function newDefaultOptions() {
|
||||
return array(
|
||||
'api-key' => null,
|
||||
'domain' => null,
|
||||
);
|
||||
}
|
||||
|
||||
public function newLegacyOptions() {
|
||||
return array(
|
||||
'api-key' => PhabricatorEnv::getEnvConfig('mailgun.api-key'),
|
||||
'domain' => PhabricatorEnv::getEnvConfig('mailgun.domain'),
|
||||
);
|
||||
}
|
||||
|
||||
public function send() {
|
||||
$key = PhabricatorEnv::getEnvConfig('mailgun.api-key');
|
||||
$domain = PhabricatorEnv::getEnvConfig('mailgun.domain');
|
||||
$key = $this->getOption('api-key');
|
||||
$domain = $this->getOption('domain');
|
||||
$params = array();
|
||||
|
||||
$params['to'] = implode(', ', idx($this->params, 'tos', array()));
|
||||
|
@ -85,11 +110,8 @@ final class PhabricatorMailImplementationMailgunAdapter
|
|||
}
|
||||
|
||||
$from = idx($this->params, 'from');
|
||||
if (idx($this->params, 'from-name')) {
|
||||
$params['from'] = "\"{$this->params['from-name']}\" <{$from}>";
|
||||
} else {
|
||||
$params['from'] = $from;
|
||||
}
|
||||
$from_name = idx($this->params, 'from-name');
|
||||
$params['from'] = $this->renderAddress($from, $from_name);
|
||||
|
||||
if (idx($this->params, 'reply-to')) {
|
||||
$replyto = $this->params['reply-to'];
|
||||
|
|
|
@ -3,40 +3,79 @@
|
|||
final class PhabricatorMailImplementationPHPMailerAdapter
|
||||
extends PhabricatorMailImplementationAdapter {
|
||||
|
||||
const ADAPTERTYPE = 'smtp';
|
||||
|
||||
private $mailer;
|
||||
|
||||
protected function validateOptions(array $options) {
|
||||
PhutilTypeSpec::checkMap(
|
||||
$options,
|
||||
array(
|
||||
'host' => 'string|null',
|
||||
'port' => 'int',
|
||||
'user' => 'string|null',
|
||||
'password' => 'string|null',
|
||||
'protocol' => 'string|null',
|
||||
'encoding' => 'string',
|
||||
'mailer' => 'string',
|
||||
));
|
||||
}
|
||||
|
||||
public function newDefaultOptions() {
|
||||
return array(
|
||||
'host' => null,
|
||||
'port' => 25,
|
||||
'user' => null,
|
||||
'password' => null,
|
||||
'protocol' => null,
|
||||
'encoding' => 'base64',
|
||||
'mailer' => 'smtp',
|
||||
);
|
||||
}
|
||||
|
||||
public function newLegacyOptions() {
|
||||
return array(
|
||||
'host' => PhabricatorEnv::getEnvConfig('phpmailer.smtp-host'),
|
||||
'port' => PhabricatorEnv::getEnvConfig('phpmailer.smtp-port'),
|
||||
'user' => PhabricatorEnv::getEnvConfig('phpmailer.smtp-user'),
|
||||
'password' => PhabricatorEnv::getEnvConfig('phpmailer.smtp-passsword'),
|
||||
'protocol' => PhabricatorEnv::getEnvConfig('phpmailer.smtp-protocol'),
|
||||
'encoding' => PhabricatorEnv::getEnvConfig('phpmailer.smtp-encoding'),
|
||||
'mailer' => PhabricatorEnv::getEnvConfig('phpmailer.mailer'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @phutil-external-symbol class PHPMailer
|
||||
*/
|
||||
public function __construct() {
|
||||
public function prepareForSend() {
|
||||
$root = phutil_get_library_root('phabricator');
|
||||
$root = dirname($root);
|
||||
require_once $root.'/externals/phpmailer/class.phpmailer.php';
|
||||
$this->mailer = new PHPMailer($use_exceptions = true);
|
||||
$this->mailer->CharSet = 'utf-8';
|
||||
|
||||
$encoding = PhabricatorEnv::getEnvConfig('phpmailer.smtp-encoding');
|
||||
$encoding = $this->getOption('encoding');
|
||||
$this->mailer->Encoding = $encoding;
|
||||
|
||||
// By default, PHPMailer sends one mail per recipient. We handle
|
||||
// multiplexing higher in the stack, so tell it to send mail exactly
|
||||
// like we ask.
|
||||
// combining or separating To and Cc higher in the stack, so tell it to
|
||||
// send mail exactly like we ask.
|
||||
$this->mailer->SingleTo = false;
|
||||
|
||||
$mailer = PhabricatorEnv::getEnvConfig('phpmailer.mailer');
|
||||
$mailer = $this->getOption('mailer');
|
||||
if ($mailer == 'smtp') {
|
||||
$this->mailer->IsSMTP();
|
||||
$this->mailer->Host = PhabricatorEnv::getEnvConfig('phpmailer.smtp-host');
|
||||
$this->mailer->Port = PhabricatorEnv::getEnvConfig('phpmailer.smtp-port');
|
||||
$user = PhabricatorEnv::getEnvConfig('phpmailer.smtp-user');
|
||||
$this->mailer->Host = $this->getOption('host');
|
||||
$this->mailer->Port = $this->getOption('port');
|
||||
$user = $this->getOption('user');
|
||||
if ($user) {
|
||||
$this->mailer->SMTPAuth = true;
|
||||
$this->mailer->Username = $user;
|
||||
$this->mailer->Password =
|
||||
PhabricatorEnv::getEnvConfig('phpmailer.smtp-password');
|
||||
$this->mailer->Password = $this->getOption('password');
|
||||
}
|
||||
|
||||
$protocol = PhabricatorEnv::getEnvConfig('phpmailer.smtp-protocol');
|
||||
$protocol = $this->getOption('protocol');
|
||||
if ($protocol) {
|
||||
$protocol = phutil_utf8_strtolower($protocol);
|
||||
$this->mailer->SMTPSecure = $protocol;
|
||||
|
|
|
@ -6,24 +6,46 @@
|
|||
class PhabricatorMailImplementationPHPMailerLiteAdapter
|
||||
extends PhabricatorMailImplementationAdapter {
|
||||
|
||||
const ADAPTERTYPE = 'sendmail';
|
||||
|
||||
protected $mailer;
|
||||
|
||||
protected function validateOptions(array $options) {
|
||||
PhutilTypeSpec::checkMap(
|
||||
$options,
|
||||
array(
|
||||
'encoding' => 'string',
|
||||
));
|
||||
}
|
||||
|
||||
public function newDefaultOptions() {
|
||||
return array(
|
||||
'encoding' => 'base64',
|
||||
);
|
||||
}
|
||||
|
||||
public function newLegacyOptions() {
|
||||
return array(
|
||||
'encoding' => PhabricatorEnv::getEnvConfig('phpmailer.smtp-encoding'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @phutil-external-symbol class PHPMailerLite
|
||||
*/
|
||||
public function __construct() {
|
||||
public function prepareForSend() {
|
||||
$root = phutil_get_library_root('phabricator');
|
||||
$root = dirname($root);
|
||||
require_once $root.'/externals/phpmailer/class.phpmailer-lite.php';
|
||||
$this->mailer = new PHPMailerLite($use_exceptions = true);
|
||||
$this->mailer->CharSet = 'utf-8';
|
||||
|
||||
$encoding = PhabricatorEnv::getEnvConfig('phpmailer.smtp-encoding');
|
||||
$encoding = $this->getOption('encoding');
|
||||
$this->mailer->Encoding = $encoding;
|
||||
|
||||
// By default, PHPMailerLite sends one mail per recipient. We handle
|
||||
// multiplexing higher in the stack, so tell it to send mail exactly
|
||||
// like we ask.
|
||||
// combining or separating To and Cc higher in the stack, so tell it to
|
||||
// send mail exactly like we ask.
|
||||
$this->mailer->SingleTo = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorMailImplementationPostmarkAdapter
|
||||
extends PhabricatorMailImplementationAdapter {
|
||||
|
||||
const ADAPTERTYPE = 'postmark';
|
||||
|
||||
private $parameters = array();
|
||||
|
||||
public function setFrom($email, $name = '') {
|
||||
$this->parameters['From'] = $this->renderAddress($email, $name);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addReplyTo($email, $name = '') {
|
||||
$this->parameters['ReplyTo'] = $this->renderAddress($email, $name);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addTos(array $emails) {
|
||||
foreach ($emails as $email) {
|
||||
$this->parameters['To'][] = $email;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addCCs(array $emails) {
|
||||
foreach ($emails as $email) {
|
||||
$this->parameters['Cc'][] = $email;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addAttachment($data, $filename, $mimetype) {
|
||||
$this->parameters['Attachments'][] = array(
|
||||
'Name' => $filename,
|
||||
'ContentType' => $mimetype,
|
||||
'Content' => base64_encode($data),
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addHeader($header_name, $header_value) {
|
||||
$this->parameters['Headers'][] = array(
|
||||
'Name' => $header_name,
|
||||
'Value' => $header_value,
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setBody($body) {
|
||||
$this->parameters['TextBody'] = $body;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setHTMLBody($html_body) {
|
||||
$this->parameters['HtmlBody'] = $html_body;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setSubject($subject) {
|
||||
$this->parameters['Subject'] = $subject;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function supportsMessageIDHeader() {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function validateOptions(array $options) {
|
||||
PhutilTypeSpec::checkMap(
|
||||
$options,
|
||||
array(
|
||||
'access-token' => 'string',
|
||||
'inbound-addresses' => 'list<string>',
|
||||
));
|
||||
|
||||
// Make sure this is properly formatted.
|
||||
PhutilCIDRList::newList($options['inbound-addresses']);
|
||||
}
|
||||
|
||||
public function newDefaultOptions() {
|
||||
return array(
|
||||
'access-token' => null,
|
||||
'inbound-addresses' => array(
|
||||
// Via Postmark support circa February 2018, see:
|
||||
//
|
||||
// https://postmarkapp.com/support/article/800-ips-for-firewalls
|
||||
//
|
||||
// "Configuring Outbound Email" should be updated if this changes.
|
||||
'50.31.156.6/32',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function newLegacyOptions() {
|
||||
return array();
|
||||
}
|
||||
|
||||
public function send() {
|
||||
$access_token = $this->getOption('access-token');
|
||||
|
||||
$parameters = $this->parameters;
|
||||
$flatten = array(
|
||||
'To',
|
||||
'Cc',
|
||||
);
|
||||
|
||||
foreach ($flatten as $key) {
|
||||
if (isset($parameters[$key])) {
|
||||
$parameters[$key] = implode(', ', $parameters[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
id(new PhutilPostmarkFuture())
|
||||
->setAccessToken($access_token)
|
||||
->setMethod('email', $parameters)
|
||||
->resolve();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -6,8 +6,33 @@
|
|||
final class PhabricatorMailImplementationSendGridAdapter
|
||||
extends PhabricatorMailImplementationAdapter {
|
||||
|
||||
const ADAPTERTYPE = 'sendgrid';
|
||||
|
||||
private $params = array();
|
||||
|
||||
protected function validateOptions(array $options) {
|
||||
PhutilTypeSpec::checkMap(
|
||||
$options,
|
||||
array(
|
||||
'api-user' => 'string',
|
||||
'api-key' => 'string',
|
||||
));
|
||||
}
|
||||
|
||||
public function newDefaultOptions() {
|
||||
return array(
|
||||
'api-user' => null,
|
||||
'api-key' => null,
|
||||
);
|
||||
}
|
||||
|
||||
public function newLegacyOptions() {
|
||||
return array(
|
||||
'api-user' => PhabricatorEnv::getEnvConfig('sendgrid.api-user'),
|
||||
'api-key' => PhabricatorEnv::getEnvConfig('sendgrid.api-key'),
|
||||
);
|
||||
}
|
||||
|
||||
public function setFrom($email, $name = '') {
|
||||
$this->params['from'] = $email;
|
||||
$this->params['from-name'] = $name;
|
||||
|
@ -73,8 +98,8 @@ final class PhabricatorMailImplementationSendGridAdapter
|
|||
|
||||
public function send() {
|
||||
|
||||
$user = PhabricatorEnv::getEnvConfig('sendgrid.api-user');
|
||||
$key = PhabricatorEnv::getEnvConfig('sendgrid.api-key');
|
||||
$user = $this->getOption('api-user');
|
||||
$key = $this->getOption('api-key');
|
||||
|
||||
if (!$user || !$key) {
|
||||
throw new Exception(
|
||||
|
|
|
@ -7,10 +7,26 @@
|
|||
final class PhabricatorMailImplementationTestAdapter
|
||||
extends PhabricatorMailImplementationAdapter {
|
||||
|
||||
private $guts = array();
|
||||
private $config;
|
||||
const ADAPTERTYPE = 'test';
|
||||
|
||||
public function __construct(array $config = array()) {
|
||||
private $guts = array();
|
||||
private $config = array();
|
||||
|
||||
protected function validateOptions(array $options) {
|
||||
PhutilTypeSpec::checkMap(
|
||||
$options,
|
||||
array());
|
||||
}
|
||||
|
||||
public function newDefaultOptions() {
|
||||
return array();
|
||||
}
|
||||
|
||||
public function newLegacyOptions() {
|
||||
return array();
|
||||
}
|
||||
|
||||
public function prepareForSend(array $config = array()) {
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ final class PhabricatorMetaMTAApplication extends PhabricatorApplication {
|
|||
'detail/(?P<id>[1-9]\d*)/' => 'PhabricatorMetaMTAMailViewController',
|
||||
'sendgrid/' => 'PhabricatorMetaMTASendGridReceiveController',
|
||||
'mailgun/' => 'PhabricatorMetaMTAMailgunReceiveController',
|
||||
'postmark/' => 'PhabricatorMetaMTAPostmarkReceiveController',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -32,6 +32,23 @@ final class PhabricatorMetaMTAMailViewController
|
|||
$color = PhabricatorMailOutboundStatus::getStatusColor($status);
|
||||
$header->setStatus($icon, $color, $name);
|
||||
|
||||
if ($mail->getMustEncrypt()) {
|
||||
Javelin::initBehavior('phabricator-tooltips');
|
||||
$header->addTag(
|
||||
id(new PHUITagView())
|
||||
->setType(PHUITagView::TYPE_SHADE)
|
||||
->setColor('blue')
|
||||
->setName(pht('Must Encrypt'))
|
||||
->setIcon('fa-shield blue')
|
||||
->addSigil('has-tooltip')
|
||||
->setMetadata(
|
||||
array(
|
||||
'tip' => pht(
|
||||
'Message content can only be transmitted over secure '.
|
||||
'channels.'),
|
||||
)));
|
||||
}
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs()
|
||||
->addTextCrumb(pht('Mail %d', $mail->getID()))
|
||||
->setBorder(true);
|
||||
|
@ -58,8 +75,26 @@ final class PhabricatorMetaMTAMailViewController
|
|||
->setKey('metadata')
|
||||
->appendChild($this->buildMetadataProperties($mail)));
|
||||
|
||||
$header_view = id(new PHUIHeaderView())
|
||||
->setHeader(pht('Mail'));
|
||||
|
||||
$object_phid = $mail->getRelatedPHID();
|
||||
if ($object_phid) {
|
||||
$handles = $viewer->loadHandles(array($object_phid));
|
||||
$handle = $handles[$object_phid];
|
||||
if ($handle->isComplete() && $handle->getURI()) {
|
||||
$view_button = id(new PHUIButtonView())
|
||||
->setTag('a')
|
||||
->setText(pht('View Object'))
|
||||
->setIcon('fa-chevron-right')
|
||||
->setHref($handle->getURI());
|
||||
|
||||
$header_view->addActionLink($view_button);
|
||||
}
|
||||
}
|
||||
|
||||
$object_box = id(new PHUIObjectBoxView())
|
||||
->setHeaderText(pht('Mail'))
|
||||
->setHeader($header_view)
|
||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||
->addTabGroup($tab_group);
|
||||
|
||||
|
@ -134,6 +169,12 @@ final class PhabricatorMetaMTAMailViewController
|
|||
|
||||
$properties->addTextContent($body);
|
||||
|
||||
$file_phids = $mail->getAttachmentFilePHIDs();
|
||||
if ($file_phids) {
|
||||
$properties->addProperty(
|
||||
pht('Attached Files'),
|
||||
$viewer->loadHandles($file_phids)->renderList());
|
||||
}
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
@ -158,6 +199,15 @@ final class PhabricatorMetaMTAMailViewController
|
|||
$properties->addProperty($key, $value);
|
||||
}
|
||||
|
||||
$encrypt_phids = $mail->getMustEncryptReasons();
|
||||
if ($encrypt_phids) {
|
||||
$properties->addProperty(
|
||||
pht('Must Encrypt'),
|
||||
$viewer->loadHandles($encrypt_phids)
|
||||
->renderList());
|
||||
}
|
||||
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue