Merge branch 'master' into blender-tweaks
This commit is contained in:
1
bin/conduit
Symbolic link
1
bin/conduit
Symbolic link
@@ -0,0 +1 @@
|
||||
../scripts/setup/manage_conduit.php
|
||||
@@ -9,11 +9,11 @@ return array(
|
||||
'names' => array(
|
||||
'conpherence.pkg.css' => 'e68cf1fa',
|
||||
'conpherence.pkg.js' => '15191c65',
|
||||
'core.pkg.css' => '144d9932',
|
||||
'core.pkg.js' => '4c79d74f',
|
||||
'core.pkg.css' => '07cdfee8',
|
||||
'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' => '4d48ee79',
|
||||
@@ -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',
|
||||
@@ -176,7 +176,7 @@ return array(
|
||||
'rsrc/css/phui/phui-spacing.css' => '042804d6',
|
||||
'rsrc/css/phui/phui-status.css' => 'd5263e49',
|
||||
'rsrc/css/phui/phui-tag-view.css' => 'b4719c50',
|
||||
'rsrc/css/phui/phui-timeline-view.css' => 'e2ef62b1',
|
||||
'rsrc/css/phui/phui-timeline-view.css' => '6ddf8126',
|
||||
'rsrc/css/phui/phui-two-column-view.css' => '44ec4951',
|
||||
'rsrc/css/phui/workboards/phui-workboard-color.css' => '783cdff5',
|
||||
'rsrc/css/phui/workboards/phui-workboard.css' => '3bc85455',
|
||||
@@ -397,8 +397,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',
|
||||
@@ -500,7 +500,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',
|
||||
@@ -534,7 +534,7 @@ return array(
|
||||
'rsrc/js/phuix/PHUIXButtonView.js' => '8a91e1ac',
|
||||
'rsrc/js/phuix/PHUIXDropdownMenu.js' => '04b2ae03',
|
||||
'rsrc/js/phuix/PHUIXExample.js' => '68af71ca',
|
||||
'rsrc/js/phuix/PHUIXFormControl.js' => '1dd0870c',
|
||||
'rsrc/js/phuix/PHUIXFormControl.js' => '16ad6224',
|
||||
'rsrc/js/phuix/PHUIXIconView.js' => 'bff6884b',
|
||||
),
|
||||
'symbols' => array(
|
||||
@@ -659,7 +659,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',
|
||||
@@ -768,7 +768,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',
|
||||
@@ -777,8 +777,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',
|
||||
@@ -786,12 +786,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' => '7821ca89',
|
||||
'phabricator-nav-view-css' => 'faf6a6fc',
|
||||
'phabricator-nav-view-css' => '028126f6',
|
||||
'phabricator-notification' => '008faf9c',
|
||||
'phabricator-notification-css' => '457861ec',
|
||||
'phabricator-notification-menu-css' => '10685bd4',
|
||||
@@ -876,7 +876,7 @@ return array(
|
||||
'phui-status-list-view-css' => 'd5263e49',
|
||||
'phui-tag-view-css' => 'b4719c50',
|
||||
'phui-theme-css' => '9f261c6b',
|
||||
'phui-timeline-view-css' => 'e2ef62b1',
|
||||
'phui-timeline-view-css' => '6ddf8126',
|
||||
'phui-two-column-view-css' => '44ec4951',
|
||||
'phui-workboard-color-css' => '783cdff5',
|
||||
'phui-workboard-view-css' => '3bc85455',
|
||||
@@ -887,7 +887,7 @@ return array(
|
||||
'phuix-autocomplete' => 'e0731603',
|
||||
'phuix-button-view' => '8a91e1ac',
|
||||
'phuix-dropdown-menu' => '04b2ae03',
|
||||
'phuix-form-control-view' => '1dd0870c',
|
||||
'phuix-form-control-view' => '16ad6224',
|
||||
'phuix-icon-view' => 'bff6884b',
|
||||
'policy-css' => '957ea14c',
|
||||
'policy-edit-css' => '815c66f7',
|
||||
@@ -998,6 +998,10 @@ return array(
|
||||
'aphront-typeahead-control-css',
|
||||
'phui-tag-view-css',
|
||||
),
|
||||
'16ad6224' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
),
|
||||
'17bb8539' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
@@ -1029,10 +1033,6 @@ return array(
|
||||
'javelin-request',
|
||||
'javelin-uri',
|
||||
),
|
||||
'1dd0870c' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
),
|
||||
'1e911d0f' => array(
|
||||
'javelin-stratcom',
|
||||
'javelin-request',
|
||||
@@ -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',
|
||||
@@ -1564,6 +1564,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',
|
||||
@@ -1651,16 +1661,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',
|
||||
),
|
||||
@@ -1681,17 +1681,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',
|
||||
@@ -1840,6 +1829,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;
|
||||
}
|
||||
}
|
||||
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);
|
||||
@@ -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',
|
||||
@@ -1525,13 +1527,10 @@ phutil_register_library_map(array(
|
||||
'ManiphestEditProjectsCapability' => 'applications/maniphest/capability/ManiphestEditProjectsCapability.php',
|
||||
'ManiphestEditStatusCapability' => 'applications/maniphest/capability/ManiphestEditStatusCapability.php',
|
||||
'ManiphestEmailCommand' => 'applications/maniphest/command/ManiphestEmailCommand.php',
|
||||
'ManiphestExcelDefaultFormat' => 'applications/maniphest/export/ManiphestExcelDefaultFormat.php',
|
||||
'ManiphestExcelFormat' => 'applications/maniphest/export/ManiphestExcelFormat.php',
|
||||
'ManiphestExcelFormatTestCase' => 'applications/maniphest/export/__tests__/ManiphestExcelFormatTestCase.php',
|
||||
'ManiphestExportController' => 'applications/maniphest/controller/ManiphestExportController.php',
|
||||
'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',
|
||||
@@ -1965,6 +1964,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',
|
||||
@@ -2226,6 +2226,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',
|
||||
'PhabricatorBuildbotController' => 'extensions/buildbot/controller/PhabricatorBuildbotController.php',
|
||||
'PhabricatorBuiltinDraftEngine' => 'applications/transactions/draft/PhabricatorBuiltinDraftEngine.php',
|
||||
@@ -2234,9 +2235,10 @@ phutil_register_library_map(array(
|
||||
'PhabricatorBulkContentSource' => 'infrastructure/daemon/contentsource/PhabricatorBulkContentSource.php',
|
||||
'PhabricatorBulkEditGroup' => 'applications/transactions/bulk/PhabricatorBulkEditGroup.php',
|
||||
'PhabricatorBulkEngine' => 'applications/transactions/bulk/PhabricatorBulkEngine.php',
|
||||
'PhabricatorBulkManagementExportWorkflow' => 'applications/transactions/bulk/management/PhabricatorBulkManagementExportWorkflow.php',
|
||||
'PhabricatorBulkManagementMakeSilentWorkflow' => 'applications/transactions/bulk/management/PhabricatorBulkManagementMakeSilentWorkflow.php',
|
||||
'PhabricatorBulkManagementWorkflow' => 'applications/transactions/bulk/management/PhabricatorBulkManagementWorkflow.php',
|
||||
'PhabricatorCSVExportFormat' => 'infrastructure/export/PhabricatorCSVExportFormat.php',
|
||||
'PhabricatorCSVExportFormat' => 'infrastructure/export/format/PhabricatorCSVExportFormat.php',
|
||||
'PhabricatorCacheDAO' => 'applications/cache/storage/PhabricatorCacheDAO.php',
|
||||
'PhabricatorCacheEngine' => 'applications/system/engine/PhabricatorCacheEngine.php',
|
||||
'PhabricatorCacheEngineExtension' => 'applications/system/engine/PhabricatorCacheEngineExtension.php',
|
||||
@@ -2414,6 +2416,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',
|
||||
@@ -2432,6 +2435,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',
|
||||
@@ -2442,6 +2446,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',
|
||||
@@ -2587,6 +2592,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorCustomFieldEditEngineExtension' => 'infrastructure/customfield/engineextension/PhabricatorCustomFieldEditEngineExtension.php',
|
||||
'PhabricatorCustomFieldEditField' => 'infrastructure/customfield/editor/PhabricatorCustomFieldEditField.php',
|
||||
'PhabricatorCustomFieldEditType' => 'infrastructure/customfield/editor/PhabricatorCustomFieldEditType.php',
|
||||
'PhabricatorCustomFieldExportEngineExtension' => 'infrastructure/export/engine/PhabricatorCustomFieldExportEngineExtension.php',
|
||||
'PhabricatorCustomFieldFulltextEngineExtension' => 'infrastructure/customfield/engineextension/PhabricatorCustomFieldFulltextEngineExtension.php',
|
||||
'PhabricatorCustomFieldHeraldAction' => 'infrastructure/customfield/herald/PhabricatorCustomFieldHeraldAction.php',
|
||||
'PhabricatorCustomFieldHeraldActionGroup' => 'infrastructure/customfield/herald/PhabricatorCustomFieldHeraldActionGroup.php',
|
||||
@@ -2751,6 +2757,8 @@ phutil_register_library_map(array(
|
||||
'PhabricatorDraftEngine' => 'applications/transactions/draft/PhabricatorDraftEngine.php',
|
||||
'PhabricatorDraftInterface' => 'applications/transactions/draft/PhabricatorDraftInterface.php',
|
||||
'PhabricatorDrydockApplication' => 'applications/drydock/application/PhabricatorDrydockApplication.php',
|
||||
'PhabricatorEdgeChangeRecord' => 'infrastructure/edges/util/PhabricatorEdgeChangeRecord.php',
|
||||
'PhabricatorEdgeChangeRecordTestCase' => 'infrastructure/edges/__tests__/PhabricatorEdgeChangeRecordTestCase.php',
|
||||
'PhabricatorEdgeConfig' => 'infrastructure/edges/constants/PhabricatorEdgeConfig.php',
|
||||
'PhabricatorEdgeConstants' => 'infrastructure/edges/constants/PhabricatorEdgeConstants.php',
|
||||
'PhabricatorEdgeCycleException' => 'infrastructure/edges/exception/PhabricatorEdgeCycleException.php',
|
||||
@@ -2814,6 +2822,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',
|
||||
@@ -2830,6 +2839,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',
|
||||
@@ -2842,15 +2852,20 @@ phutil_register_library_map(array(
|
||||
'PhabricatorEnv' => 'infrastructure/env/PhabricatorEnv.php',
|
||||
'PhabricatorEnvTestCase' => 'infrastructure/env/__tests__/PhabricatorEnvTestCase.php',
|
||||
'PhabricatorEpochEditField' => 'applications/transactions/editfield/PhabricatorEpochEditField.php',
|
||||
'PhabricatorEpochExportField' => 'infrastructure/export/PhabricatorEpochExportField.php',
|
||||
'PhabricatorEpochExportField' => 'infrastructure/export/field/PhabricatorEpochExportField.php',
|
||||
'PhabricatorEvent' => 'infrastructure/events/PhabricatorEvent.php',
|
||||
'PhabricatorEventEngine' => 'infrastructure/events/PhabricatorEventEngine.php',
|
||||
'PhabricatorEventListener' => 'infrastructure/events/PhabricatorEventListener.php',
|
||||
'PhabricatorEventType' => 'infrastructure/events/constant/PhabricatorEventType.php',
|
||||
'PhabricatorExampleEventListener' => 'infrastructure/events/PhabricatorExampleEventListener.php',
|
||||
'PhabricatorExcelExportFormat' => 'infrastructure/export/format/PhabricatorExcelExportFormat.php',
|
||||
'PhabricatorExecFutureFileUploadSource' => 'applications/files/uploadsource/PhabricatorExecFutureFileUploadSource.php',
|
||||
'PhabricatorExportField' => 'infrastructure/export/PhabricatorExportField.php',
|
||||
'PhabricatorExportFormat' => 'infrastructure/export/PhabricatorExportFormat.php',
|
||||
'PhabricatorExportEngine' => 'infrastructure/export/engine/PhabricatorExportEngine.php',
|
||||
'PhabricatorExportEngineBulkJobType' => 'infrastructure/export/engine/PhabricatorExportEngineBulkJobType.php',
|
||||
'PhabricatorExportEngineExtension' => 'infrastructure/export/engine/PhabricatorExportEngineExtension.php',
|
||||
'PhabricatorExportField' => 'infrastructure/export/field/PhabricatorExportField.php',
|
||||
'PhabricatorExportFormat' => 'infrastructure/export/format/PhabricatorExportFormat.php',
|
||||
'PhabricatorExportFormatSetting' => 'infrastructure/export/engine/PhabricatorExportFormatSetting.php',
|
||||
'PhabricatorExtendedPolicyInterface' => 'applications/policy/interface/PhabricatorExtendedPolicyInterface.php',
|
||||
'PhabricatorExtendingPhabricatorConfigOptions' => 'applications/config/option/PhabricatorExtendingPhabricatorConfigOptions.php',
|
||||
'PhabricatorExtensionsSetupCheck' => 'applications/config/check/PhabricatorExtensionsSetupCheck.php',
|
||||
@@ -3020,6 +3035,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorGDSetupCheck' => 'applications/config/check/PhabricatorGDSetupCheck.php',
|
||||
'PhabricatorGarbageCollector' => 'infrastructure/daemon/garbagecollector/PhabricatorGarbageCollector.php',
|
||||
'PhabricatorGarbageCollectorManagementCollectWorkflow' => 'infrastructure/daemon/garbagecollector/management/PhabricatorGarbageCollectorManagementCollectWorkflow.php',
|
||||
'PhabricatorGarbageCollectorManagementCompactEdgesWorkflow' => 'infrastructure/daemon/garbagecollector/management/PhabricatorGarbageCollectorManagementCompactEdgesWorkflow.php',
|
||||
'PhabricatorGarbageCollectorManagementSetPolicyWorkflow' => 'infrastructure/daemon/garbagecollector/management/PhabricatorGarbageCollectorManagementSetPolicyWorkflow.php',
|
||||
'PhabricatorGarbageCollectorManagementWorkflow' => 'infrastructure/daemon/garbagecollector/management/PhabricatorGarbageCollectorManagementWorkflow.php',
|
||||
'PhabricatorGeneralCachePurger' => 'applications/cache/purger/PhabricatorGeneralCachePurger.php',
|
||||
@@ -3070,7 +3086,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorHomeProfileMenuItem' => 'applications/home/menuitem/PhabricatorHomeProfileMenuItem.php',
|
||||
'PhabricatorHovercardEngineExtension' => 'applications/search/engineextension/PhabricatorHovercardEngineExtension.php',
|
||||
'PhabricatorHovercardEngineExtensionModule' => 'applications/search/engineextension/PhabricatorHovercardEngineExtensionModule.php',
|
||||
'PhabricatorIDExportField' => 'infrastructure/export/PhabricatorIDExportField.php',
|
||||
'PhabricatorIDExportField' => 'infrastructure/export/field/PhabricatorIDExportField.php',
|
||||
'PhabricatorIDsSearchEngineExtension' => 'applications/search/engineextension/PhabricatorIDsSearchEngineExtension.php',
|
||||
'PhabricatorIDsSearchField' => 'applications/search/field/PhabricatorIDsSearchField.php',
|
||||
'PhabricatorIconDatasource' => 'applications/files/typeahead/PhabricatorIconDatasource.php',
|
||||
@@ -3094,7 +3110,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorInlineSummaryView' => 'infrastructure/diff/view/PhabricatorInlineSummaryView.php',
|
||||
'PhabricatorInstructionsEditField' => 'applications/transactions/editfield/PhabricatorInstructionsEditField.php',
|
||||
'PhabricatorIntConfigType' => 'applications/config/type/PhabricatorIntConfigType.php',
|
||||
'PhabricatorIntExportField' => 'infrastructure/export/PhabricatorIntExportField.php',
|
||||
'PhabricatorIntExportField' => 'infrastructure/export/field/PhabricatorIntExportField.php',
|
||||
'PhabricatorInternalSetting' => 'applications/settings/setting/PhabricatorInternalSetting.php',
|
||||
'PhabricatorInternationalizationManagementExtractWorkflow' => 'infrastructure/internationalization/management/PhabricatorInternationalizationManagementExtractWorkflow.php',
|
||||
'PhabricatorInternationalizationManagementWorkflow' => 'infrastructure/internationalization/management/PhabricatorInternationalizationManagementWorkflow.php',
|
||||
@@ -3104,7 +3120,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorIteratorFileUploadSource' => 'applications/files/uploadsource/PhabricatorIteratorFileUploadSource.php',
|
||||
'PhabricatorJIRAAuthProvider' => 'applications/auth/provider/PhabricatorJIRAAuthProvider.php',
|
||||
'PhabricatorJSONConfigType' => 'applications/config/type/PhabricatorJSONConfigType.php',
|
||||
'PhabricatorJSONExportFormat' => 'infrastructure/export/PhabricatorJSONExportFormat.php',
|
||||
'PhabricatorJSONExportFormat' => 'infrastructure/export/format/PhabricatorJSONExportFormat.php',
|
||||
'PhabricatorJavelinLinter' => 'infrastructure/lint/linter/PhabricatorJavelinLinter.php',
|
||||
'PhabricatorJiraIssueHasObjectEdgeType' => 'applications/doorkeeper/edge/PhabricatorJiraIssueHasObjectEdgeType.php',
|
||||
'PhabricatorJumpNavHandler' => 'applications/search/engine/PhabricatorJumpNavHandler.php',
|
||||
@@ -3127,9 +3143,11 @@ phutil_register_library_map(array(
|
||||
'PhabricatorLipsumManagementWorkflow' => 'applications/lipsum/management/PhabricatorLipsumManagementWorkflow.php',
|
||||
'PhabricatorLipsumMondrianArtist' => 'applications/lipsum/image/PhabricatorLipsumMondrianArtist.php',
|
||||
'PhabricatorLiskDAO' => 'infrastructure/storage/lisk/PhabricatorLiskDAO.php',
|
||||
'PhabricatorLiskExportEngineExtension' => 'infrastructure/export/engine/PhabricatorLiskExportEngineExtension.php',
|
||||
'PhabricatorLiskFulltextEngineExtension' => 'applications/search/engineextension/PhabricatorLiskFulltextEngineExtension.php',
|
||||
'PhabricatorLiskSearchEngineExtension' => 'applications/search/engineextension/PhabricatorLiskSearchEngineExtension.php',
|
||||
'PhabricatorLiskSerializer' => 'infrastructure/storage/lisk/PhabricatorLiskSerializer.php',
|
||||
'PhabricatorListExportField' => 'infrastructure/export/field/PhabricatorListExportField.php',
|
||||
'PhabricatorLocalDiskFileStorageEngine' => 'applications/files/engine/PhabricatorLocalDiskFileStorageEngine.php',
|
||||
'PhabricatorLocalTimeTestCase' => 'view/__tests__/PhabricatorLocalTimeTestCase.php',
|
||||
'PhabricatorLocaleScopeGuard' => 'infrastructure/internationalization/scope/PhabricatorLocaleScopeGuard.php',
|
||||
@@ -3165,14 +3183,17 @@ phutil_register_library_map(array(
|
||||
'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',
|
||||
@@ -3185,6 +3206,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',
|
||||
@@ -3195,6 +3217,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',
|
||||
@@ -3254,6 +3277,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',
|
||||
@@ -3271,6 +3295,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',
|
||||
@@ -3424,10 +3450,12 @@ phutil_register_library_map(array(
|
||||
'PhabricatorPHDConfigOptions' => 'applications/config/option/PhabricatorPHDConfigOptions.php',
|
||||
'PhabricatorPHID' => 'applications/phid/storage/PhabricatorPHID.php',
|
||||
'PhabricatorPHIDConstants' => 'applications/phid/PhabricatorPHIDConstants.php',
|
||||
'PhabricatorPHIDExportField' => 'infrastructure/export/PhabricatorPHIDExportField.php',
|
||||
'PhabricatorPHIDExportField' => 'infrastructure/export/field/PhabricatorPHIDExportField.php',
|
||||
'PhabricatorPHIDInterface' => 'applications/phid/interface/PhabricatorPHIDInterface.php',
|
||||
'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',
|
||||
@@ -3836,7 +3864,9 @@ phutil_register_library_map(array(
|
||||
'PhabricatorProjectsCurtainExtension' => 'applications/project/engineextension/PhabricatorProjectsCurtainExtension.php',
|
||||
'PhabricatorProjectsEditEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsEditEngineExtension.php',
|
||||
'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',
|
||||
@@ -4128,8 +4158,10 @@ phutil_register_library_map(array(
|
||||
'PhabricatorSpacesController' => 'applications/spaces/controller/PhabricatorSpacesController.php',
|
||||
'PhabricatorSpacesDAO' => 'applications/spaces/storage/PhabricatorSpacesDAO.php',
|
||||
'PhabricatorSpacesEditController' => 'applications/spaces/controller/PhabricatorSpacesEditController.php',
|
||||
'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',
|
||||
@@ -4191,9 +4223,11 @@ phutil_register_library_map(array(
|
||||
'PhabricatorStorageSchemaSpec' => 'infrastructure/storage/schema/PhabricatorStorageSchemaSpec.php',
|
||||
'PhabricatorStorageSetupCheck' => 'applications/config/check/PhabricatorStorageSetupCheck.php',
|
||||
'PhabricatorStringConfigType' => 'applications/config/type/PhabricatorStringConfigType.php',
|
||||
'PhabricatorStringExportField' => 'infrastructure/export/PhabricatorStringExportField.php',
|
||||
'PhabricatorStringExportField' => 'infrastructure/export/field/PhabricatorStringExportField.php',
|
||||
'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',
|
||||
@@ -4208,9 +4242,12 @@ phutil_register_library_map(array(
|
||||
'PhabricatorSubscriptionsEditController' => 'applications/subscriptions/controller/PhabricatorSubscriptionsEditController.php',
|
||||
'PhabricatorSubscriptionsEditEngineExtension' => 'applications/subscriptions/engineextension/PhabricatorSubscriptionsEditEngineExtension.php',
|
||||
'PhabricatorSubscriptionsEditor' => 'applications/subscriptions/editor/PhabricatorSubscriptionsEditor.php',
|
||||
'PhabricatorSubscriptionsExportEngineExtension' => 'infrastructure/export/engine/PhabricatorSubscriptionsExportEngineExtension.php',
|
||||
'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',
|
||||
@@ -4255,7 +4292,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorTextAreaEditField' => 'applications/transactions/editfield/PhabricatorTextAreaEditField.php',
|
||||
'PhabricatorTextConfigType' => 'applications/config/type/PhabricatorTextConfigType.php',
|
||||
'PhabricatorTextEditField' => 'applications/transactions/editfield/PhabricatorTextEditField.php',
|
||||
'PhabricatorTextExportFormat' => 'infrastructure/export/PhabricatorTextExportFormat.php',
|
||||
'PhabricatorTextExportFormat' => 'infrastructure/export/format/PhabricatorTextExportFormat.php',
|
||||
'PhabricatorTextListConfigType' => 'applications/config/type/PhabricatorTextListConfigType.php',
|
||||
'PhabricatorTime' => 'infrastructure/time/PhabricatorTime.php',
|
||||
'PhabricatorTimeFormatSetting' => 'applications/settings/setting/PhabricatorTimeFormatSetting.php',
|
||||
@@ -4320,6 +4357,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorUIExample' => 'applications/uiexample/examples/PhabricatorUIExample.php',
|
||||
'PhabricatorUIExampleRenderController' => 'applications/uiexample/controller/PhabricatorUIExampleRenderController.php',
|
||||
'PhabricatorUIExamplesApplication' => 'applications/uiexample/application/PhabricatorUIExamplesApplication.php',
|
||||
'PhabricatorURIExportField' => 'infrastructure/export/field/PhabricatorURIExportField.php',
|
||||
'PhabricatorUSEnglishTranslation' => 'infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php',
|
||||
'PhabricatorUnifiedDiffsSetting' => 'applications/settings/setting/PhabricatorUnifiedDiffsSetting.php',
|
||||
'PhabricatorUnitTestContentSource' => 'infrastructure/contentsource/PhabricatorUnitTestContentSource.php',
|
||||
@@ -4413,6 +4451,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorWorkerManagementWorkflow' => 'infrastructure/daemon/workers/management/PhabricatorWorkerManagementWorkflow.php',
|
||||
'PhabricatorWorkerPermanentFailureException' => 'infrastructure/daemon/workers/exception/PhabricatorWorkerPermanentFailureException.php',
|
||||
'PhabricatorWorkerSchemaSpec' => 'infrastructure/daemon/workers/storage/PhabricatorWorkerSchemaSpec.php',
|
||||
'PhabricatorWorkerSingleBulkJobType' => 'infrastructure/daemon/workers/bulk/PhabricatorWorkerSingleBulkJobType.php',
|
||||
'PhabricatorWorkerTask' => 'infrastructure/daemon/workers/storage/PhabricatorWorkerTask.php',
|
||||
'PhabricatorWorkerTaskData' => 'infrastructure/daemon/workers/storage/PhabricatorWorkerTaskData.php',
|
||||
'PhabricatorWorkerTaskDetailController' => 'applications/daemon/controller/PhabricatorWorkerTaskDetailController.php',
|
||||
@@ -5588,6 +5627,7 @@ phutil_register_library_map(array(
|
||||
'DifferentialLintField' => 'DifferentialHarbormasterField',
|
||||
'DifferentialLintStatus' => 'Phobject',
|
||||
'DifferentialLocalCommitsView' => 'AphrontView',
|
||||
'DifferentialMailEngineExtension' => 'PhabricatorMailEngineExtension',
|
||||
'DifferentialMailView' => 'Phobject',
|
||||
'DifferentialManiphestTasksField' => 'DifferentialCoreCustomField',
|
||||
'DifferentialModernHunk' => 'DifferentialHunk',
|
||||
@@ -6560,6 +6600,7 @@ phutil_register_library_map(array(
|
||||
'HarbormasterWaitForPreviousBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
|
||||
'HarbormasterWorker' => 'PhabricatorWorker',
|
||||
'HarbormasterWorkingCopyArtifact' => 'HarbormasterDrydockLeaseArtifact',
|
||||
'HeraldActingUserField' => 'HeraldField',
|
||||
'HeraldAction' => 'Phobject',
|
||||
'HeraldActionGroup' => 'HeraldGroup',
|
||||
'HeraldActionRecord' => 'HeraldDAO',
|
||||
@@ -6772,13 +6813,10 @@ phutil_register_library_map(array(
|
||||
'ManiphestEditProjectsCapability' => 'PhabricatorPolicyCapability',
|
||||
'ManiphestEditStatusCapability' => 'PhabricatorPolicyCapability',
|
||||
'ManiphestEmailCommand' => 'MetaMTAEmailTransactionCommand',
|
||||
'ManiphestExcelDefaultFormat' => 'ManiphestExcelFormat',
|
||||
'ManiphestExcelFormat' => 'Phobject',
|
||||
'ManiphestExcelFormatTestCase' => 'PhabricatorTestCase',
|
||||
'ManiphestExportController' => 'ManiphestController',
|
||||
'ManiphestGetTaskTransactionsConduitAPIMethod' => 'ManiphestConduitAPIMethod',
|
||||
'ManiphestHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension',
|
||||
'ManiphestInfoConduitAPIMethod' => 'ManiphestConduitAPIMethod',
|
||||
'ManiphestMailEngineExtension' => 'PhabricatorMailEngineExtension',
|
||||
'ManiphestNameIndex' => 'ManiphestDAO',
|
||||
'ManiphestPointsConfigType' => 'PhabricatorJSONConfigType',
|
||||
'ManiphestPrioritiesConfigType' => 'PhabricatorJSONConfigType',
|
||||
@@ -7265,6 +7303,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorApplicationEditHTTPParameterHelpView' => 'AphrontView',
|
||||
'PhabricatorApplicationEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||
'PhabricatorApplicationEmailCommandsController' => 'PhabricatorApplicationsController',
|
||||
'PhabricatorApplicationObjectMailEngineExtension' => 'PhabricatorMailEngineExtension',
|
||||
'PhabricatorApplicationPanelController' => 'PhabricatorApplicationsController',
|
||||
'PhabricatorApplicationPolicyChangeTransaction' => 'PhabricatorApplicationTransactionType',
|
||||
'PhabricatorApplicationProfileMenuItem' => 'PhabricatorProfileMenuItem',
|
||||
@@ -7572,6 +7611,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorBoardResponseEngine' => 'Phobject',
|
||||
'PhabricatorBoolConfigType' => 'PhabricatorTextConfigType',
|
||||
'PhabricatorBoolEditField' => 'PhabricatorEditField',
|
||||
'PhabricatorBoolMailStamp' => 'PhabricatorMailStamp',
|
||||
'PhabricatorBritishEnglishTranslation' => 'PhutilTranslation',
|
||||
'PhabricatorBuildbotController' => 'PhabricatorController',
|
||||
'PhabricatorBuiltinDraftEngine' => 'PhabricatorDraftEngine',
|
||||
@@ -7580,6 +7620,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorBulkContentSource' => 'PhabricatorContentSource',
|
||||
'PhabricatorBulkEditGroup' => 'Phobject',
|
||||
'PhabricatorBulkEngine' => 'Phobject',
|
||||
'PhabricatorBulkManagementExportWorkflow' => 'PhabricatorBulkManagementWorkflow',
|
||||
'PhabricatorBulkManagementMakeSilentWorkflow' => 'PhabricatorBulkManagementWorkflow',
|
||||
'PhabricatorBulkManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
||||
'PhabricatorCSVExportFormat' => 'PhabricatorExportFormat',
|
||||
@@ -7803,6 +7844,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorClusterExceptionHandler' => 'PhabricatorRequestExceptionHandler',
|
||||
'PhabricatorClusterImpossibleWriteException' => 'PhabricatorClusterException',
|
||||
'PhabricatorClusterImproperWriteException' => 'PhabricatorClusterException',
|
||||
'PhabricatorClusterMailersConfigType' => 'PhabricatorJSONConfigType',
|
||||
'PhabricatorClusterNoHostForRoleException' => 'Exception',
|
||||
'PhabricatorClusterSearchConfigType' => 'PhabricatorJSONConfigType',
|
||||
'PhabricatorClusterServiceHealthRecord' => 'Phobject',
|
||||
@@ -7820,6 +7862,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorCommonPasswords' => 'Phobject',
|
||||
'PhabricatorConduitAPIController' => 'PhabricatorConduitController',
|
||||
'PhabricatorConduitApplication' => 'PhabricatorApplication',
|
||||
'PhabricatorConduitCallManagementWorkflow' => 'PhabricatorConduitManagementWorkflow',
|
||||
'PhabricatorConduitCertificateToken' => 'PhabricatorConduitDAO',
|
||||
'PhabricatorConduitConsoleController' => 'PhabricatorConduitController',
|
||||
'PhabricatorConduitContentSource' => 'PhabricatorContentSource',
|
||||
@@ -7830,6 +7873,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorConduitLogController' => 'PhabricatorConduitController',
|
||||
'PhabricatorConduitLogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhabricatorConduitLogSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
'PhabricatorConduitManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
||||
'PhabricatorConduitMethodCallLog' => array(
|
||||
'PhabricatorConduitDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
@@ -7999,6 +8043,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorCustomFieldEditEngineExtension' => 'PhabricatorEditEngineExtension',
|
||||
'PhabricatorCustomFieldEditField' => 'PhabricatorEditField',
|
||||
'PhabricatorCustomFieldEditType' => 'PhabricatorEditType',
|
||||
'PhabricatorCustomFieldExportEngineExtension' => 'PhabricatorExportEngineExtension',
|
||||
'PhabricatorCustomFieldFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension',
|
||||
'PhabricatorCustomFieldHeraldAction' => 'HeraldAction',
|
||||
'PhabricatorCustomFieldHeraldActionGroup' => 'HeraldActionGroup',
|
||||
@@ -8181,6 +8226,8 @@ phutil_register_library_map(array(
|
||||
'PhabricatorDraftDAO' => 'PhabricatorLiskDAO',
|
||||
'PhabricatorDraftEngine' => 'Phobject',
|
||||
'PhabricatorDrydockApplication' => 'PhabricatorApplication',
|
||||
'PhabricatorEdgeChangeRecord' => 'Phobject',
|
||||
'PhabricatorEdgeChangeRecordTestCase' => 'PhabricatorTestCase',
|
||||
'PhabricatorEdgeConfig' => 'PhabricatorEdgeConstants',
|
||||
'PhabricatorEdgeConstants' => 'Phobject',
|
||||
'PhabricatorEdgeCycleException' => 'Exception',
|
||||
@@ -8252,6 +8299,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorEditPage' => 'Phobject',
|
||||
'PhabricatorEditType' => 'Phobject',
|
||||
'PhabricatorEditor' => 'Phobject',
|
||||
'PhabricatorEditorMailEngineExtension' => 'PhabricatorMailEngineExtension',
|
||||
'PhabricatorEditorMultipleSetting' => 'PhabricatorSelectSetting',
|
||||
'PhabricatorEditorSetting' => 'PhabricatorStringSetting',
|
||||
'PhabricatorElasticFulltextStorageEngine' => 'PhabricatorFulltextStorageEngine',
|
||||
@@ -8267,6 +8315,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorEmailPreferencesSettingsPanel' => 'PhabricatorSettingsPanel',
|
||||
'PhabricatorEmailRePrefixSetting' => 'PhabricatorSelectSetting',
|
||||
'PhabricatorEmailSelfActionsSetting' => 'PhabricatorSelectSetting',
|
||||
'PhabricatorEmailStampsSetting' => 'PhabricatorSelectSetting',
|
||||
'PhabricatorEmailTagsSetting' => 'PhabricatorInternalSetting',
|
||||
'PhabricatorEmailVarySubjectsSetting' => 'PhabricatorSelectSetting',
|
||||
'PhabricatorEmailVerificationController' => 'PhabricatorAuthController',
|
||||
@@ -8285,9 +8334,14 @@ phutil_register_library_map(array(
|
||||
'PhabricatorEventListener' => 'PhutilEventListener',
|
||||
'PhabricatorEventType' => 'PhutilEventType',
|
||||
'PhabricatorExampleEventListener' => 'PhabricatorEventListener',
|
||||
'PhabricatorExcelExportFormat' => 'PhabricatorExportFormat',
|
||||
'PhabricatorExecFutureFileUploadSource' => 'PhabricatorFileUploadSource',
|
||||
'PhabricatorExportEngine' => 'Phobject',
|
||||
'PhabricatorExportEngineBulkJobType' => 'PhabricatorWorkerSingleBulkJobType',
|
||||
'PhabricatorExportEngineExtension' => 'Phobject',
|
||||
'PhabricatorExportField' => 'Phobject',
|
||||
'PhabricatorExportFormat' => 'Phobject',
|
||||
'PhabricatorExportFormatSetting' => 'PhabricatorInternalSetting',
|
||||
'PhabricatorExtendingPhabricatorConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
'PhabricatorExtensionsSetupCheck' => 'PhabricatorSetupCheck',
|
||||
'PhabricatorExternalAccount' => array(
|
||||
@@ -8493,6 +8547,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorGDSetupCheck' => 'PhabricatorSetupCheck',
|
||||
'PhabricatorGarbageCollector' => 'Phobject',
|
||||
'PhabricatorGarbageCollectorManagementCollectWorkflow' => 'PhabricatorGarbageCollectorManagementWorkflow',
|
||||
'PhabricatorGarbageCollectorManagementCompactEdgesWorkflow' => 'PhabricatorGarbageCollectorManagementWorkflow',
|
||||
'PhabricatorGarbageCollectorManagementSetPolicyWorkflow' => 'PhabricatorGarbageCollectorManagementWorkflow',
|
||||
'PhabricatorGarbageCollectorManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
||||
'PhabricatorGeneralCachePurger' => 'PhabricatorCachePurger',
|
||||
@@ -8604,9 +8659,11 @@ phutil_register_library_map(array(
|
||||
'PhabricatorLipsumManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
||||
'PhabricatorLipsumMondrianArtist' => 'PhabricatorLipsumArtist',
|
||||
'PhabricatorLiskDAO' => 'LiskDAO',
|
||||
'PhabricatorLiskExportEngineExtension' => 'PhabricatorExportEngineExtension',
|
||||
'PhabricatorLiskFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension',
|
||||
'PhabricatorLiskSearchEngineExtension' => 'PhabricatorSearchEngineExtension',
|
||||
'PhabricatorLiskSerializer' => 'Phobject',
|
||||
'PhabricatorListExportField' => 'PhabricatorExportField',
|
||||
'PhabricatorLocalDiskFileStorageEngine' => 'PhabricatorFileStorageEngine',
|
||||
'PhabricatorLocalTimeTestCase' => 'PhabricatorTestCase',
|
||||
'PhabricatorLocaleScopeGuard' => 'Phobject',
|
||||
@@ -8642,14 +8699,17 @@ phutil_register_library_map(array(
|
||||
'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',
|
||||
@@ -8662,6 +8722,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorMailManagementUnverifyWorkflow' => 'PhabricatorMailManagementWorkflow',
|
||||
'PhabricatorMailManagementVolumeWorkflow' => 'PhabricatorMailManagementWorkflow',
|
||||
'PhabricatorMailManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
||||
'PhabricatorMailMustEncryptHeraldAction' => 'HeraldAction',
|
||||
'PhabricatorMailOutboundMailHeraldAdapter' => 'HeraldAdapter',
|
||||
'PhabricatorMailOutboundRoutingHeraldAction' => 'HeraldAction',
|
||||
'PhabricatorMailOutboundRoutingSelfEmailHeraldAction' => 'PhabricatorMailOutboundRoutingHeraldAction',
|
||||
@@ -8672,6 +8733,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorMailReplyHandler' => 'Phobject',
|
||||
'PhabricatorMailRoutingRule' => 'Phobject',
|
||||
'PhabricatorMailSetupCheck' => 'PhabricatorSetupCheck',
|
||||
'PhabricatorMailStamp' => 'Phobject',
|
||||
'PhabricatorMailTarget' => 'Phobject',
|
||||
'PhabricatorMailgunConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
'PhabricatorMainMenuBarExtension' => 'Phobject',
|
||||
@@ -8725,6 +8787,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorMetaMTAMail' => array(
|
||||
'PhabricatorMetaMTADAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
'PhabricatorDestructibleInterface',
|
||||
),
|
||||
'PhabricatorMetaMTAMailBody' => 'Phobject',
|
||||
'PhabricatorMetaMTAMailBodyTestCase' => 'PhabricatorTestCase',
|
||||
@@ -8741,6 +8804,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorMetaMTAMailgunReceiveController' => 'PhabricatorMetaMTAController',
|
||||
'PhabricatorMetaMTAMemberQuery' => 'PhabricatorQuery',
|
||||
'PhabricatorMetaMTAPermanentFailureException' => 'Exception',
|
||||
'PhabricatorMetaMTAPostmarkReceiveController' => 'PhabricatorMetaMTAController',
|
||||
'PhabricatorMetaMTAReceivedMail' => 'PhabricatorMetaMTADAO',
|
||||
'PhabricatorMetaMTAReceivedMailProcessingException' => 'Exception',
|
||||
'PhabricatorMetaMTAReceivedMailTestCase' => 'PhabricatorTestCase',
|
||||
@@ -8758,6 +8822,8 @@ phutil_register_library_map(array(
|
||||
'PhabricatorMultiFactorSettingsPanel' => 'PhabricatorSettingsPanel',
|
||||
'PhabricatorMultimeterApplication' => 'PhabricatorApplication',
|
||||
'PhabricatorMustVerifyEmailController' => 'PhabricatorAuthController',
|
||||
'PhabricatorMutedByEdgeType' => 'PhabricatorEdgeType',
|
||||
'PhabricatorMutedEdgeType' => 'PhabricatorEdgeType',
|
||||
'PhabricatorMySQLConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
'PhabricatorMySQLFileStorageEngine' => 'PhabricatorFileStorageEngine',
|
||||
'PhabricatorMySQLSearchHost' => 'PhabricatorSearchHost',
|
||||
@@ -8944,6 +9010,8 @@ phutil_register_library_map(array(
|
||||
'PhabricatorPHIDExportField' => 'PhabricatorExportField',
|
||||
'PhabricatorPHIDListEditField' => 'PhabricatorEditField',
|
||||
'PhabricatorPHIDListEditType' => 'PhabricatorEditType',
|
||||
'PhabricatorPHIDListExportField' => 'PhabricatorListExportField',
|
||||
'PhabricatorPHIDMailStamp' => 'PhabricatorMailStamp',
|
||||
'PhabricatorPHIDResolver' => 'Phobject',
|
||||
'PhabricatorPHIDType' => 'Phobject',
|
||||
'PhabricatorPHIDTypeTestCase' => 'PhutilTestCase',
|
||||
@@ -9444,7 +9512,9 @@ phutil_register_library_map(array(
|
||||
'PhabricatorProjectsCurtainExtension' => 'PHUICurtainExtension',
|
||||
'PhabricatorProjectsEditEngineExtension' => 'PhabricatorEditEngineExtension',
|
||||
'PhabricatorProjectsEditField' => 'PhabricatorTokenizerEditField',
|
||||
'PhabricatorProjectsExportEngineExtension' => 'PhabricatorExportEngineExtension',
|
||||
'PhabricatorProjectsFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension',
|
||||
'PhabricatorProjectsMailEngineExtension' => 'PhabricatorMailEngineExtension',
|
||||
'PhabricatorProjectsMembersSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
|
||||
'PhabricatorProjectsMembershipIndexEngineExtension' => 'PhabricatorIndexEngineExtension',
|
||||
'PhabricatorProjectsPolicyRule' => 'PhabricatorPolicyRule',
|
||||
@@ -9812,8 +9882,10 @@ phutil_register_library_map(array(
|
||||
'PhabricatorSpacesController' => 'PhabricatorController',
|
||||
'PhabricatorSpacesDAO' => 'PhabricatorLiskDAO',
|
||||
'PhabricatorSpacesEditController' => 'PhabricatorSpacesController',
|
||||
'PhabricatorSpacesExportEngineExtension' => 'PhabricatorExportEngineExtension',
|
||||
'PhabricatorSpacesInterface' => 'PhabricatorPHIDInterface',
|
||||
'PhabricatorSpacesListController' => 'PhabricatorSpacesController',
|
||||
'PhabricatorSpacesMailEngineExtension' => 'PhabricatorMailEngineExtension',
|
||||
'PhabricatorSpacesNamespace' => array(
|
||||
'PhabricatorSpacesDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
@@ -9885,6 +9957,8 @@ phutil_register_library_map(array(
|
||||
'PhabricatorStringExportField' => 'PhabricatorExportField',
|
||||
'PhabricatorStringListConfigType' => 'PhabricatorTextListConfigType',
|
||||
'PhabricatorStringListEditField' => 'PhabricatorEditField',
|
||||
'PhabricatorStringListExportField' => 'PhabricatorListExportField',
|
||||
'PhabricatorStringMailStamp' => 'PhabricatorMailStamp',
|
||||
'PhabricatorStringSetting' => 'PhabricatorSetting',
|
||||
'PhabricatorSubmitEditField' => 'PhabricatorEditField',
|
||||
'PhabricatorSubscribedToObjectEdgeType' => 'PhabricatorEdgeType',
|
||||
@@ -9898,9 +9972,12 @@ phutil_register_library_map(array(
|
||||
'PhabricatorSubscriptionsEditController' => 'PhabricatorController',
|
||||
'PhabricatorSubscriptionsEditEngineExtension' => 'PhabricatorEditEngineExtension',
|
||||
'PhabricatorSubscriptionsEditor' => 'PhabricatorEditor',
|
||||
'PhabricatorSubscriptionsExportEngineExtension' => 'PhabricatorExportEngineExtension',
|
||||
'PhabricatorSubscriptionsFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension',
|
||||
'PhabricatorSubscriptionsHeraldAction' => 'HeraldAction',
|
||||
'PhabricatorSubscriptionsListController' => 'PhabricatorController',
|
||||
'PhabricatorSubscriptionsMailEngineExtension' => 'PhabricatorMailEngineExtension',
|
||||
'PhabricatorSubscriptionsMuteController' => 'PhabricatorController',
|
||||
'PhabricatorSubscriptionsRemoveSelfHeraldAction' => 'PhabricatorSubscriptionsHeraldAction',
|
||||
'PhabricatorSubscriptionsRemoveSubscribersHeraldAction' => 'PhabricatorSubscriptionsHeraldAction',
|
||||
'PhabricatorSubscriptionsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
|
||||
@@ -10021,6 +10098,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorUIExample' => 'Phobject',
|
||||
'PhabricatorUIExampleRenderController' => 'PhabricatorController',
|
||||
'PhabricatorUIExamplesApplication' => 'PhabricatorApplication',
|
||||
'PhabricatorURIExportField' => 'PhabricatorExportField',
|
||||
'PhabricatorUSEnglishTranslation' => 'PhutilTranslation',
|
||||
'PhabricatorUnifiedDiffsSetting' => 'PhabricatorSelectSetting',
|
||||
'PhabricatorUnitTestContentSource' => 'PhabricatorContentSource',
|
||||
@@ -10144,6 +10222,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorWorkerManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
||||
'PhabricatorWorkerPermanentFailureException' => 'Exception',
|
||||
'PhabricatorWorkerSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
||||
'PhabricatorWorkerSingleBulkJobType' => 'PhabricatorWorkerBulkJobType',
|
||||
'PhabricatorWorkerTask' => 'PhabricatorWorkerDAO',
|
||||
'PhabricatorWorkerTaskData' => 'PhabricatorWorkerDAO',
|
||||
'PhabricatorWorkerTaskDetailController' => 'PhabricatorDaemonController',
|
||||
|
||||
@@ -358,9 +358,17 @@ final class PhabricatorAuditEditor
|
||||
array $changes,
|
||||
PhutilMarkupEngine $engine) {
|
||||
|
||||
// we are only really trying to find unmentionable phids here...
|
||||
// don't bother with this outside initial commit (i.e. create)
|
||||
// transaction
|
||||
$actor = $this->getActor();
|
||||
$result = array();
|
||||
|
||||
// Some interactions (like "Fixes Txxx" interacting with Maniphest) have
|
||||
// already been processed, so we're only re-parsing them here to avoid
|
||||
// generating an extra redundant mention. Other interactions are being
|
||||
// processed for the first time.
|
||||
|
||||
// We're only recognizing magic in the commit message itself, not in
|
||||
// audit comments.
|
||||
|
||||
$is_commit = false;
|
||||
foreach ($xactions as $xaction) {
|
||||
switch ($xaction->getTransactionType()) {
|
||||
@@ -370,8 +378,6 @@ final class PhabricatorAuditEditor
|
||||
}
|
||||
}
|
||||
|
||||
// "result" is always an array....
|
||||
$result = array();
|
||||
if (!$is_commit) {
|
||||
return $result;
|
||||
}
|
||||
@@ -403,6 +409,46 @@ final class PhabricatorAuditEditor
|
||||
->withNames($monograms)
|
||||
->execute();
|
||||
$phid_map[] = mpull($objects, 'getPHID', 'getPHID');
|
||||
|
||||
|
||||
$reverts_refs = id(new DifferentialCustomFieldRevertsParser())
|
||||
->parseCorpus($huge_block);
|
||||
$reverts = array_mergev(ipull($reverts_refs, 'monograms'));
|
||||
if ($reverts) {
|
||||
// Only allow commits to revert other commits in the same repository.
|
||||
$reverted_commits = id(new DiffusionCommitQuery())
|
||||
->setViewer($actor)
|
||||
->withRepository($object->getRepository())
|
||||
->withIdentifiers($reverts)
|
||||
->execute();
|
||||
|
||||
$reverted_revisions = id(new PhabricatorObjectQuery())
|
||||
->setViewer($actor)
|
||||
->withNames($reverts)
|
||||
->withTypes(
|
||||
array(
|
||||
DifferentialRevisionPHIDType::TYPECONST,
|
||||
))
|
||||
->execute();
|
||||
|
||||
$reverted_phids =
|
||||
mpull($reverted_commits, 'getPHID', 'getPHID') +
|
||||
mpull($reverted_revisions, 'getPHID', 'getPHID');
|
||||
|
||||
// NOTE: Skip any write attempts if a user cleverly implies a commit
|
||||
// reverts itself, although this would be exceptionally clever in Git
|
||||
// or Mercurial.
|
||||
unset($reverted_phids[$object->getPHID()]);
|
||||
|
||||
$reverts_edge = DiffusionCommitRevertsCommitEdgeType::EDGECONST;
|
||||
$result[] = id(new PhabricatorAuditTransaction())
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
|
||||
->setMetadataValue('edge:type', $reverts_edge)
|
||||
->setNewValue(array('+' => $reverted_phids));
|
||||
|
||||
$phid_map[] = $reverted_phids;
|
||||
}
|
||||
|
||||
$phid_map = array_mergev($phid_map);
|
||||
$this->setUnmentionablePHIDMap($phid_map);
|
||||
|
||||
@@ -427,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,
|
||||
@@ -453,7 +496,6 @@ final class PhabricatorAuditEditor
|
||||
$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
|
||||
@@ -461,7 +503,7 @@ final class PhabricatorAuditEditor
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($audit->getAuditStatus() != $status_resigned) {
|
||||
if (!$audit->isResigned()) {
|
||||
$phids[] = $audit->getAuditorPHID();
|
||||
}
|
||||
}
|
||||
@@ -471,6 +513,18 @@ final class PhabricatorAuditEditor
|
||||
return $phids;
|
||||
}
|
||||
|
||||
protected function newMailUnexpandablePHIDs(PhabricatorLiskDAO $object) {
|
||||
$phids = array();
|
||||
|
||||
foreach ($object->getAudits() as $auditor) {
|
||||
if ($auditor->isResigned()) {
|
||||
$phids[] = $auditor->getAuditorPHID();
|
||||
}
|
||||
}
|
||||
|
||||
return $phids;
|
||||
}
|
||||
|
||||
protected function buildMailBody(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
|
||||
@@ -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 {}
|
||||
@@ -3,9 +3,26 @@
|
||||
final class ConduitPHIDParameterType
|
||||
extends ConduitParameterType {
|
||||
|
||||
private $isNullable;
|
||||
|
||||
public function setIsNullable($is_nullable) {
|
||||
$this->isNullable = $is_nullable;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIsNullable() {
|
||||
return $this->isNullable;
|
||||
}
|
||||
|
||||
protected function getParameterValue(array $request, $key, $strict) {
|
||||
$value = parent::getParameterValue($request, $key, $strict);
|
||||
|
||||
if ($this->getIsNullable()) {
|
||||
if ($value === null) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_string($value)) {
|
||||
$this->raiseValidationException(
|
||||
$request,
|
||||
@@ -17,8 +34,12 @@ final class ConduitPHIDParameterType
|
||||
}
|
||||
|
||||
protected function getParameterTypeName() {
|
||||
if ($this->getIsNullable()) {
|
||||
return 'phid|null';
|
||||
} else {
|
||||
return 'phid';
|
||||
}
|
||||
}
|
||||
|
||||
protected function getParameterFormatDescriptions() {
|
||||
return array(
|
||||
@@ -27,9 +48,15 @@ final class ConduitPHIDParameterType
|
||||
}
|
||||
|
||||
protected function getParameterExamples() {
|
||||
return array(
|
||||
$examples = array(
|
||||
'"PHID-WXYZ-1111222233334444"',
|
||||
);
|
||||
|
||||
if ($this->getIsNullable()) {
|
||||
$examples[] = 'null';
|
||||
}
|
||||
|
||||
return $examples;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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,8 +37,20 @@ final class PhabricatorConfigManagementSetWorkflow
|
||||
pht('Specify a configuration key and a value to set it to.'));
|
||||
}
|
||||
|
||||
$is_stdin = $args->getArg('stdin');
|
||||
|
||||
$key = $argv[0];
|
||||
|
||||
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(
|
||||
@@ -40,14 +58,16 @@ final class PhabricatorConfigManagementSetWorkflow
|
||||
$key));
|
||||
}
|
||||
|
||||
$value = $argv[1];
|
||||
|
||||
if (count($argv) > 2) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Too many arguments: expected one key and one value.'));
|
||||
}
|
||||
|
||||
$value = $argv[1];
|
||||
}
|
||||
|
||||
|
||||
$options = PhabricatorApplicationConfigOptions::loadAllOptions();
|
||||
if (empty($options[$key])) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -71,18 +71,10 @@ final class PhabricatorDaemonBulkJobViewController
|
||||
$viewer = $this->getViewer();
|
||||
$curtain = $this->newCurtainView($job);
|
||||
|
||||
if ($job->isConfirming()) {
|
||||
$continue_uri = $job->getMonitorURI();
|
||||
} else {
|
||||
$continue_uri = $job->getDoneURI();
|
||||
foreach ($job->getCurtainActions($viewer) as $action) {
|
||||
$curtain->addAction($action);
|
||||
}
|
||||
|
||||
$curtain->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setHref($continue_uri)
|
||||
->setIcon('fa-arrow-circle-o-right')
|
||||
->setName(pht('Continue')));
|
||||
|
||||
return $curtain;
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
@@ -919,7 +930,44 @@ final class DifferentialTransactionEditor
|
||||
}
|
||||
}
|
||||
|
||||
$this->setUnmentionablePHIDMap(array_merge($task_phids, $rev_phids));
|
||||
$revert_refs = id(new DifferentialCustomFieldRevertsParser())
|
||||
->parseCorpus($content_block);
|
||||
|
||||
$revert_monograms = array();
|
||||
foreach ($revert_refs as $match) {
|
||||
foreach ($match['monograms'] as $monogram) {
|
||||
$revert_monograms[] = $monogram;
|
||||
}
|
||||
}
|
||||
|
||||
if ($revert_monograms) {
|
||||
$revert_objects = id(new PhabricatorObjectQuery())
|
||||
->setViewer($this->getActor())
|
||||
->withNames($revert_monograms)
|
||||
->withTypes(
|
||||
array(
|
||||
DifferentialRevisionPHIDType::TYPECONST,
|
||||
PhabricatorRepositoryCommitPHIDType::TYPECONST,
|
||||
))
|
||||
->execute();
|
||||
|
||||
$revert_phids = mpull($revert_objects, 'getPHID', 'getPHID');
|
||||
|
||||
// Don't let an object revert itself, although other silly stuff like
|
||||
// cycles of objects reverting each other is not prevented.
|
||||
unset($revert_phids[$object->getPHID()]);
|
||||
|
||||
$revert_type = DiffusionCommitRevertsCommitEdgeType::EDGECONST;
|
||||
$edges[$revert_type] = $revert_phids;
|
||||
} else {
|
||||
$revert_phids = array();
|
||||
}
|
||||
|
||||
$this->setUnmentionablePHIDMap(
|
||||
array_merge(
|
||||
$task_phids,
|
||||
$rev_phids,
|
||||
$revert_phids));
|
||||
|
||||
$result = array();
|
||||
foreach ($edges as $type => $specs) {
|
||||
@@ -1693,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;
|
||||
|
||||
@@ -87,19 +87,6 @@ final class DifferentialTransaction
|
||||
}
|
||||
break;
|
||||
|
||||
case PhabricatorTransactions::TYPE_EDGE:
|
||||
$add = array_diff_key($new, $old);
|
||||
$rem = array_diff_key($old, $new);
|
||||
|
||||
// Hide metadata-only edge transactions. These correspond to users
|
||||
// accepting or rejecting revisions, but the change is always explicit
|
||||
// because of the TYPE_ACTION transaction. Rendering these transactions
|
||||
// just creates clutter.
|
||||
|
||||
if (!$add && !$rem) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case DifferentialRevisionRequestReviewTransaction::TRANSACTIONTYPE:
|
||||
// Don't hide the initial "X requested review: ..." transaction from
|
||||
// mail or feed even when it occurs during creation. We need this
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ final class PhabricatorDiffusionApplication extends PhabricatorApplication {
|
||||
$this->getEditRoutePattern('edit/') =>
|
||||
'DiffusionRepositoryEditController',
|
||||
'pushlog/' => array(
|
||||
'(?:query/(?P<queryKey>[^/]+)/)?' => 'DiffusionPushLogListController',
|
||||
$this->getQueryRoutePattern() => 'DiffusionPushLogListController',
|
||||
'view/(?P<id>\d+)/' => 'DiffusionPushEventViewController',
|
||||
),
|
||||
'pulllog/' => array(
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -9,4 +9,9 @@ final class DiffusionPullLogListController
|
||||
->buildResponse();
|
||||
}
|
||||
|
||||
protected function buildApplicationCrumbs() {
|
||||
return parent::buildApplicationCrumbs()
|
||||
->addTextCrumb(pht('Pull Logs'), $this->getApplicationURI('pulllog/'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,4 +9,9 @@ final class DiffusionPushLogListController
|
||||
->buildResponse();
|
||||
}
|
||||
|
||||
protected function buildApplicationCrumbs() {
|
||||
return parent::buildApplicationCrumbs()
|
||||
->addTextCrumb(pht('Push Logs'), $this->getApplicationURI('pushlog/'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -19,7 +19,7 @@ final class DiffusionCommitRevertedByCommitEdgeType
|
||||
$add_edges) {
|
||||
|
||||
return pht(
|
||||
'%s added %s reverting commit(s): %s.',
|
||||
'%s added %s reverting change(s): %s.',
|
||||
$actor,
|
||||
$add_count,
|
||||
$add_edges);
|
||||
@@ -31,7 +31,7 @@ final class DiffusionCommitRevertedByCommitEdgeType
|
||||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s removed %s reverting commit(s): %s.',
|
||||
'%s removed %s reverting change(s): %s.',
|
||||
$actor,
|
||||
$rem_count,
|
||||
$rem_edges);
|
||||
@@ -46,7 +46,7 @@ final class DiffusionCommitRevertedByCommitEdgeType
|
||||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s edited reverting commit(s), added %s: %s; removed %s: %s.',
|
||||
'%s edited reverting change(s), added %s: %s; removed %s: %s.',
|
||||
$actor,
|
||||
$add_count,
|
||||
$add_edges,
|
||||
@@ -61,7 +61,7 @@ final class DiffusionCommitRevertedByCommitEdgeType
|
||||
$add_edges) {
|
||||
|
||||
return pht(
|
||||
'%s added %s reverting commit(s) for %s: %s.',
|
||||
'%s added %s reverting change(s) for %s: %s.',
|
||||
$actor,
|
||||
$add_count,
|
||||
$object,
|
||||
@@ -75,7 +75,7 @@ final class DiffusionCommitRevertedByCommitEdgeType
|
||||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s removed %s reverting commit(s) for %s: %s.',
|
||||
'%s removed %s reverting change(s) for %s: %s.',
|
||||
$actor,
|
||||
$rem_count,
|
||||
$object,
|
||||
@@ -92,7 +92,7 @@ final class DiffusionCommitRevertedByCommitEdgeType
|
||||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s edited reverting commit(s) for %s, added %s: %s; removed %s: %s.',
|
||||
'%s edited reverting change(s) for %s, added %s: %s; removed %s: %s.',
|
||||
$actor,
|
||||
$object,
|
||||
$add_count,
|
||||
|
||||
@@ -22,7 +22,7 @@ final class DiffusionCommitRevertsCommitEdgeType extends PhabricatorEdgeType {
|
||||
$add_edges) {
|
||||
|
||||
return pht(
|
||||
'%s added %s reverted commit(s): %s.',
|
||||
'%s added %s reverted change(s): %s.',
|
||||
$actor,
|
||||
$add_count,
|
||||
$add_edges);
|
||||
@@ -34,7 +34,7 @@ final class DiffusionCommitRevertsCommitEdgeType extends PhabricatorEdgeType {
|
||||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s removed %s reverted commit(s): %s.',
|
||||
'%s removed %s reverted change(s): %s.',
|
||||
$actor,
|
||||
$rem_count,
|
||||
$rem_edges);
|
||||
@@ -49,7 +49,7 @@ final class DiffusionCommitRevertsCommitEdgeType extends PhabricatorEdgeType {
|
||||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s edited reverted commit(s), added %s: %s; removed %s: %s.',
|
||||
'%s edited reverted change(s), added %s: %s; removed %s: %s.',
|
||||
$actor,
|
||||
$add_count,
|
||||
$add_edges,
|
||||
@@ -64,7 +64,7 @@ final class DiffusionCommitRevertsCommitEdgeType extends PhabricatorEdgeType {
|
||||
$add_edges) {
|
||||
|
||||
return pht(
|
||||
'%s added %s reverted commit(s) for %s: %s.',
|
||||
'%s added %s reverted change(s) for %s: %s.',
|
||||
$actor,
|
||||
$add_count,
|
||||
$object,
|
||||
@@ -78,7 +78,7 @@ final class DiffusionCommitRevertsCommitEdgeType extends PhabricatorEdgeType {
|
||||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s removed %s reverted commit(s) for %s: %s.',
|
||||
'%s removed %s reverted change(s) for %s: %s.',
|
||||
$actor,
|
||||
$rem_count,
|
||||
$object,
|
||||
@@ -95,7 +95,7 @@ final class DiffusionCommitRevertsCommitEdgeType extends PhabricatorEdgeType {
|
||||
$rem_edges) {
|
||||
|
||||
return pht(
|
||||
'%s edited reverted commit(s) for %s, added %s: %s; removed %s: %s.',
|
||||
'%s edited reverted change(s) for %s, added %s: %s; removed %s: %s.',
|
||||
$actor,
|
||||
$object,
|
||||
$add_count,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -84,7 +84,7 @@ final class DiffusionGitLFSAuthenticateWorkflow
|
||||
// This works even if normal HTTP repository operations are not available
|
||||
// on this host, and does not require the user to have a VCS password.
|
||||
|
||||
$user = $this->getUser();
|
||||
$user = $this->getSSHUser();
|
||||
|
||||
$authorization = DiffusionGitLFSTemporaryTokenType::newHTTPAuthorization(
|
||||
$repository,
|
||||
|
||||
@@ -135,13 +135,16 @@ final class HeraldCommitAdapter
|
||||
}
|
||||
|
||||
public function loadAffectedPaths() {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
if ($this->affectedPaths === null) {
|
||||
$result = PhabricatorOwnerPathQuery::loadAffectedPaths(
|
||||
$this->getRepository(),
|
||||
$this->commit,
|
||||
PhabricatorUser::getOmnipotentUser());
|
||||
$viewer);
|
||||
$this->affectedPaths = $result;
|
||||
}
|
||||
|
||||
return $this->affectedPaths;
|
||||
}
|
||||
|
||||
@@ -172,6 +175,8 @@ final class HeraldCommitAdapter
|
||||
}
|
||||
|
||||
public function loadDifferentialRevision() {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
if ($this->affectedRevision === null) {
|
||||
$this->affectedRevision = false;
|
||||
|
||||
@@ -189,7 +194,7 @@ final class HeraldCommitAdapter
|
||||
|
||||
$revision = id(new DifferentialRevisionQuery())
|
||||
->withIDs(array($revision_id))
|
||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||
->setViewer($viewer)
|
||||
->needReviewers(true)
|
||||
->executeOne();
|
||||
if ($revision) {
|
||||
@@ -197,6 +202,7 @@ final class HeraldCommitAdapter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->affectedRevision;
|
||||
}
|
||||
|
||||
@@ -323,7 +329,7 @@ final class HeraldCommitAdapter
|
||||
}
|
||||
|
||||
private function callConduit($method, array $params) {
|
||||
$viewer = PhabricatorUser::getOmnipotentUser();
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$drequest = DiffusionRequest::newFromDictionary(
|
||||
array(
|
||||
|
||||
@@ -26,6 +26,12 @@ final class DiffusionPullLogSearchEngine
|
||||
$query->withPullerPHIDs($map['pullerPHIDs']);
|
||||
}
|
||||
|
||||
if ($map['createdStart'] || $map['createdEnd']) {
|
||||
$query->withEpochBetween(
|
||||
$map['createdStart'],
|
||||
$map['createdEnd']);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
@@ -44,17 +50,19 @@ final class DiffusionPullLogSearchEngine
|
||||
->setLabel(pht('Pullers'))
|
||||
->setDescription(
|
||||
pht('Search for pull logs by specific users.')),
|
||||
id(new PhabricatorSearchDateField())
|
||||
->setLabel(pht('Created After'))
|
||||
->setKey('createdStart'),
|
||||
id(new PhabricatorSearchDateField())
|
||||
->setLabel(pht('Created Before'))
|
||||
->setKey('createdEnd'),
|
||||
);
|
||||
}
|
||||
|
||||
protected function newExportFields() {
|
||||
return array(
|
||||
id(new PhabricatorIDExportField())
|
||||
->setKey('id')
|
||||
->setLabel(pht('ID')),
|
||||
id(new PhabricatorPHIDExportField())
|
||||
->setKey('phid')
|
||||
->setLabel(pht('PHID')),
|
||||
$viewer = $this->requireViewer();
|
||||
|
||||
$fields = array(
|
||||
id(new PhabricatorPHIDExportField())
|
||||
->setKey('repositoryPHID')
|
||||
->setLabel(pht('Repository PHID')),
|
||||
@@ -80,9 +88,17 @@ final class DiffusionPullLogSearchEngine
|
||||
->setKey('date')
|
||||
->setLabel(pht('Date')),
|
||||
);
|
||||
|
||||
if ($viewer->getIsAdmin()) {
|
||||
$fields[] = id(new PhabricatorStringExportField())
|
||||
->setKey('remoteAddress')
|
||||
->setLabel(pht('Remote Address'));
|
||||
}
|
||||
|
||||
public function newExport(array $events) {
|
||||
return $fields;
|
||||
}
|
||||
|
||||
protected function newExportData(array $events) {
|
||||
$viewer = $this->requireViewer();
|
||||
|
||||
$phids = array();
|
||||
@@ -111,9 +127,7 @@ final class DiffusionPullLogSearchEngine
|
||||
$puller_name = null;
|
||||
}
|
||||
|
||||
$export[] = array(
|
||||
'id' => $event->getID(),
|
||||
'phid' => $event->getPHID(),
|
||||
$map = array(
|
||||
'repositoryPHID' => $repository_phid,
|
||||
'repository' => $repository_name,
|
||||
'pullerPHID' => $puller_phid,
|
||||
@@ -123,6 +137,12 @@ final class DiffusionPullLogSearchEngine
|
||||
'code' => $event->getResultCode(),
|
||||
'date' => $event->getEpoch(),
|
||||
);
|
||||
|
||||
if ($viewer->getIsAdmin()) {
|
||||
$map['remoteAddress'] = $event->getRemoteAddress();
|
||||
}
|
||||
|
||||
$export[] = $map;
|
||||
}
|
||||
|
||||
return $export;
|
||||
|
||||
@@ -22,24 +22,10 @@ final class DiffusionPullLogListView extends AphrontView {
|
||||
}
|
||||
$handles = $viewer->loadHandles($handle_phids);
|
||||
|
||||
// Figure out which repositories are editable. We only let you see remote
|
||||
// IPs if you have edit capability on a repository.
|
||||
$editable_repos = array();
|
||||
if ($events) {
|
||||
$editable_repos = id(new PhabricatorRepositoryQuery())
|
||||
->setViewer($viewer)
|
||||
->requireCapabilities(
|
||||
array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
))
|
||||
->withPHIDs(mpull($events, 'getRepositoryPHID'))
|
||||
->execute();
|
||||
$editable_repos = mpull($editable_repos, null, 'getPHID');
|
||||
}
|
||||
// Only administrators can view remote addresses.
|
||||
$remotes_visible = $viewer->getIsAdmin();
|
||||
|
||||
$rows = array();
|
||||
$any_host = false;
|
||||
foreach ($events as $event) {
|
||||
if ($event->getRepositoryPHID()) {
|
||||
$repository = $event->getRepository();
|
||||
@@ -47,13 +33,10 @@ final class DiffusionPullLogListView extends AphrontView {
|
||||
$repository = null;
|
||||
}
|
||||
|
||||
// Reveal this if it's valid and the user can edit the repository. For
|
||||
// invalid requests you currently have to go fishing in the database.
|
||||
$remote_address = '-';
|
||||
if ($repository) {
|
||||
if (isset($editable_repos[$event->getRepositoryPHID()])) {
|
||||
if ($remotes_visible) {
|
||||
$remote_address = $event->getRemoteAddress();
|
||||
}
|
||||
} else {
|
||||
$remote_address = null;
|
||||
}
|
||||
|
||||
$event_id = $event->getID();
|
||||
@@ -107,6 +90,13 @@ final class DiffusionPullLogListView extends AphrontView {
|
||||
'',
|
||||
'n',
|
||||
'right',
|
||||
))
|
||||
->setColumnVisibility(
|
||||
array(
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
$remotes_visible,
|
||||
));
|
||||
|
||||
return $table;
|
||||
|
||||
@@ -25,31 +25,21 @@ final class DiffusionPushLogListView extends AphrontView {
|
||||
|
||||
$handles = $viewer->loadHandles($handle_phids);
|
||||
|
||||
// Figure out which repositories are editable. We only let you see remote
|
||||
// IPs if you have edit capability on a repository.
|
||||
$editable_repos = array();
|
||||
if ($logs) {
|
||||
$editable_repos = id(new PhabricatorRepositoryQuery())
|
||||
->setViewer($viewer)
|
||||
->requireCapabilities(
|
||||
array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
))
|
||||
->withPHIDs(mpull($logs, 'getRepositoryPHID'))
|
||||
->execute();
|
||||
$editable_repos = mpull($editable_repos, null, 'getPHID');
|
||||
}
|
||||
// Only administrators can view remote addresses.
|
||||
$remotes_visible = $viewer->getIsAdmin();
|
||||
|
||||
$flag_map = PhabricatorRepositoryPushLog::getFlagDisplayNames();
|
||||
$reject_map = PhabricatorRepositoryPushLog::getRejectCodeDisplayNames();
|
||||
|
||||
$rows = array();
|
||||
$any_host = false;
|
||||
foreach ($logs as $log) {
|
||||
$repository = $log->getRepository();
|
||||
|
||||
// Reveal this if it's valid and the user can edit the repository.
|
||||
$remote_address = '-';
|
||||
if (isset($editable_repos[$log->getRepositoryPHID()])) {
|
||||
if ($remotes_visible) {
|
||||
$remote_address = $log->getPushEvent()->getRemoteAddress();
|
||||
} else {
|
||||
$remote_address = null;
|
||||
}
|
||||
|
||||
$event_id = $log->getPushEvent()->getID();
|
||||
@@ -72,6 +62,23 @@ final class DiffusionPushLogListView extends AphrontView {
|
||||
$device = null;
|
||||
}
|
||||
|
||||
$flags = $log->getChangeFlags();
|
||||
$flag_names = array();
|
||||
foreach ($flag_map as $flag_key => $flag_name) {
|
||||
if (($flags & $flag_key) === $flag_key) {
|
||||
$flag_names[] = $flag_name;
|
||||
}
|
||||
}
|
||||
$flag_names = phutil_implode_html(
|
||||
phutil_tag('br'),
|
||||
$flag_names);
|
||||
|
||||
$reject_code = $log->getPushEvent()->getRejectCode();
|
||||
$reject_label = idx(
|
||||
$reject_map,
|
||||
$reject_code,
|
||||
pht('Unknown ("%s")', $reject_code));
|
||||
|
||||
$rows[] = array(
|
||||
phutil_tag(
|
||||
'a',
|
||||
@@ -98,10 +105,8 @@ final class DiffusionPushLogListView extends AphrontView {
|
||||
'href' => $repository->getCommitURI($log->getRefNew()),
|
||||
),
|
||||
$log->getRefNewShort()),
|
||||
|
||||
// TODO: Make these human-readable.
|
||||
$log->getChangeFlags(),
|
||||
$log->getPushEvent()->getRejectCode(),
|
||||
$flag_names,
|
||||
$reject_label,
|
||||
$viewer->formatShortDateTime($log->getEpoch()),
|
||||
);
|
||||
}
|
||||
@@ -120,7 +125,7 @@ final class DiffusionPushLogListView extends AphrontView {
|
||||
pht('Old'),
|
||||
pht('New'),
|
||||
pht('Flags'),
|
||||
pht('Code'),
|
||||
pht('Result'),
|
||||
pht('Date'),
|
||||
))
|
||||
->setColumnClasses(
|
||||
@@ -135,6 +140,8 @@ final class DiffusionPushLogListView extends AphrontView {
|
||||
'wide',
|
||||
'n',
|
||||
'n',
|
||||
'',
|
||||
'',
|
||||
'right',
|
||||
))
|
||||
->setColumnVisibility(
|
||||
@@ -142,7 +149,7 @@ final class DiffusionPushLogListView extends AphrontView {
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
$remotes_visible,
|
||||
true,
|
||||
$any_host,
|
||||
));
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -272,8 +272,12 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
||||
$file->setByteSize($length);
|
||||
|
||||
// NOTE: Once we receive the first chunk, we'll detect its MIME type and
|
||||
// update the parent file. This matters for large media files like video.
|
||||
// update the parent file if a MIME type hasn't been provided. This matters
|
||||
// for large media files like video.
|
||||
$mime_type = idx($params, 'mime-type');
|
||||
if (!strlen($mime_type)) {
|
||||
$file->setMimeType('application/octet-stream');
|
||||
}
|
||||
|
||||
$chunked_hash = idx($params, 'chunkedHash');
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ abstract class PhabricatorFileUploadSource
|
||||
private $name;
|
||||
private $relativeTTL;
|
||||
private $viewPolicy;
|
||||
private $mimeType;
|
||||
private $authorPHID;
|
||||
|
||||
private $rope;
|
||||
private $data;
|
||||
@@ -51,6 +53,24 @@ abstract class PhabricatorFileUploadSource
|
||||
return $this->byteLimit;
|
||||
}
|
||||
|
||||
public function setMIMEType($mime_type) {
|
||||
$this->mimeType = $mime_type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMIMEType() {
|
||||
return $this->mimeType;
|
||||
}
|
||||
|
||||
public function setAuthorPHID($author_phid) {
|
||||
$this->authorPHID = $author_phid;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAuthorPHID() {
|
||||
return $this->authorPHID;
|
||||
}
|
||||
|
||||
public function uploadFile() {
|
||||
if (!$this->shouldChunkFile()) {
|
||||
return $this->writeSingleFile();
|
||||
@@ -245,6 +265,16 @@ abstract class PhabricatorFileUploadSource
|
||||
$parameters['ttl.relative'] = $ttl;
|
||||
}
|
||||
|
||||
$mime_type = $this->getMimeType();
|
||||
if ($mime_type !== null) {
|
||||
$parameters['mime-type'] = $mime_type;
|
||||
}
|
||||
|
||||
$author_phid = $this->getAuthorPHID();
|
||||
if ($author_phid !== null) {
|
||||
$parameters['authorPHID'] = $author_phid;
|
||||
}
|
||||
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -39,6 +39,8 @@ abstract class HeraldAdapter extends Phobject {
|
||||
private $edgeCache = array();
|
||||
private $forbiddenActions = array();
|
||||
private $viewer;
|
||||
private $mustEncryptReasons = array();
|
||||
private $actingAsPHID;
|
||||
|
||||
public function getEmailPHIDs() {
|
||||
return array_values($this->emailPHIDs);
|
||||
@@ -48,6 +50,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 +1193,17 @@ 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -265,7 +265,15 @@ final class HeraldRuleController extends HeraldController {
|
||||
$new_name = $request->getStr('name');
|
||||
$match_all = ($request->getStr('must_match') == 'all');
|
||||
|
||||
$repetition_policy_param = $request->getStr('repetition_policy');
|
||||
$repetition_policy = $request->getStr('repetition_policy');
|
||||
|
||||
// If the user selected an invalid policy, or there's only one possible
|
||||
// value so we didn't render a control, adjust the value to the first
|
||||
// valid policy value.
|
||||
$repetition_options = $this->getRepetitionOptionMap($adapter);
|
||||
if (!isset($repetition_options[$repetition_policy])) {
|
||||
$repetition_policy = head_key($repetition_options);
|
||||
}
|
||||
|
||||
$e_name = true;
|
||||
$errors = array();
|
||||
@@ -348,7 +356,7 @@ final class HeraldRuleController extends HeraldController {
|
||||
$match_all,
|
||||
$conditions,
|
||||
$actions,
|
||||
$repetition_policy_param);
|
||||
$repetition_policy);
|
||||
|
||||
$xactions = array();
|
||||
$xactions[] = id(new HeraldRuleTransaction())
|
||||
@@ -373,7 +381,7 @@ final class HeraldRuleController extends HeraldController {
|
||||
// mutate current rule, so it would be sent to the client in the right state
|
||||
$rule->setMustMatchAll((int)$match_all);
|
||||
$rule->setName($new_name);
|
||||
$rule->setRepetitionPolicyStringConstant($repetition_policy_param);
|
||||
$rule->setRepetitionPolicyStringConstant($repetition_policy);
|
||||
$rule->attachConditions($conditions);
|
||||
$rule->attachActions($actions);
|
||||
|
||||
@@ -594,13 +602,9 @@ final class HeraldRuleController extends HeraldController {
|
||||
*/
|
||||
private function renderRepetitionSelector($rule, HeraldAdapter $adapter) {
|
||||
$repetition_policy = $rule->getRepetitionPolicyStringConstant();
|
||||
|
||||
$repetition_options = $adapter->getRepetitionOptions();
|
||||
$repetition_names = HeraldRule::getRepetitionPolicySelectOptionMap();
|
||||
$repetition_map = array_select_keys($repetition_names, $repetition_options);
|
||||
|
||||
$repetition_map = $this->getRepetitionOptionMap($adapter);
|
||||
if (count($repetition_map) < 2) {
|
||||
return head($repetition_names);
|
||||
return head($repetition_map);
|
||||
} else {
|
||||
return AphrontFormSelectControl::renderSelectTag(
|
||||
$repetition_policy,
|
||||
@@ -611,6 +615,11 @@ final class HeraldRuleController extends HeraldController {
|
||||
}
|
||||
}
|
||||
|
||||
private function getRepetitionOptionMap(HeraldAdapter $adapter) {
|
||||
$repetition_options = $adapter->getRepetitionOptions();
|
||||
$repetition_names = HeraldRule::getRepetitionPolicySelectOptionMap();
|
||||
return array_select_keys($repetition_names, $repetition_options);
|
||||
}
|
||||
|
||||
protected function buildTokenizerTemplates() {
|
||||
$template = new AphrontTokenizerTemplateView();
|
||||
|
||||
@@ -41,6 +41,7 @@ final class HeraldTestConsoleController extends HeraldController {
|
||||
|
||||
$adapter
|
||||
->setIsNewObject(false)
|
||||
->setActingAsPHID($viewer->getPHID())
|
||||
->setViewer($viewer);
|
||||
|
||||
$rules = id(new HeraldRuleQuery())
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -51,13 +51,13 @@ final class PhabricatorManiphestApplication extends PhabricatorApplication {
|
||||
'/T(?P<id>[1-9]\d*)' => 'ManiphestTaskDetailController',
|
||||
'/maniphest/' => array(
|
||||
'(?:project/(?P<projectKey>[^/]+)/)?(?:type/(?P<taskTypeKey>[^/]+)/)?(?:query/(?P<queryKey>[^/]+)/)?' => 'ManiphestTaskListController',
|
||||
$this->getQueryRoutePattern() => 'ManiphestTaskListController',
|
||||
'report/(?:(?P<view>\w+)/)?' => 'ManiphestReportController',
|
||||
$this->getBulkRoutePattern('bulk/') => 'ManiphestBulkEditController',
|
||||
'task/' => array(
|
||||
$this->getEditRoutePattern('edit/')
|
||||
=> 'ManiphestTaskEditController',
|
||||
),
|
||||
'export/(?P<key>[^/]+)/' => 'ManiphestExportController',
|
||||
'subpriority/' => 'ManiphestSubpriorityController',
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
<?php
|
||||
|
||||
final class ManiphestExportController extends ManiphestController {
|
||||
|
||||
/**
|
||||
* @phutil-external-symbol class PHPExcel
|
||||
* @phutil-external-symbol class PHPExcel_IOFactory
|
||||
* @phutil-external-symbol class PHPExcel_Style_NumberFormat
|
||||
* @phutil-external-symbol class PHPExcel_Cell_DataType
|
||||
*/
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $this->getViewer();
|
||||
$key = $request->getURIData('key');
|
||||
|
||||
$ok = @include_once 'PHPExcel.php';
|
||||
if (!$ok) {
|
||||
$dialog = $this->newDialog();
|
||||
|
||||
$inst1 = pht(
|
||||
'This system does not have PHPExcel installed. This software '.
|
||||
'component is required to export tasks to Excel. Have your system '.
|
||||
'administrator install it from:');
|
||||
|
||||
$inst2 = pht(
|
||||
'Your PHP "%s" needs to be updated to include the '.
|
||||
'PHPExcel Classes directory.',
|
||||
'include_path');
|
||||
|
||||
$dialog->setTitle(pht('Excel Export Not Configured'));
|
||||
$dialog->appendChild(hsprintf(
|
||||
'<p>%s</p>'.
|
||||
'<br />'.
|
||||
'<p>'.
|
||||
'<a href="https://github.com/PHPOffice/PHPExcel">'.
|
||||
'https://github.com/PHPOffice/PHPExcel'.
|
||||
'</a>'.
|
||||
'</p>'.
|
||||
'<br />'.
|
||||
'<p>%s</p>',
|
||||
$inst1,
|
||||
$inst2));
|
||||
|
||||
$dialog->addCancelButton('/maniphest/');
|
||||
return id(new AphrontDialogResponse())->setDialog($dialog);
|
||||
}
|
||||
|
||||
// TODO: PHPExcel has a dependency on the PHP zip extension. We should test
|
||||
// for that here, since it fatals if we don't have the ZipArchive class.
|
||||
|
||||
$saved = id(new PhabricatorSavedQueryQuery())
|
||||
->setViewer($viewer)
|
||||
->withQueryKeys(array($key))
|
||||
->executeOne();
|
||||
if (!$saved) {
|
||||
$engine = id(new ManiphestTaskSearchEngine())
|
||||
->setViewer($viewer)
|
||||
->setProjectKey($this->projectKey)
|
||||
->setTaskTypeKey($this->taskTypeKey);
|
||||
if ($engine->isBuiltinQuery($key)) {
|
||||
$saved = $engine->buildSavedQueryFromBuiltin($key);
|
||||
}
|
||||
if (!$saved) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
}
|
||||
|
||||
$formats = ManiphestExcelFormat::loadAllFormats();
|
||||
$export_formats = array();
|
||||
foreach ($formats as $format_class => $format_object) {
|
||||
$export_formats[$format_class] = $format_object->getName();
|
||||
}
|
||||
|
||||
if (!$request->isDialogFormPost()) {
|
||||
$dialog = new AphrontDialogView();
|
||||
$dialog->setUser($viewer);
|
||||
|
||||
$dialog->setTitle(pht('Export Tasks to Excel'));
|
||||
$dialog->appendChild(
|
||||
phutil_tag(
|
||||
'p',
|
||||
array(),
|
||||
pht('Do you want to export the query results to Excel?')));
|
||||
|
||||
$form = id(new PHUIFormLayoutView())
|
||||
->appendChild(
|
||||
id(new AphrontFormSelectControl())
|
||||
->setLabel(pht('Format:'))
|
||||
->setName('excel-format')
|
||||
->setOptions($export_formats));
|
||||
|
||||
$dialog->appendChild($form);
|
||||
|
||||
$dialog->addCancelButton('/maniphest/');
|
||||
$dialog->addSubmitButton(pht('Export to Excel'));
|
||||
return id(new AphrontDialogResponse())->setDialog($dialog);
|
||||
}
|
||||
|
||||
$format = idx($formats, $request->getStr('excel-format'));
|
||||
if ($format === null) {
|
||||
throw new Exception(pht('Excel format object not found.'));
|
||||
}
|
||||
|
||||
$saved->makeEphemeral();
|
||||
$saved->setParameter('limit', PHP_INT_MAX);
|
||||
|
||||
$engine = id(new ManiphestTaskSearchEngine())
|
||||
->setViewer($viewer);
|
||||
|
||||
$query = $engine->buildQueryFromSavedQuery($saved);
|
||||
$query->setViewer($viewer);
|
||||
$tasks = $query->execute();
|
||||
|
||||
$all_projects = array_mergev(mpull($tasks, 'getProjectPHIDs'));
|
||||
$all_assigned = mpull($tasks, 'getOwnerPHID');
|
||||
|
||||
$handles = id(new PhabricatorHandleQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs(array_merge($all_projects, $all_assigned))
|
||||
->execute();
|
||||
|
||||
$workbook = new PHPExcel();
|
||||
$format->buildWorkbook($workbook, $tasks, $handles, $viewer);
|
||||
$writer = PHPExcel_IOFactory::createWriter($workbook, 'Excel2007');
|
||||
|
||||
ob_start();
|
||||
$writer->save('php://output');
|
||||
$data = ob_get_clean();
|
||||
|
||||
$mime = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
|
||||
|
||||
return id(new AphrontFileResponse())
|
||||
->setMimeType($mime)
|
||||
->setDownload($format->getFileName().'.xlsx')
|
||||
->setContent($data);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -196,6 +196,7 @@ EODOCS
|
||||
pht('New task owner, or `null` to unassign.'))
|
||||
->setTransactionType(ManiphestTaskOwnerTransaction::TRANSACTIONTYPE)
|
||||
->setIsCopyable(true)
|
||||
->setIsNullable(true)
|
||||
->setSingleValue($object->getOwnerPHID())
|
||||
->setCommentActionLabel(pht('Assign / Claim'))
|
||||
->setCommentActionValue($owner_value)
|
||||
|
||||
@@ -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(
|
||||
@@ -534,7 +533,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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
<?php
|
||||
|
||||
final class ManiphestExcelDefaultFormat extends ManiphestExcelFormat {
|
||||
|
||||
public function getName() {
|
||||
return pht('Default');
|
||||
}
|
||||
|
||||
public function getFileName() {
|
||||
return 'maniphest_tasks_'.date('Ymd');
|
||||
}
|
||||
|
||||
/**
|
||||
* @phutil-external-symbol class PHPExcel
|
||||
* @phutil-external-symbol class PHPExcel_IOFactory
|
||||
* @phutil-external-symbol class PHPExcel_Style_NumberFormat
|
||||
* @phutil-external-symbol class PHPExcel_Cell_DataType
|
||||
*/
|
||||
public function buildWorkbook(
|
||||
PHPExcel $workbook,
|
||||
array $tasks,
|
||||
array $handles,
|
||||
PhabricatorUser $user) {
|
||||
|
||||
$sheet = $workbook->setActiveSheetIndex(0);
|
||||
$sheet->setTitle(pht('Tasks'));
|
||||
|
||||
$widths = array(
|
||||
null,
|
||||
15,
|
||||
null,
|
||||
10,
|
||||
15,
|
||||
15,
|
||||
60,
|
||||
30,
|
||||
20,
|
||||
100,
|
||||
);
|
||||
|
||||
foreach ($widths as $col => $width) {
|
||||
if ($width !== null) {
|
||||
$sheet->getColumnDimension($this->col($col))->setWidth($width);
|
||||
}
|
||||
}
|
||||
|
||||
$status_map = ManiphestTaskStatus::getTaskStatusMap();
|
||||
$pri_map = ManiphestTaskPriority::getTaskPriorityMap();
|
||||
|
||||
$date_format = null;
|
||||
|
||||
$rows = array();
|
||||
$rows[] = array(
|
||||
pht('ID'),
|
||||
pht('Owner'),
|
||||
pht('Status'),
|
||||
pht('Priority'),
|
||||
pht('Date Created'),
|
||||
pht('Date Updated'),
|
||||
pht('Title'),
|
||||
pht('Tags'),
|
||||
pht('URI'),
|
||||
pht('Description'),
|
||||
);
|
||||
|
||||
$is_date = array(
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
|
||||
$header_format = array(
|
||||
'font' => array(
|
||||
'bold' => true,
|
||||
),
|
||||
);
|
||||
|
||||
foreach ($tasks as $task) {
|
||||
$task_owner = null;
|
||||
if ($task->getOwnerPHID()) {
|
||||
$task_owner = $handles[$task->getOwnerPHID()]->getName();
|
||||
}
|
||||
|
||||
$projects = array();
|
||||
foreach ($task->getProjectPHIDs() as $phid) {
|
||||
$projects[] = $handles[$phid]->getName();
|
||||
}
|
||||
$projects = implode(', ', $projects);
|
||||
|
||||
$rows[] = array(
|
||||
'T'.$task->getID(),
|
||||
$task_owner,
|
||||
idx($status_map, $task->getStatus(), '?'),
|
||||
idx($pri_map, $task->getPriority(), '?'),
|
||||
$this->computeExcelDate($task->getDateCreated()),
|
||||
$this->computeExcelDate($task->getDateModified()),
|
||||
$task->getTitle(),
|
||||
$projects,
|
||||
PhabricatorEnv::getProductionURI('/T'.$task->getID()),
|
||||
id(new PhutilUTF8StringTruncator())
|
||||
->setMaximumBytes(512)
|
||||
->truncateString($task->getDescription()),
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($rows as $row => $cols) {
|
||||
foreach ($cols as $col => $spec) {
|
||||
$cell_name = $this->col($col).($row + 1);
|
||||
$cell = $sheet
|
||||
->setCellValue($cell_name, $spec, $return_cell = true);
|
||||
|
||||
if ($row == 0) {
|
||||
$sheet->getStyle($cell_name)->applyFromArray($header_format);
|
||||
}
|
||||
|
||||
if ($is_date[$col]) {
|
||||
$code = PHPExcel_Style_NumberFormat::FORMAT_DATE_YYYYMMDD2;
|
||||
$sheet
|
||||
->getStyle($cell_name)
|
||||
->getNumberFormat()
|
||||
->setFormatCode($code);
|
||||
} else {
|
||||
$cell->setDataType(PHPExcel_Cell_DataType::TYPE_STRING);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function col($n) {
|
||||
return chr(ord('A') + $n);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
<?php
|
||||
|
||||
abstract class ManiphestExcelFormat extends Phobject {
|
||||
|
||||
final public static function loadAllFormats() {
|
||||
return id(new PhutilClassMapQuery())
|
||||
->setAncestorClass(__CLASS__)
|
||||
->setSortMethod('getOrder')
|
||||
->execute();
|
||||
}
|
||||
|
||||
abstract public function getName();
|
||||
abstract public function getFileName();
|
||||
|
||||
public function getOrder() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected function computeExcelDate($epoch) {
|
||||
$seconds_per_day = (60 * 60 * 24);
|
||||
$offset = ($seconds_per_day * 25569);
|
||||
|
||||
return ($epoch + $offset) / $seconds_per_day;
|
||||
}
|
||||
|
||||
/**
|
||||
* @phutil-external-symbol class PHPExcel
|
||||
*/
|
||||
abstract public function buildWorkbook(
|
||||
PHPExcel $workbook,
|
||||
array $tasks,
|
||||
array $handles,
|
||||
PhabricatorUser $user);
|
||||
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
<?php
|
||||
|
||||
final class ManiphestExcelFormatTestCase extends PhabricatorTestCase {
|
||||
|
||||
public function testLoadAllFormats() {
|
||||
ManiphestExcelFormat::loadAllFormats();
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
@@ -411,6 +425,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,
|
||||
@@ -755,6 +790,10 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||
'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'),
|
||||
@@ -773,6 +812,7 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||
'outdated',
|
||||
'newest',
|
||||
'oldest',
|
||||
'closed',
|
||||
'title',
|
||||
)) + $orders;
|
||||
|
||||
@@ -822,6 +862,12 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||
'column' => 'dateModified',
|
||||
'type' => 'int',
|
||||
),
|
||||
'closed' => array(
|
||||
'table' => 'task',
|
||||
'column' => 'closedEpoch',
|
||||
'type' => 'int',
|
||||
'null' => 'tail',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -840,6 +886,7 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||
'status' => $task->getStatus(),
|
||||
'title' => $task->getTitle(),
|
||||
'updated' => $task->getDateModified(),
|
||||
'closed' => $task->getClosedEpoch(),
|
||||
);
|
||||
|
||||
foreach ($keys as $key) {
|
||||
|
||||
@@ -128,6 +128,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'),
|
||||
@@ -155,6 +166,9 @@ final class ManiphestTaskSearchEngine
|
||||
'createdEnd',
|
||||
'modifiedStart',
|
||||
'modifiedEnd',
|
||||
'closedStart',
|
||||
'closedEnd',
|
||||
'closerPHIDs',
|
||||
'limit',
|
||||
);
|
||||
}
|
||||
@@ -210,6 +224,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']);
|
||||
}
|
||||
@@ -485,4 +507,131 @@ final class ManiphestTaskSearchEngine
|
||||
return $view;
|
||||
}
|
||||
|
||||
|
||||
protected function newExportFields() {
|
||||
$fields = array(
|
||||
id(new PhabricatorStringExportField())
|
||||
->setKey('monogram')
|
||||
->setLabel(pht('Monogram')),
|
||||
id(new PhabricatorPHIDExportField())
|
||||
->setKey('authorPHID')
|
||||
->setLabel(pht('Author PHID')),
|
||||
id(new PhabricatorStringExportField())
|
||||
->setKey('author')
|
||||
->setLabel(pht('Author')),
|
||||
id(new PhabricatorPHIDExportField())
|
||||
->setKey('ownerPHID')
|
||||
->setLabel(pht('Owner PHID')),
|
||||
id(new PhabricatorStringExportField())
|
||||
->setKey('owner')
|
||||
->setLabel(pht('Owner')),
|
||||
id(new PhabricatorStringExportField())
|
||||
->setKey('status')
|
||||
->setLabel(pht('Status')),
|
||||
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')),
|
||||
id(new PhabricatorStringExportField())
|
||||
->setKey('priorityName')
|
||||
->setLabel(pht('Priority Name')),
|
||||
id(new PhabricatorStringExportField())
|
||||
->setKey('subtype')
|
||||
->setLabel('Subtype'),
|
||||
id(new PhabricatorURIExportField())
|
||||
->setKey('uri')
|
||||
->setLabel(pht('URI')),
|
||||
id(new PhabricatorStringExportField())
|
||||
->setKey('title')
|
||||
->setLabel(pht('Title')),
|
||||
id(new PhabricatorStringExportField())
|
||||
->setKey('description')
|
||||
->setLabel(pht('Description')),
|
||||
);
|
||||
|
||||
if (ManiphestTaskPoints::getIsEnabled()) {
|
||||
$fields[] = id(new PhabricatorIntExportField())
|
||||
->setKey('points')
|
||||
->setLabel('Points');
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
protected function newExportData(array $tasks) {
|
||||
$viewer = $this->requireViewer();
|
||||
|
||||
$phids = array();
|
||||
foreach ($tasks as $task) {
|
||||
$phids[] = $task->getAuthorPHID();
|
||||
$phids[] = $task->getOwnerPHID();
|
||||
$phids[] = $task->getCloserPHID();
|
||||
}
|
||||
$handles = $viewer->loadHandles($phids);
|
||||
|
||||
$export = array();
|
||||
foreach ($tasks as $task) {
|
||||
|
||||
$author_phid = $task->getAuthorPHID();
|
||||
if ($author_phid) {
|
||||
$author_name = $handles[$author_phid]->getName();
|
||||
} else {
|
||||
$author_name = null;
|
||||
}
|
||||
|
||||
$owner_phid = $task->getOwnerPHID();
|
||||
if ($owner_phid) {
|
||||
$owner_name = $handles[$owner_phid]->getName();
|
||||
} else {
|
||||
$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);
|
||||
|
||||
$priority_value = $task->getPriority();
|
||||
$priority_name = ManiphestTaskPriority::getTaskPriorityName(
|
||||
$priority_value);
|
||||
|
||||
$export[] = array(
|
||||
'monogram' => $task->getMonogram(),
|
||||
'authorPHID' => $author_phid,
|
||||
'author' => $author_name,
|
||||
'ownerPHID' => $owner_phid,
|
||||
'owner' => $owner_name,
|
||||
'status' => $status_value,
|
||||
'statusName' => $status_name,
|
||||
'priority' => $priority_value,
|
||||
'priorityName' => $priority_name,
|
||||
'points' => $task->getPoints(),
|
||||
'subtype' => $task->getSubtype(),
|
||||
'title' => $task->getTitle(),
|
||||
'uri' => PhabricatorEnv::getProductionURI($task->getURI()),
|
||||
'description' => $task->getDescription(),
|
||||
'dateClosed' => $task->getClosedEpoch(),
|
||||
'closerPHID' => $closer_phid,
|
||||
'closer' => $closer_name,
|
||||
);
|
||||
}
|
||||
|
||||
return $export;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -68,6 +68,11 @@ final class ManiphestTaskListView extends ManiphestView {
|
||||
->setHeader($task->getTitle())
|
||||
->setHref('/T'.$task->getID());
|
||||
|
||||
if ($task->getAuthorPHID()) {
|
||||
$author = $handles[$task->getAuthorPHID()];
|
||||
$item->addByline(pht('By: %s', $author->renderLink()));
|
||||
}
|
||||
|
||||
if ($task->getOwnerPHID()) {
|
||||
$owner = $handles[$task->getOwnerPHID()];
|
||||
$item->addByline(pht('Assigned: %s', $owner->renderLink()));
|
||||
@@ -87,19 +92,24 @@ final class ManiphestTaskListView extends ManiphestView {
|
||||
|
||||
$item->setStatusIcon($icon.' '.$color, $tooltip);
|
||||
|
||||
$date = phabricator_datetime($task->getDateModified(), $this->getUser());
|
||||
if ($task->isClosed()) {
|
||||
$closed_epoch = $task->getClosedEpoch();
|
||||
|
||||
if ($task->getAuthorPHID()) {
|
||||
$author = idx($handles, $task->getAuthorPHID());
|
||||
// TODO: This should be guaranteed, see T3817.
|
||||
if ($author) {
|
||||
$date = array($date, " by ", $author->renderLink());
|
||||
}
|
||||
// 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',
|
||||
$date);
|
||||
phabricator_datetime($task->getDateModified(), $this->getUser()));
|
||||
}
|
||||
|
||||
if ($this->showSubpriorityControls) {
|
||||
$item->setGrippable(true);
|
||||
|
||||
@@ -175,8 +175,7 @@ final class ManiphestTaskResultListView extends ManiphestView {
|
||||
}
|
||||
|
||||
if (!$user->isLoggedIn()) {
|
||||
// Don't show the batch editor or excel export for logged-out users.
|
||||
// Technically we //could// let them export, but ehh.
|
||||
// Don't show the batch editor for logged-out users.
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -220,14 +219,6 @@ final class ManiphestTaskResultListView extends ManiphestView {
|
||||
),
|
||||
pht("Bulk Edit Selected \xC2\xBB"));
|
||||
|
||||
$export = javelin_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => '/maniphest/export/'.$saved_query->getQueryKey().'/',
|
||||
'class' => 'button button-grey',
|
||||
),
|
||||
pht('Export to Excel'));
|
||||
|
||||
$hidden = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
@@ -239,14 +230,12 @@ final class ManiphestTaskResultListView extends ManiphestView {
|
||||
'<table class="maniphest-batch-editor-layout">'.
|
||||
'<tr>'.
|
||||
'<td>%s%s</td>'.
|
||||
'<td>%s</td>'.
|
||||
'<td id="batch-select-status-cell">%s</td>'.
|
||||
'<td class="batch-select-submit-cell">%s%s</td>'.
|
||||
'</tr>'.
|
||||
'</table>',
|
||||
$select_all,
|
||||
$select_none,
|
||||
$export,
|
||||
'',
|
||||
$submit,
|
||||
$hidden);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,14 +8,28 @@ final class PhabricatorMetaMTAMailgunReceiveController
|
||||
}
|
||||
|
||||
private function verifyMessage() {
|
||||
$api_key = PhabricatorEnv::getEnvConfig('mailgun.api-key');
|
||||
$request = $this->getRequest();
|
||||
$timestamp = $request->getStr('timestamp');
|
||||
$token = $request->getStr('token');
|
||||
$sig = $request->getStr('signature');
|
||||
$hash = hash_hmac('sha256', $timestamp.$token, $api_key);
|
||||
|
||||
return phutil_hashes_are_identical($sig, $hash);
|
||||
// An install may configure multiple Mailgun mailers, and we might receive
|
||||
// inbound mail from any of them. Test the signature to see if it matches
|
||||
// any configured Mailgun mailer.
|
||||
|
||||
$mailers = PhabricatorMetaMTAMail::newMailersWithTypes(
|
||||
array(
|
||||
PhabricatorMailImplementationMailgunAdapter::ADAPTERTYPE,
|
||||
));
|
||||
foreach ($mailers as $mailer) {
|
||||
$api_key = $mailer->getOption('api-key');
|
||||
$hash = hash_hmac('sha256', $timestamp.$token, $api_key);
|
||||
if (phutil_hashes_are_identical($sig, $hash)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorMetaMTAPostmarkReceiveController
|
||||
extends PhabricatorMetaMTAController {
|
||||
|
||||
public function shouldRequireLogin() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @phutil-external-symbol class PhabricatorStartup
|
||||
*/
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
// Don't process requests if we don't have a configured Postmark adapter.
|
||||
$mailers = PhabricatorMetaMTAMail::newMailersWithTypes(
|
||||
array(
|
||||
PhabricatorMailImplementationPostmarkAdapter::ADAPTERTYPE,
|
||||
));
|
||||
if (!$mailers) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$remote_address = $request->getRemoteAddress();
|
||||
$any_remote_match = false;
|
||||
foreach ($mailers as $mailer) {
|
||||
$inbound_addresses = $mailer->getOption('inbound-addresses');
|
||||
$cidr_list = PhutilCIDRList::newList($inbound_addresses);
|
||||
if ($cidr_list->containsAddress($remote_address)) {
|
||||
$any_remote_match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$any_remote_match) {
|
||||
return new Aphront400Response();
|
||||
}
|
||||
|
||||
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
||||
$raw_input = PhabricatorStartup::getRawInput();
|
||||
|
||||
try {
|
||||
$data = phutil_json_decode($raw_input);
|
||||
} catch (Exception $ex) {
|
||||
return new Aphront400Response();
|
||||
}
|
||||
|
||||
$raw_headers = array();
|
||||
$header_items = idx($data, 'Headers', array());
|
||||
foreach ($header_items as $header_item) {
|
||||
$name = idx($header_item, 'Name');
|
||||
$value = idx($header_item, 'Value');
|
||||
$raw_headers[$name] = $value;
|
||||
}
|
||||
|
||||
$headers = array(
|
||||
'to' => idx($data, 'To'),
|
||||
'from' => idx($data, 'From'),
|
||||
'cc' => idx($data, 'Cc'),
|
||||
'subject' => idx($data, 'Subject'),
|
||||
) + $raw_headers;
|
||||
|
||||
|
||||
$received = id(new PhabricatorMetaMTAReceivedMail())
|
||||
->setHeaders($headers)
|
||||
->setBodies(
|
||||
array(
|
||||
'text' => idx($data, 'TextBody'),
|
||||
'html' => idx($data, 'HtmlBody'),
|
||||
));
|
||||
|
||||
$file_phids = array();
|
||||
$attachments = idx($data, 'Attachments', array());
|
||||
foreach ($attachments as $attachment) {
|
||||
$file_data = idx($attachment, 'Content');
|
||||
$file_data = base64_decode($file_data);
|
||||
|
||||
try {
|
||||
$file = PhabricatorFile::newFromFileData(
|
||||
$file_data,
|
||||
array(
|
||||
'name' => idx($attachment, 'Name'),
|
||||
'viewPolicy' => PhabricatorPolicies::POLICY_NOONE,
|
||||
));
|
||||
$file_phids[] = $file->getPHID();
|
||||
} catch (Exception $ex) {
|
||||
phlog($ex);
|
||||
}
|
||||
}
|
||||
$received->setAttachments($file_phids);
|
||||
|
||||
try {
|
||||
$received->save();
|
||||
$received->processReceivedMail();
|
||||
} catch (Exception $ex) {
|
||||
phlog($ex);
|
||||
}
|
||||
|
||||
return id(new AphrontWebpageResponse())
|
||||
->setContent(pht("Got it! Thanks, Postmark!\n"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,6 +8,16 @@ final class PhabricatorMetaMTASendGridReceiveController
|
||||
}
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
// SendGrid doesn't sign payloads so we can't be sure that SendGrid
|
||||
// actually sent this request, but require a configured SendGrid mailer
|
||||
// before we activate this endpoint.
|
||||
$mailers = PhabricatorMetaMTAMail::newMailersWithTypes(
|
||||
array(
|
||||
PhabricatorMailImplementationSendGridAdapter::ADAPTERTYPE,
|
||||
));
|
||||
if (!$mailers) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
// No CSRF for SendGrid.
|
||||
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
abstract class PhabricatorMailEngineExtension
|
||||
extends Phobject {
|
||||
|
||||
private $viewer;
|
||||
private $editor;
|
||||
|
||||
final public function getExtensionKey() {
|
||||
return $this->getPhobjectClassConstant('EXTENSIONKEY');
|
||||
}
|
||||
|
||||
final public function setViewer($viewer) {
|
||||
$this->viewer = $viewer;
|
||||
return $this;
|
||||
}
|
||||
|
||||
final public function getViewer() {
|
||||
return $this->viewer;
|
||||
}
|
||||
|
||||
final public function setEditor(
|
||||
PhabricatorApplicationTransactionEditor $editor) {
|
||||
$this->editor = $editor;
|
||||
return $this;
|
||||
}
|
||||
|
||||
final public function getEditor() {
|
||||
return $this->editor;
|
||||
}
|
||||
|
||||
abstract public function supportsObject($object);
|
||||
abstract public function newMailStampTemplates($object);
|
||||
abstract public function newMailStamps($object, array $xactions);
|
||||
|
||||
final public static function getAllExtensions() {
|
||||
return id(new PhutilClassMapQuery())
|
||||
->setAncestorClass(__CLASS__)
|
||||
->setUniqueMethod('getExtensionKey')
|
||||
->execute();
|
||||
}
|
||||
|
||||
final protected function getMailStamp($key) {
|
||||
return $this->getEditor()->getMailStamp($key);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,8 +18,9 @@ final class MetaMTAMailSentGarbageCollector
|
||||
'dateCreated < %d LIMIT 100',
|
||||
$this->getGarbageEpoch());
|
||||
|
||||
$engine = new PhabricatorDestructionEngine();
|
||||
foreach ($mails as $mail) {
|
||||
$mail->delete();
|
||||
$engine->destroyObject($mail);
|
||||
}
|
||||
|
||||
return (count($mails) == 100);
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorMailMustEncryptHeraldAction
|
||||
extends HeraldAction {
|
||||
|
||||
const DO_MUST_ENCRYPT = 'do.must-encrypt';
|
||||
|
||||
const ACTIONCONST = 'email.must-encrypt';
|
||||
|
||||
public function getHeraldActionName() {
|
||||
return pht('Require secure email');
|
||||
}
|
||||
|
||||
public function renderActionDescription($value) {
|
||||
return pht(
|
||||
'Require mail content be transmitted only over secure channels.');
|
||||
}
|
||||
public function supportsObject($object) {
|
||||
return PhabricatorMetaMTAEmailHeraldAction::isMailGeneratingObject($object);
|
||||
}
|
||||
|
||||
public function getActionGroupKey() {
|
||||
return HeraldUtilityActionGroup::ACTIONGROUPKEY;
|
||||
}
|
||||
|
||||
public function supportsRuleType($rule_type) {
|
||||
return ($rule_type != HeraldRuleTypeConfig::RULE_TYPE_PERSONAL);
|
||||
}
|
||||
|
||||
public function getHeraldActionStandardType() {
|
||||
return self::STANDARD_NONE;
|
||||
}
|
||||
|
||||
public function applyEffect($object, HeraldEffect $effect) {
|
||||
$rule_phid = $effect->getRule()->getPHID();
|
||||
|
||||
$adapter = $this->getAdapter();
|
||||
$adapter->addMustEncryptReason($rule_phid);
|
||||
|
||||
$this->logEffect(self::DO_MUST_ENCRYPT, array($rule_phid));
|
||||
}
|
||||
|
||||
protected function getActionEffectMap() {
|
||||
return array(
|
||||
self::DO_MUST_ENCRYPT => array(
|
||||
'icon' => 'fa-shield',
|
||||
'color' => 'blue',
|
||||
'name' => pht('Must Encrypt'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
protected function renderActionEffectDescription($type, $data) {
|
||||
switch ($type) {
|
||||
case self::DO_MUST_ENCRYPT:
|
||||
return pht(
|
||||
'Made it a requirement that mail content be transmitted only '.
|
||||
'over secure channels.');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,6 +13,10 @@ abstract class PhabricatorMetaMTAEmailHeraldAction
|
||||
}
|
||||
|
||||
public function supportsObject($object) {
|
||||
return self::isMailGeneratingObject($object);
|
||||
}
|
||||
|
||||
public static function isMailGeneratingObject($object) {
|
||||
// NOTE: This implementation lacks generality, but there's no great way to
|
||||
// figure out if something generates email right now.
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ final class PhabricatorMailManagementListOutboundWorkflow
|
||||
$table = id(new PhutilConsoleTable())
|
||||
->setShowHeader(false)
|
||||
->addColumn('id', array('title' => pht('ID')))
|
||||
->addColumn('encrypt', array('title' => pht('#')))
|
||||
->addColumn('status', array('title' => pht('Status')))
|
||||
->addColumn('subject', array('title' => pht('Subject')));
|
||||
|
||||
@@ -45,6 +46,7 @@ final class PhabricatorMailManagementListOutboundWorkflow
|
||||
|
||||
$table->addRow(array(
|
||||
'id' => $mail->getID(),
|
||||
'encrypt' => ($mail->getMustEncrypt() ? '#' : ' '),
|
||||
'status' => PhabricatorMailOutboundStatus::getStatusName($status),
|
||||
'subject' => $mail->getSubject(),
|
||||
));
|
||||
|
||||
@@ -47,6 +47,11 @@ final class PhabricatorMailManagementSendTestWorkflow
|
||||
'help' => pht('Attach a file.'),
|
||||
'repeat' => true,
|
||||
),
|
||||
array(
|
||||
'name' => 'mailer',
|
||||
'param' => 'key',
|
||||
'help' => pht('Send with a specific configured mailer.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'html',
|
||||
'help' => pht('Send as HTML mail.'),
|
||||
@@ -161,6 +166,21 @@ final class PhabricatorMailManagementSendTestWorkflow
|
||||
$mail->setFrom($from->getPHID());
|
||||
}
|
||||
|
||||
$mailer_key = $args->getArg('mailer');
|
||||
if ($mailer_key !== null) {
|
||||
$mailers = PhabricatorMetaMTAMail::newMailers();
|
||||
$mailers = mpull($mailers, null, 'getKey');
|
||||
if (!isset($mailers[$mailer_key])) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Mailer key ("%s") is not configured. Available keys are: %s.',
|
||||
$mailer_key,
|
||||
implode(', ', array_keys($mailers))));
|
||||
}
|
||||
|
||||
$mail->setTryMailers(array($mailer_key));
|
||||
}
|
||||
|
||||
foreach ($attach as $attachment) {
|
||||
$data = Filesystem::readFile($attachment);
|
||||
$name = basename($attachment);
|
||||
|
||||
@@ -79,7 +79,7 @@ final class PhabricatorMailManagementShowOutboundWorkflow
|
||||
|
||||
$info = array();
|
||||
|
||||
$info[] = pht('PROPERTIES');
|
||||
$info[] = $this->newSectionHeader(pht('PROPERTIES'));
|
||||
$info[] = pht('ID: %d', $message->getID());
|
||||
$info[] = pht('Status: %s', $message->getStatus());
|
||||
$info[] = pht('Related PHID: %s', $message->getRelatedPHID());
|
||||
@@ -87,15 +87,17 @@ final class PhabricatorMailManagementShowOutboundWorkflow
|
||||
|
||||
$ignore = array(
|
||||
'body' => true,
|
||||
'body.sent' => true,
|
||||
'html-body' => true,
|
||||
'headers' => true,
|
||||
'attachments' => true,
|
||||
'headers.sent' => true,
|
||||
'headers.unfiltered' => true,
|
||||
'authors.sent' => true,
|
||||
);
|
||||
|
||||
$info[] = null;
|
||||
$info[] = pht('PARAMETERS');
|
||||
$info[] = $this->newSectionHeader(pht('PARAMETERS'));
|
||||
$parameters = $message->getParameters();
|
||||
foreach ($parameters as $key => $value) {
|
||||
if (isset($ignore[$key])) {
|
||||
@@ -110,22 +112,40 @@ final class PhabricatorMailManagementShowOutboundWorkflow
|
||||
}
|
||||
|
||||
$info[] = null;
|
||||
$info[] = pht('HEADERS');
|
||||
$info[] = $this->newSectionHeader(pht('HEADERS'));
|
||||
|
||||
$headers = $message->getDeliveredHeaders();
|
||||
if (!$headers) {
|
||||
$unfiltered = $message->getUnfilteredHeaders();
|
||||
if (!$unfiltered) {
|
||||
$headers = $message->generateHeaders();
|
||||
$unfiltered = $headers;
|
||||
}
|
||||
|
||||
$header_map = array();
|
||||
foreach ($headers as $header) {
|
||||
list($name, $value) = $header;
|
||||
$info[] = "{$name}: {$value}";
|
||||
$header_map[$name.':'.$value] = true;
|
||||
}
|
||||
|
||||
foreach ($unfiltered as $header) {
|
||||
list($name, $value) = $header;
|
||||
$was_sent = isset($header_map[$name.':'.$value]);
|
||||
|
||||
if ($was_sent) {
|
||||
$marker = ' ';
|
||||
} else {
|
||||
$marker = '#';
|
||||
}
|
||||
|
||||
$info[] = "{$marker} {$name}: {$value}";
|
||||
}
|
||||
|
||||
$attachments = idx($parameters, 'attachments');
|
||||
if ($attachments) {
|
||||
$info[] = null;
|
||||
$info[] = pht('ATTACHMENTS');
|
||||
|
||||
$info[] = $this->newSectionHeader(pht('ATTACHMENTS'));
|
||||
|
||||
foreach ($attachments as $attachment) {
|
||||
$info[] = idx($attachment, 'filename', pht('Unnamed File'));
|
||||
}
|
||||
@@ -136,7 +156,9 @@ final class PhabricatorMailManagementShowOutboundWorkflow
|
||||
$actors = $message->getDeliveredActors();
|
||||
if ($actors) {
|
||||
$info[] = null;
|
||||
$info[] = pht('RECIPIENTS');
|
||||
|
||||
$info[] = $this->newSectionHeader(pht('RECIPIENTS'));
|
||||
|
||||
foreach ($actors as $actor_phid => $actor_info) {
|
||||
$actor = idx($all_actors, $actor_phid);
|
||||
if ($actor) {
|
||||
@@ -162,15 +184,22 @@ final class PhabricatorMailManagementShowOutboundWorkflow
|
||||
}
|
||||
|
||||
$info[] = null;
|
||||
$info[] = pht('TEXT BODY');
|
||||
$info[] = $this->newSectionHeader(pht('TEXT BODY'));
|
||||
if (strlen($message->getBody())) {
|
||||
$info[] = $message->getBody();
|
||||
$info[] = tsprintf('%B', $message->getBody());
|
||||
} else {
|
||||
$info[] = pht('(This message has no text body.)');
|
||||
}
|
||||
|
||||
$delivered_body = $message->getDeliveredBody();
|
||||
if ($delivered_body !== null) {
|
||||
$info[] = null;
|
||||
$info[] = pht('HTML BODY');
|
||||
$info[] = $this->newSectionHeader(pht('BODY AS DELIVERED'), true);
|
||||
$info[] = tsprintf('%B', $delivered_body);
|
||||
}
|
||||
|
||||
$info[] = null;
|
||||
$info[] = $this->newSectionHeader(pht('HTML BODY'));
|
||||
if (strlen($message->getHTMLBody())) {
|
||||
$info[] = $message->getHTMLBody();
|
||||
$info[] = null;
|
||||
@@ -186,4 +215,12 @@ final class PhabricatorMailManagementShowOutboundWorkflow
|
||||
}
|
||||
}
|
||||
|
||||
private function newSectionHeader($label, $emphasize = false) {
|
||||
if ($emphasize) {
|
||||
return tsprintf('**<bg:yellow> %s </bg>**', $label);
|
||||
} else {
|
||||
return tsprintf('**<bg:blue> %s </bg>**', $label);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ final class PhabricatorMetaMTAActor extends Phobject {
|
||||
const REASON_ROUTE_AS_NOTIFICATION = 'route-as-notification';
|
||||
const REASON_ROUTE_AS_MAIL = 'route-as-mail';
|
||||
const REASON_UNVERIFIED = 'unverified';
|
||||
const REASON_MUTED = 'muted';
|
||||
|
||||
private $phid;
|
||||
private $emailAddress;
|
||||
@@ -116,6 +117,7 @@ final class PhabricatorMetaMTAActor extends Phobject {
|
||||
self::REASON_ROUTE_AS_NOTIFICATION => pht('Route as Notification'),
|
||||
self::REASON_ROUTE_AS_MAIL => pht('Route as Mail'),
|
||||
self::REASON_UNVERIFIED => pht('Address Not Verified'),
|
||||
self::REASON_MUTED => pht('Muted'),
|
||||
);
|
||||
|
||||
return idx($names, $reason, pht('Unknown ("%s")', $reason));
|
||||
@@ -172,6 +174,8 @@ final class PhabricatorMetaMTAActor extends Phobject {
|
||||
'in Herald.'),
|
||||
self::REASON_UNVERIFIED => pht(
|
||||
'This recipient does not have a verified primary email address.'),
|
||||
self::REASON_MUTED => pht(
|
||||
'This recipient has muted notifications for this object.'),
|
||||
);
|
||||
|
||||
return idx($descriptions, $reason, pht('Unknown Reason ("%s")', $reason));
|
||||
|
||||
@@ -6,6 +6,7 @@ abstract class PhabricatorMailReplyHandler extends Phobject {
|
||||
private $applicationEmail;
|
||||
private $actor;
|
||||
private $excludePHIDs = array();
|
||||
private $unexpandablePHIDs = array();
|
||||
|
||||
final public function setMailReceiver($mail_receiver) {
|
||||
$this->validateMailReceiver($mail_receiver);
|
||||
@@ -45,6 +46,15 @@ abstract class PhabricatorMailReplyHandler extends Phobject {
|
||||
return $this->excludePHIDs;
|
||||
}
|
||||
|
||||
public function setUnexpandablePHIDs(array $phids) {
|
||||
$this->unexpandablePHIDs = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUnexpandablePHIDs() {
|
||||
return $this->unexpandablePHIDs;
|
||||
}
|
||||
|
||||
abstract public function validateMailReceiver($mail_receiver);
|
||||
abstract public function getPrivateReplyHandlerEmailAddress(
|
||||
PhabricatorUser $user);
|
||||
@@ -297,6 +307,16 @@ abstract class PhabricatorMailReplyHandler extends Phobject {
|
||||
$to_result = array();
|
||||
$cc_result = array();
|
||||
|
||||
// "Unexpandable" users have disengaged from an object (for example,
|
||||
// by resigning from a revision).
|
||||
|
||||
// If such a user is still a direct recipient (for example, they're still
|
||||
// on the Subscribers list) they're fair game, but group targets (like
|
||||
// projects) will no longer include them when expanded.
|
||||
|
||||
$unexpandable = $this->getUnexpandablePHIDs();
|
||||
$unexpandable = array_fuse($unexpandable);
|
||||
|
||||
$all_phids = array_merge($to, $cc);
|
||||
if ($all_phids) {
|
||||
$map = id(new PhabricatorMetaMTAMemberQuery())
|
||||
@@ -305,11 +325,21 @@ abstract class PhabricatorMailReplyHandler extends Phobject {
|
||||
->execute();
|
||||
foreach ($to as $phid) {
|
||||
foreach ($map[$phid] as $expanded) {
|
||||
if ($expanded !== $phid) {
|
||||
if (isset($unexpandable[$expanded])) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$to_result[$expanded] = $expanded;
|
||||
}
|
||||
}
|
||||
foreach ($cc as $phid) {
|
||||
foreach ($map[$phid] as $expanded) {
|
||||
if ($expanded !== $phid) {
|
||||
if (isset($unexpandable[$expanded])) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$cc_result[$expanded] = $expanded;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,23 +58,55 @@ final class PhabricatorMailTarget extends Phobject {
|
||||
public function willSendMail(PhabricatorMetaMTAMail $mail) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$show_stamps = $mail->shouldRenderMailStampsInBody($viewer);
|
||||
|
||||
$body = $mail->getBody();
|
||||
$html_body = $mail->getHTMLBody();
|
||||
$has_html = (strlen($html_body) > 0);
|
||||
|
||||
if ($show_stamps) {
|
||||
$stamps = $mail->getMailStamps();
|
||||
if ($stamps) {
|
||||
$body .= "\n";
|
||||
$body .= pht('STAMPS');
|
||||
$body .= "\n";
|
||||
$body .= implode(' ', $stamps);
|
||||
$body .= "\n";
|
||||
|
||||
if ($has_html) {
|
||||
$html = array();
|
||||
$html[] = phutil_tag('strong', array(), pht('STAMPS'));
|
||||
$html[] = phutil_tag('br');
|
||||
$html[] = phutil_tag(
|
||||
'span',
|
||||
array(
|
||||
'style' => 'font-size: smaller; color: #92969D',
|
||||
),
|
||||
phutil_implode_html(' ', $stamps));
|
||||
$html[] = phutil_tag('br');
|
||||
$html[] = phutil_tag('br');
|
||||
$html = phutil_tag('div', array(), $html);
|
||||
$html_body .= hsprintf('%s', $html);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$mail->addPHIDHeaders('X-Phabricator-To', $this->rawToPHIDs);
|
||||
$mail->addPHIDHeaders('X-Phabricator-Cc', $this->rawCCPHIDs);
|
||||
|
||||
$to_handles = $viewer->loadHandles($this->rawToPHIDs);
|
||||
$cc_handles = $viewer->loadHandles($this->rawCCPHIDs);
|
||||
|
||||
$body = $mail->getBody();
|
||||
$body .= "\n";
|
||||
$body .= $this->getRecipientsSummary($to_handles, $cc_handles);
|
||||
$mail->setBody($body);
|
||||
|
||||
$html_body = $mail->getHTMLBody();
|
||||
if (strlen($html_body)) {
|
||||
if ($has_html) {
|
||||
$html_body .= hsprintf(
|
||||
'%s',
|
||||
$this->getRecipientsSummaryHTML($to_handles, $cc_handles));
|
||||
}
|
||||
|
||||
$mail->setBody($body);
|
||||
$mail->setHTMLBody($html_body);
|
||||
|
||||
$reply_to = $this->getReplyTo();
|
||||
|
||||
16
src/applications/metamta/stamp/PhabricatorBoolMailStamp.php
Normal file
16
src/applications/metamta/stamp/PhabricatorBoolMailStamp.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorBoolMailStamp
|
||||
extends PhabricatorMailStamp {
|
||||
|
||||
const STAMPTYPE = 'bool';
|
||||
|
||||
public function renderStamps($value) {
|
||||
if (!$value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->renderStamp($this->getKey());
|
||||
}
|
||||
|
||||
}
|
||||
88
src/applications/metamta/stamp/PhabricatorMailStamp.php
Normal file
88
src/applications/metamta/stamp/PhabricatorMailStamp.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
abstract class PhabricatorMailStamp
|
||||
extends Phobject {
|
||||
|
||||
private $key;
|
||||
private $value;
|
||||
private $label;
|
||||
private $viewer;
|
||||
|
||||
final public function getStampType() {
|
||||
return $this->getPhobjectClassConstant('STAMPTYPE');
|
||||
}
|
||||
|
||||
final public function setKey($key) {
|
||||
$this->key = $key;
|
||||
return $this;
|
||||
}
|
||||
|
||||
final public function getKey() {
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
final protected function setRawValue($value) {
|
||||
$this->value = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
final protected function getRawValue() {
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
final public function setViewer(PhabricatorUser $viewer) {
|
||||
$this->viewer = $viewer;
|
||||
return $this;
|
||||
}
|
||||
|
||||
final public function getViewer() {
|
||||
return $this->viewer;
|
||||
}
|
||||
|
||||
final public function setLabel($label) {
|
||||
$this->label = $label;
|
||||
return $this;
|
||||
}
|
||||
|
||||
final public function getLabel() {
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
public function setValue($value) {
|
||||
return $this->setRawValue($value);
|
||||
}
|
||||
|
||||
final public function toDictionary() {
|
||||
return array(
|
||||
'type' => $this->getStampType(),
|
||||
'key' => $this->getKey(),
|
||||
'value' => $this->getValueForDictionary(),
|
||||
);
|
||||
}
|
||||
|
||||
final public static function getAllStamps() {
|
||||
return id(new PhutilClassMapQuery())
|
||||
->setAncestorClass(__CLASS__)
|
||||
->setUniqueMethod('getStampType')
|
||||
->execute();
|
||||
}
|
||||
|
||||
protected function getValueForDictionary() {
|
||||
return $this->getRawValue();
|
||||
}
|
||||
|
||||
public function setValueFromDictionary($value) {
|
||||
return $this->setRawValue($value);
|
||||
}
|
||||
|
||||
public function getValueForRendering() {
|
||||
return $this->getRawValue();
|
||||
}
|
||||
|
||||
abstract public function renderStamps($value);
|
||||
|
||||
final protected function renderStamp($key, $value = null) {
|
||||
return $key.'('.$value.')';
|
||||
}
|
||||
|
||||
}
|
||||
36
src/applications/metamta/stamp/PhabricatorPHIDMailStamp.php
Normal file
36
src/applications/metamta/stamp/PhabricatorPHIDMailStamp.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorPHIDMailStamp
|
||||
extends PhabricatorMailStamp {
|
||||
|
||||
const STAMPTYPE = 'phid';
|
||||
|
||||
public function renderStamps($value) {
|
||||
if ($value === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$value = (array)$value;
|
||||
if (!$value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$viewer = $this->getViewer();
|
||||
$handles = $viewer->loadHandles($value);
|
||||
|
||||
$results = array();
|
||||
foreach ($value as $phid) {
|
||||
$handle = $handles[$phid];
|
||||
|
||||
$mail_name = $handle->getMailStampName();
|
||||
if ($mail_name === null) {
|
||||
$mail_name = $handle->getPHID();
|
||||
}
|
||||
|
||||
$results[] = $this->renderStamp($this->getKey(), $mail_name);
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorStringMailStamp
|
||||
extends PhabricatorMailStamp {
|
||||
|
||||
const STAMPTYPE = 'string';
|
||||
|
||||
public function renderStamps($value) {
|
||||
if ($value === null || $value === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$value = (array)$value;
|
||||
if (!$value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$results = array();
|
||||
foreach ($value as $v) {
|
||||
$results[] = $this->renderStamp($this->getKey(), $v);
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorMetaMTAAttachment extends Phobject {
|
||||
protected $data;
|
||||
protected $filename;
|
||||
protected $mimetype;
|
||||
|
||||
private $data;
|
||||
private $filename;
|
||||
private $mimetype;
|
||||
private $file;
|
||||
private $filePHID;
|
||||
|
||||
public function __construct($data, $filename, $mimetype) {
|
||||
$this->setData($data);
|
||||
@@ -39,18 +42,49 @@ final class PhabricatorMetaMTAAttachment extends Phobject {
|
||||
}
|
||||
|
||||
public function toDictionary() {
|
||||
if (!$this->file) {
|
||||
$iterator = new ArrayIterator(array($this->getData()));
|
||||
|
||||
$source = id(new PhabricatorIteratorFileUploadSource())
|
||||
->setName($this->getFilename())
|
||||
->setViewPolicy(PhabricatorPolicies::POLICY_NOONE)
|
||||
->setMIMEType($this->getMimeType())
|
||||
->setIterator($iterator);
|
||||
|
||||
$this->file = $source->uploadFile();
|
||||
}
|
||||
|
||||
return array(
|
||||
'filename' => $this->getFilename(),
|
||||
'mimetype' => $this->getMimeType(),
|
||||
'data' => $this->getData(),
|
||||
'filePHID' => $this->file->getPHID(),
|
||||
);
|
||||
}
|
||||
|
||||
public static function newFromDictionary(array $dict) {
|
||||
return new PhabricatorMetaMTAAttachment(
|
||||
$file = null;
|
||||
|
||||
$file_phid = idx($dict, 'filePHID');
|
||||
if ($file_phid) {
|
||||
$file = id(new PhabricatorFileQuery())
|
||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||
->withPHIDs(array($file_phid))
|
||||
->executeOne();
|
||||
if ($file) {
|
||||
$dict['data'] = $file->loadFileData();
|
||||
}
|
||||
}
|
||||
|
||||
$attachment = new self(
|
||||
idx($dict, 'data'),
|
||||
idx($dict, 'filename'),
|
||||
idx($dict, 'mimetype'));
|
||||
|
||||
if ($file) {
|
||||
$attachment->file = $file;
|
||||
}
|
||||
|
||||
return $attachment;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
*/
|
||||
final class PhabricatorMetaMTAMail
|
||||
extends PhabricatorMetaMTADAO
|
||||
implements PhabricatorPolicyInterface {
|
||||
implements
|
||||
PhabricatorPolicyInterface,
|
||||
PhabricatorDestructibleInterface {
|
||||
|
||||
const RETRY_DELAY = 5;
|
||||
|
||||
@@ -21,7 +23,10 @@ final class PhabricatorMetaMTAMail
|
||||
public function __construct() {
|
||||
|
||||
$this->status = PhabricatorMailOutboundStatus::STATUS_QUEUE;
|
||||
$this->parameters = array('sensitive' => true);
|
||||
$this->parameters = array(
|
||||
'sensitive' => true,
|
||||
'mustEncrypt' => false,
|
||||
);
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
@@ -155,6 +160,15 @@ final class PhabricatorMetaMTAMail
|
||||
return $this->getParam('exclude', array());
|
||||
}
|
||||
|
||||
public function setMutedPHIDs(array $muted) {
|
||||
$this->setParam('muted', $muted);
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function getMutedPHIDs() {
|
||||
return $this->getParam('muted', array());
|
||||
}
|
||||
|
||||
public function setForceHeraldMailRecipientPHIDs(array $force) {
|
||||
$this->setParam('herald-force-recipients', $force);
|
||||
return $this;
|
||||
@@ -192,6 +206,35 @@ final class PhabricatorMetaMTAMail
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getAttachmentFilePHIDs() {
|
||||
$file_phids = array();
|
||||
|
||||
$dictionaries = $this->getParam('attachments');
|
||||
if ($dictionaries) {
|
||||
foreach ($dictionaries as $dictionary) {
|
||||
$file_phid = idx($dictionary, 'filePHID');
|
||||
if ($file_phid) {
|
||||
$file_phids[] = $file_phid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $file_phids;
|
||||
}
|
||||
|
||||
public function loadAttachedFiles(PhabricatorUser $viewer) {
|
||||
$file_phids = $this->getAttachmentFilePHIDs();
|
||||
|
||||
if (!$file_phids) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return id(new PhabricatorFileQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs($file_phids)
|
||||
->execute();
|
||||
}
|
||||
|
||||
public function setAttachments(array $attachments) {
|
||||
assert_instances_of($attachments, 'PhabricatorMetaMTAAttachment');
|
||||
$this->setParam('attachments', mpull($attachments, 'toDictionary'));
|
||||
@@ -247,6 +290,48 @@ final class PhabricatorMetaMTAMail
|
||||
return $this->getParam('sensitive', true);
|
||||
}
|
||||
|
||||
public function setMustEncrypt($bool) {
|
||||
$this->setParam('mustEncrypt', $bool);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMustEncrypt() {
|
||||
return $this->getParam('mustEncrypt', false);
|
||||
}
|
||||
|
||||
public function setMustEncryptReasons(array $reasons) {
|
||||
$this->setParam('mustEncryptReasons', $reasons);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMustEncryptReasons() {
|
||||
return $this->getParam('mustEncryptReasons', array());
|
||||
}
|
||||
|
||||
public function setMailStamps(array $stamps) {
|
||||
return $this->setParam('stamps', $stamps);
|
||||
}
|
||||
|
||||
public function getMailStamps() {
|
||||
return $this->getParam('stamps', array());
|
||||
}
|
||||
|
||||
public function setMailStampMetadata($metadata) {
|
||||
return $this->setParam('stampMetadata', $metadata);
|
||||
}
|
||||
|
||||
public function getMailStampMetadata() {
|
||||
return $this->getParam('stampMetadata', array());
|
||||
}
|
||||
|
||||
public function getMailerKey() {
|
||||
return $this->getParam('mailer.key');
|
||||
}
|
||||
|
||||
public function setTryMailers(array $mailers) {
|
||||
return $this->setParam('mailers.try', $mailers);
|
||||
}
|
||||
|
||||
public function setHTMLBody($html) {
|
||||
$this->setParam('html-body', $html);
|
||||
return $this;
|
||||
@@ -385,36 +470,196 @@ final class PhabricatorMetaMTAMail
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function buildDefaultMailer() {
|
||||
return PhabricatorEnv::newObjectFromConfig('metamta.mail-adapter');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Attempt to deliver an email immediately, in this process.
|
||||
*
|
||||
* @param bool Try to deliver this email even if it has already been
|
||||
* delivered or is in backoff after a failed delivery attempt.
|
||||
* @param PhabricatorMailImplementationAdapter Use a specific mail adapter,
|
||||
* instead of the default.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function sendNow(
|
||||
$force_send = false,
|
||||
PhabricatorMailImplementationAdapter $mailer = null) {
|
||||
|
||||
if ($mailer === null) {
|
||||
$mailer = $this->buildDefaultMailer();
|
||||
}
|
||||
|
||||
if (!$force_send) {
|
||||
public function sendNow() {
|
||||
if ($this->getStatus() != PhabricatorMailOutboundStatus::STATUS_QUEUE) {
|
||||
throw new Exception(pht('Trying to send an already-sent mail!'));
|
||||
}
|
||||
|
||||
$mailers = self::newMailers();
|
||||
|
||||
$try_mailers = $this->getParam('mailers.try');
|
||||
if ($try_mailers) {
|
||||
$mailers = mpull($mailers, null, 'getKey');
|
||||
$mailers = array_select_keys($mailers, $try_mailers);
|
||||
}
|
||||
|
||||
return $this->sendWithMailers($mailers);
|
||||
}
|
||||
|
||||
public static function newMailersWithTypes(array $types) {
|
||||
$mailers = self::newMailers();
|
||||
$types = array_fuse($types);
|
||||
|
||||
foreach ($mailers as $key => $mailer) {
|
||||
$mailer_type = $mailer->getAdapterType();
|
||||
if (!isset($types[$mailer_type])) {
|
||||
unset($mailers[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
return array_values($mailers);
|
||||
}
|
||||
|
||||
public static function newMailers() {
|
||||
$mailers = array();
|
||||
|
||||
$config = PhabricatorEnv::getEnvConfig('cluster.mailers');
|
||||
if ($config === null) {
|
||||
$mailer = PhabricatorEnv::newObjectFromConfig('metamta.mail-adapter');
|
||||
|
||||
$defaults = $mailer->newDefaultOptions();
|
||||
$options = $mailer->newLegacyOptions();
|
||||
|
||||
$options = $options + $defaults;
|
||||
|
||||
$mailer
|
||||
->setKey('default')
|
||||
->setPriority(-1)
|
||||
->setOptions($options);
|
||||
|
||||
$mailers[] = $mailer;
|
||||
} else {
|
||||
$adapters = PhabricatorMailImplementationAdapter::getAllAdapters();
|
||||
$next_priority = -1;
|
||||
|
||||
foreach ($config as $spec) {
|
||||
$type = $spec['type'];
|
||||
if (!isset($adapters[$type])) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Unknown mailer ("%s")!',
|
||||
$type));
|
||||
}
|
||||
|
||||
$key = $spec['key'];
|
||||
$mailer = id(clone $adapters[$type])
|
||||
->setKey($key);
|
||||
|
||||
$priority = idx($spec, 'priority');
|
||||
if (!$priority) {
|
||||
$priority = $next_priority;
|
||||
$next_priority--;
|
||||
}
|
||||
$mailer->setPriority($priority);
|
||||
|
||||
$defaults = $mailer->newDefaultOptions();
|
||||
$options = idx($spec, 'options', array()) + $defaults;
|
||||
$mailer->setOptions($options);
|
||||
|
||||
$mailers[] = $mailer;
|
||||
}
|
||||
}
|
||||
|
||||
$sorted = array();
|
||||
$groups = mgroup($mailers, 'getPriority');
|
||||
krsort($groups);
|
||||
foreach ($groups as $group) {
|
||||
// Reorder services within the same priority group randomly.
|
||||
shuffle($group);
|
||||
foreach ($group as $mailer) {
|
||||
$sorted[] = $mailer;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($sorted as $mailer) {
|
||||
$mailer->prepareForSend();
|
||||
}
|
||||
|
||||
return $sorted;
|
||||
}
|
||||
|
||||
public function sendWithMailers(array $mailers) {
|
||||
if (!$mailers) {
|
||||
return $this
|
||||
->setStatus(PhabricatorMailOutboundStatus::STATUS_VOID)
|
||||
->setMessage(pht('No mailers are configured.'))
|
||||
->save();
|
||||
}
|
||||
|
||||
$exceptions = array();
|
||||
foreach ($mailers as $template_mailer) {
|
||||
$mailer = null;
|
||||
|
||||
try {
|
||||
$mailer = $this->buildMailer($template_mailer);
|
||||
} catch (Exception $ex) {
|
||||
$exceptions[] = $ex;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$mailer) {
|
||||
// If we don't get a mailer back, that means the mail doesn't
|
||||
// actually need to be sent (for example, because recipients have
|
||||
// declined to receive the mail). Void it and return.
|
||||
return $this
|
||||
->setStatus(PhabricatorMailOutboundStatus::STATUS_VOID)
|
||||
->save();
|
||||
}
|
||||
|
||||
try {
|
||||
$ok = $mailer->send();
|
||||
if (!$ok) {
|
||||
// TODO: At some point, we should clean this up and make all mailers
|
||||
// throw.
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Mail adapter encountered an unexpected, unspecified '.
|
||||
'failure.'));
|
||||
}
|
||||
} catch (PhabricatorMetaMTAPermanentFailureException $ex) {
|
||||
// If any mailer raises a permanent failure, stop trying to send the
|
||||
// mail with other mailers.
|
||||
$this
|
||||
->setStatus(PhabricatorMailOutboundStatus::STATUS_FAIL)
|
||||
->setMessage($ex->getMessage())
|
||||
->save();
|
||||
|
||||
throw $ex;
|
||||
} catch (Exception $ex) {
|
||||
$exceptions[] = $ex;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Keep track of which mailer actually ended up accepting the message.
|
||||
$mailer_key = $mailer->getKey();
|
||||
if ($mailer_key !== null) {
|
||||
$this->setParam('mailer.key', $mailer_key);
|
||||
}
|
||||
|
||||
return $this
|
||||
->setStatus(PhabricatorMailOutboundStatus::STATUS_SENT)
|
||||
->save();
|
||||
}
|
||||
|
||||
// If we make it here, no mailer could send the mail but no mailer failed
|
||||
// permanently either. We update the error message for the mail, but leave
|
||||
// it in the current status (usually, STATUS_QUEUE) and try again later.
|
||||
|
||||
$messages = array();
|
||||
foreach ($exceptions as $ex) {
|
||||
$messages[] = $ex->getMessage();
|
||||
}
|
||||
$messages = implode("\n\n", $messages);
|
||||
|
||||
$this
|
||||
->setMessage($messages)
|
||||
->save();
|
||||
|
||||
if (count($exceptions) === 1) {
|
||||
throw head($exceptions);
|
||||
}
|
||||
|
||||
throw new PhutilAggregateException(
|
||||
pht('Encountered multiple exceptions while transmitting mail.'),
|
||||
$exceptions);
|
||||
}
|
||||
|
||||
private function buildMailer(PhabricatorMailImplementationAdapter $mailer) {
|
||||
$headers = $this->generateHeaders();
|
||||
|
||||
$params = $this->parameters;
|
||||
@@ -431,6 +676,7 @@ final class PhabricatorMetaMTAMail
|
||||
unset($params['is-first-message']);
|
||||
|
||||
$is_threaded = (bool)idx($params, 'thread-id');
|
||||
$must_encrypt = $this->getMustEncrypt();
|
||||
|
||||
$reply_to_name = idx($params, 'reply-to-name', '');
|
||||
unset($params['reply-to-name']);
|
||||
@@ -438,8 +684,8 @@ final class PhabricatorMetaMTAMail
|
||||
$add_cc = array();
|
||||
$add_to = array();
|
||||
|
||||
// If multiplexing is enabled, some recipients will be in "Cc"
|
||||
// rather than "To". We'll move them to "To" later (or supply a
|
||||
// If we're sending one mail to everyone, some recipients will be in
|
||||
// "Cc" rather than "To". We'll move them to "To" later (or supply a
|
||||
// dummy "To") but need to look for the recipient in either the
|
||||
// "To" or "Cc" fields here.
|
||||
$target_phid = head(idx($params, 'to', array()));
|
||||
@@ -456,6 +702,12 @@ final class PhabricatorMetaMTAMail
|
||||
$mailer->setFrom($from_email, $from_name);
|
||||
break;
|
||||
case 'from':
|
||||
// If the mail content must be encrypted, disguise the sender.
|
||||
if ($must_encrypt) {
|
||||
$mailer->setFrom($default_from, pht('Phabricator'));
|
||||
break;
|
||||
}
|
||||
|
||||
$from = $value;
|
||||
$actor_email = null;
|
||||
$actor_name = null;
|
||||
@@ -502,6 +754,17 @@ final class PhabricatorMetaMTAMail
|
||||
mpull($cc_actors, 'getEmailAddress'));
|
||||
break;
|
||||
case 'attachments':
|
||||
$attached_viewer = PhabricatorUser::getOmnipotentUser();
|
||||
$files = $this->loadAttachedFiles($attached_viewer);
|
||||
foreach ($files as $file) {
|
||||
$file->attachToObject($this->getPHID());
|
||||
}
|
||||
|
||||
// If the mail content must be encrypted, don't add attachments.
|
||||
if ($must_encrypt) {
|
||||
break;
|
||||
}
|
||||
|
||||
$value = $this->getAttachments();
|
||||
foreach ($value as $attachment) {
|
||||
$mailer->addAttachment(
|
||||
@@ -521,6 +784,11 @@ final class PhabricatorMetaMTAMail
|
||||
|
||||
$subject[] = trim(idx($params, 'subject-prefix'));
|
||||
|
||||
// If mail content must be encrypted, we replace the subject with
|
||||
// a generic one.
|
||||
if ($must_encrypt) {
|
||||
$subject[] = pht('Object Updated');
|
||||
} else {
|
||||
$vary_prefix = idx($params, 'vary-subject-prefix');
|
||||
if ($vary_prefix != '') {
|
||||
if ($this->shouldVarySubject($preferences)) {
|
||||
@@ -529,6 +797,7 @@ final class PhabricatorMetaMTAMail
|
||||
}
|
||||
|
||||
$subject[] = $value;
|
||||
}
|
||||
|
||||
$mailer->setSubject(implode(' ', array_filter($subject)));
|
||||
break;
|
||||
@@ -567,7 +836,27 @@ final class PhabricatorMetaMTAMail
|
||||
}
|
||||
}
|
||||
|
||||
$body = idx($params, 'body', '');
|
||||
$stamps = $this->getMailStamps();
|
||||
if ($stamps) {
|
||||
$headers[] = array('X-Phabricator-Stamps', implode(' ', $stamps));
|
||||
}
|
||||
|
||||
$raw_body = idx($params, 'body', '');
|
||||
$body = $raw_body;
|
||||
if ($must_encrypt) {
|
||||
$parts = array();
|
||||
$parts[] = pht(
|
||||
'The content for this message can only be transmitted over a '.
|
||||
'secure channel. To view the message content, follow this '.
|
||||
'link:');
|
||||
|
||||
$parts[] = PhabricatorEnv::getProductionURI($this->getURI());
|
||||
|
||||
$body = implode("\n\n", $parts);
|
||||
} else {
|
||||
$body = $raw_body;
|
||||
}
|
||||
|
||||
$max = PhabricatorEnv::getEnvConfig('metamta.email-body-limit');
|
||||
if (strlen($body) > $max) {
|
||||
$body = id(new PhutilUTF8StringTruncator())
|
||||
@@ -578,18 +867,32 @@ final class PhabricatorMetaMTAMail
|
||||
}
|
||||
$mailer->setBody($body);
|
||||
|
||||
$html_emails = $this->shouldSendHTML($preferences);
|
||||
if ($html_emails && isset($params['html-body'])) {
|
||||
// If we sent a different message body than we were asked to, record
|
||||
// what we actually sent to make debugging and diagnostics easier.
|
||||
if ($body !== $raw_body) {
|
||||
$this->setParam('body.sent', $body);
|
||||
}
|
||||
|
||||
if ($must_encrypt) {
|
||||
$send_html = false;
|
||||
} else {
|
||||
$send_html = $this->shouldSendHTML($preferences);
|
||||
}
|
||||
|
||||
if ($send_html && isset($params['html-body'])) {
|
||||
$mailer->setHTMLBody($params['html-body']);
|
||||
}
|
||||
|
||||
// Pass the headers to the mailer, then save the state so we can show
|
||||
// them in the web UI.
|
||||
foreach ($headers as $header) {
|
||||
// them in the web UI. If the mail must be encrypted, we remove headers
|
||||
// which are not on a strict whitelist to avoid disclosing information.
|
||||
$filtered_headers = $this->filterHeaders($headers, $must_encrypt);
|
||||
foreach ($filtered_headers as $header) {
|
||||
list($header_key, $header_value) = $header;
|
||||
$mailer->addHeader($header_key, $header_value);
|
||||
}
|
||||
$this->setParam('headers.sent', $headers);
|
||||
$this->setParam('headers.unfiltered', $headers);
|
||||
$this->setParam('headers.sent', $filtered_headers);
|
||||
|
||||
// Save the final deliverability outcomes and reasoning so we can
|
||||
// explain why things happened the way they did.
|
||||
@@ -606,35 +909,35 @@ final class PhabricatorMetaMTAMail
|
||||
$this->setParam('routingmap.sent', $this->getRoutingRuleMap());
|
||||
|
||||
if (!$add_to && !$add_cc) {
|
||||
$this->setStatus(PhabricatorMailOutboundStatus::STATUS_VOID);
|
||||
$this->setMessage(
|
||||
pht(
|
||||
'Message has no valid recipients: all To/Cc are disabled, '.
|
||||
'invalid, or configured not to receive this mail.'));
|
||||
return $this->save();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->getIsErrorEmail()) {
|
||||
$all_recipients = array_merge($add_to, $add_cc);
|
||||
if ($this->shouldRateLimitMail($all_recipients)) {
|
||||
$this->setStatus(PhabricatorMailOutboundStatus::STATUS_VOID);
|
||||
$this->setMessage(
|
||||
pht(
|
||||
'This is an error email, but one or more recipients have '.
|
||||
'exceeded the error email rate limit. Declining to deliver '.
|
||||
'message.'));
|
||||
return $this->save();
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (PhabricatorEnv::getEnvConfig('phabricator.silent')) {
|
||||
$this->setStatus(PhabricatorMailOutboundStatus::STATUS_VOID);
|
||||
$this->setMessage(
|
||||
pht(
|
||||
'Phabricator is running in silent mode. See `%s` '.
|
||||
'in the configuration to change this setting.',
|
||||
'phabricator.silent'));
|
||||
return $this->save();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Some mailers require a valid "To:" in order to deliver mail. If we
|
||||
@@ -658,42 +961,8 @@ final class PhabricatorMetaMTAMail
|
||||
if ($add_cc) {
|
||||
$mailer->addCCs($add_cc);
|
||||
}
|
||||
} catch (Exception $ex) {
|
||||
$this
|
||||
->setStatus(PhabricatorMailOutboundStatus::STATUS_FAIL)
|
||||
->setMessage($ex->getMessage())
|
||||
->save();
|
||||
|
||||
throw $ex;
|
||||
}
|
||||
|
||||
try {
|
||||
$ok = $mailer->send();
|
||||
if (!$ok) {
|
||||
// TODO: At some point, we should clean this up and make all mailers
|
||||
// throw.
|
||||
throw new Exception(
|
||||
pht('Mail adapter encountered an unexpected, unspecified failure.'));
|
||||
}
|
||||
|
||||
$this->setStatus(PhabricatorMailOutboundStatus::STATUS_SENT);
|
||||
$this->save();
|
||||
|
||||
return $this;
|
||||
} catch (PhabricatorMetaMTAPermanentFailureException $ex) {
|
||||
$this
|
||||
->setStatus(PhabricatorMailOutboundStatus::STATUS_FAIL)
|
||||
->setMessage($ex->getMessage())
|
||||
->save();
|
||||
|
||||
throw $ex;
|
||||
} catch (Exception $ex) {
|
||||
$this
|
||||
->setMessage($ex->getMessage()."\n".$ex->getTraceAsString())
|
||||
->save();
|
||||
|
||||
throw $ex;
|
||||
}
|
||||
return $mailer;
|
||||
}
|
||||
|
||||
private function generateThreadIndex($seed, $is_first_mail) {
|
||||
@@ -727,7 +996,7 @@ final class PhabricatorMetaMTAMail
|
||||
return base64_encode($base);
|
||||
}
|
||||
|
||||
public static function shouldMultiplexAllMail() {
|
||||
public static function shouldMailEachRecipient() {
|
||||
return PhabricatorEnv::getEnvConfig('metamta.one-mail-per-recipient');
|
||||
}
|
||||
|
||||
@@ -853,6 +1122,18 @@ final class PhabricatorMetaMTAMail
|
||||
}
|
||||
}
|
||||
|
||||
// Exclude muted recipients. We're doing this after saving deliverability
|
||||
// so that Herald "Send me an email" actions can still punch through a
|
||||
// mute.
|
||||
|
||||
foreach ($this->getMutedPHIDs() as $muted_phid) {
|
||||
$muted_actor = idx($actors, $muted_phid);
|
||||
if (!$muted_actor) {
|
||||
continue;
|
||||
}
|
||||
$muted_actor->setUndeliverable(PhabricatorMetaMTAActor::REASON_MUTED);
|
||||
}
|
||||
|
||||
// For the rest of the rules, order matters. We're going to run all the
|
||||
// possible rules in order from weakest to strongest, and let the strongest
|
||||
// matching rule win. The weaker rules leave annotations behind which help
|
||||
@@ -979,20 +1260,6 @@ final class PhabricatorMetaMTAMail
|
||||
}
|
||||
}
|
||||
|
||||
public function delete() {
|
||||
$this->openTransaction();
|
||||
queryfx(
|
||||
$this->establishConnection('w'),
|
||||
'DELETE FROM %T WHERE src = %s AND type = %d',
|
||||
PhabricatorEdgeConfig::TABLE_NAME_EDGE,
|
||||
$this->getPHID(),
|
||||
PhabricatorMetaMTAMailHasRecipientEdgeType::EDGECONST);
|
||||
$ret = parent::delete();
|
||||
$this->saveTransaction();
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function generateHeaders() {
|
||||
$headers = array();
|
||||
|
||||
@@ -1002,8 +1269,6 @@ final class PhabricatorMetaMTAMail
|
||||
// Some clients respect this to suppress OOF and other auto-responses.
|
||||
$headers[] = array('X-Auto-Response-Suppress', 'All');
|
||||
|
||||
// If the message has mailtags, filter out any recipients who don't want
|
||||
// to receive this type of mail.
|
||||
$mailtags = $this->getParam('mailtags');
|
||||
if ($mailtags) {
|
||||
$tag_header = array();
|
||||
@@ -1028,6 +1293,15 @@ final class PhabricatorMetaMTAMail
|
||||
$headers[] = array('Precedence', 'bulk');
|
||||
}
|
||||
|
||||
if ($this->getMustEncrypt()) {
|
||||
$headers[] = array('X-Phabricator-Must-Encrypt', 'Yes');
|
||||
}
|
||||
|
||||
$related_phid = $this->getRelatedPHID();
|
||||
if ($related_phid) {
|
||||
$headers[] = array('Thread-Topic', $related_phid);
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
|
||||
@@ -1035,6 +1309,19 @@ final class PhabricatorMetaMTAMail
|
||||
return $this->getParam('headers.sent');
|
||||
}
|
||||
|
||||
public function getUnfilteredHeaders() {
|
||||
$unfiltered = $this->getParam('headers.unfiltered');
|
||||
|
||||
if ($unfiltered === null) {
|
||||
// Older versions of Phabricator did not filter headers, and thus did
|
||||
// not record unfiltered headers. If we don't have unfiltered header
|
||||
// data just return the delivered headers for compatibility.
|
||||
return $this->getDeliveredHeaders();
|
||||
}
|
||||
|
||||
return $unfiltered;
|
||||
}
|
||||
|
||||
public function getDeliveredActors() {
|
||||
return $this->getParam('actors.sent');
|
||||
}
|
||||
@@ -1047,6 +1334,55 @@ final class PhabricatorMetaMTAMail
|
||||
return $this->getParam('routingmap.sent');
|
||||
}
|
||||
|
||||
public function getDeliveredBody() {
|
||||
return $this->getParam('body.sent');
|
||||
}
|
||||
|
||||
private function filterHeaders(array $headers, $must_encrypt) {
|
||||
if (!$must_encrypt) {
|
||||
return $headers;
|
||||
}
|
||||
|
||||
$whitelist = array(
|
||||
'In-Reply-To',
|
||||
'Message-ID',
|
||||
'Precedence',
|
||||
'References',
|
||||
'Thread-Index',
|
||||
'Thread-Topic',
|
||||
|
||||
'X-Mail-Transport-Agent',
|
||||
'X-Auto-Response-Suppress',
|
||||
|
||||
'X-Phabricator-Sent-This-Message',
|
||||
'X-Phabricator-Must-Encrypt',
|
||||
);
|
||||
|
||||
// NOTE: The major header we want to drop is "X-Phabricator-Mail-Tags".
|
||||
// This header contains a significant amount of meaningful information
|
||||
// about the object.
|
||||
|
||||
$whitelist_map = array();
|
||||
foreach ($whitelist as $term) {
|
||||
$whitelist_map[phutil_utf8_strtolower($term)] = true;
|
||||
}
|
||||
|
||||
foreach ($headers as $key => $header) {
|
||||
list($name, $value) = $header;
|
||||
$name = phutil_utf8_strtolower($name);
|
||||
|
||||
if (!isset($whitelist_map[$name])) {
|
||||
unset($headers[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
|
||||
public function getURI() {
|
||||
return '/mail/detail/'.$this->getID().'/';
|
||||
}
|
||||
|
||||
|
||||
/* -( Routing )------------------------------------------------------------ */
|
||||
|
||||
@@ -1121,7 +1457,7 @@ final class PhabricatorMetaMTAMail
|
||||
private function loadPreferences($target_phid) {
|
||||
$viewer = PhabricatorUser::getOmnipotentUser();
|
||||
|
||||
if (self::shouldMultiplexAllMail()) {
|
||||
if (self::shouldMailEachRecipient()) {
|
||||
$preferences = id(new PhabricatorUserPreferencesQuery())
|
||||
->setViewer($viewer)
|
||||
->withUserPHIDs(array($target_phid))
|
||||
@@ -1156,6 +1492,14 @@ final class PhabricatorMetaMTAMail
|
||||
return ($value == PhabricatorEmailFormatSetting::VALUE_HTML_EMAIL);
|
||||
}
|
||||
|
||||
public function shouldRenderMailStampsInBody($viewer) {
|
||||
$preferences = $this->loadPreferences($viewer->getPHID());
|
||||
$value = $preferences->getSettingValue(
|
||||
PhabricatorEmailStampsSetting::SETTINGKEY);
|
||||
|
||||
return ($value == PhabricatorEmailStampsSetting::VALUE_BODY_STAMPS);
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
@@ -1181,4 +1525,18 @@ final class PhabricatorMetaMTAMail
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorDestructibleInterface )----------------------------------- */
|
||||
|
||||
|
||||
public function destroyObjectPermanently(
|
||||
PhabricatorDestructionEngine $engine) {
|
||||
|
||||
$files = $this->loadAttachedFiles($engine->getViewer());
|
||||
foreach ($files as $file) {
|
||||
$engine->destroyObject($file);
|
||||
}
|
||||
|
||||
$this->delete();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user