diff --git a/.arcconfig b/.arcconfig index e6aa540bd3..f0fa152929 100644 --- a/.arcconfig +++ b/.arcconfig @@ -1,5 +1,4 @@ { "phabricator.uri": "https://secure.phabricator.com/", - "unit.engine": "PhutilUnitTestEngine", "load": ["src/"] } diff --git a/.arcunit b/.arcunit new file mode 100644 index 0000000000..860ee1aee2 --- /dev/null +++ b/.arcunit @@ -0,0 +1,8 @@ +{ + "engines": { + "phutil": { + "type": "phutil", + "include": "(\\.php$)" + } + } +} diff --git a/resources/celerity/map.php b/resources/celerity/map.php index da014bccde..64d3ff99f3 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -93,7 +93,7 @@ return array( 'rsrc/css/application/policy/policy-edit.css' => '815c66f7', 'rsrc/css/application/policy/policy-transaction-detail.css' => '82100a43', 'rsrc/css/application/policy/policy.css' => '957ea14c', - 'rsrc/css/application/ponder/ponder-view.css' => 'fcd6b398', + 'rsrc/css/application/ponder/ponder-view.css' => '6a399881', 'rsrc/css/application/projects/project-icon.css' => '4e3eaa5a', 'rsrc/css/application/releeph/releeph-core.css' => '9b3c5733', 'rsrc/css/application/releeph/releeph-preview-branch.css' => 'b7a6f4a5', @@ -148,6 +148,7 @@ return array( 'rsrc/css/phui/phui-tag-view.css' => '402691cc', 'rsrc/css/phui/phui-text.css' => 'cf019f54', 'rsrc/css/phui/phui-timeline-view.css' => 'f1bccf73', + 'rsrc/css/phui/phui-two-column-view.css' => 'add0a7d1', 'rsrc/css/phui/phui-workboard-view.css' => '6704d68d', 'rsrc/css/phui/phui-workpanel-view.css' => 'adec7699', 'rsrc/css/sprite-login.css' => '1ebb9bf9', @@ -400,7 +401,6 @@ return array( 'rsrc/js/application/phortune/phortune-credit-card-form.js' => '2290aeef', 'rsrc/js/application/policy/behavior-policy-control.js' => '7d470398', 'rsrc/js/application/policy/behavior-policy-rule-editor.js' => '5e9f347c', - 'rsrc/js/application/ponder/behavior-votebox.js' => '4e9b766b', 'rsrc/js/application/projects/behavior-project-boards.js' => 'ba4fa35c', 'rsrc/js/application/projects/behavior-project-create.js' => '065227cc', 'rsrc/js/application/projects/behavior-reorder-columns.js' => 'e1d25dfb', @@ -632,7 +632,6 @@ return array( 'javelin-behavior-phui-object-box-tabs' => '2bfa2836', 'javelin-behavior-policy-control' => '7d470398', 'javelin-behavior-policy-rule-editor' => '5e9f347c', - 'javelin-behavior-ponder-votebox' => '4e9b766b', 'javelin-behavior-project-boards' => 'ba4fa35c', 'javelin-behavior-project-create' => '065227cc', 'javelin-behavior-quicksand-blacklist' => '7927a7d3', @@ -803,6 +802,7 @@ return array( 'phui-text-css' => 'cf019f54', 'phui-theme-css' => '6b451f24', 'phui-timeline-view-css' => 'f1bccf73', + 'phui-two-column-view-css' => 'add0a7d1', 'phui-workboard-view-css' => '6704d68d', 'phui-workpanel-view-css' => 'adec7699', 'phuix-action-list-view' => 'b5c256b8', @@ -811,7 +811,7 @@ return array( 'policy-css' => '957ea14c', 'policy-edit-css' => '815c66f7', 'policy-transaction-detail-css' => '82100a43', - 'ponder-view-css' => 'fcd6b398', + 'ponder-view-css' => '6a399881', 'project-icon-css' => '4e3eaa5a', 'raphael-core' => '51ee6b43', 'raphael-g' => '40dde778', @@ -1142,13 +1142,6 @@ return array( 'javelin-stratcom', 'javelin-dom', ), - '4e9b766b' => array( - 'javelin-behavior', - 'javelin-dom', - 'javelin-util', - 'javelin-stratcom', - 'javelin-request', - ), '4fdb476d' => array( 'javelin-behavior', 'javelin-stratcom', diff --git a/resources/sql/autopatches/20150806.ponder.answer.1.sql b/resources/sql/autopatches/20150806.ponder.answer.1.sql new file mode 100644 index 0000000000..9c684b4446 --- /dev/null +++ b/resources/sql/autopatches/20150806.ponder.answer.1.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_ponder.ponder_answer + DROP COLUMN contentSource; diff --git a/resources/sql/autopatches/20150806.ponder.editpolicy.2.sql b/resources/sql/autopatches/20150806.ponder.editpolicy.2.sql new file mode 100644 index 0000000000..954f1b450e --- /dev/null +++ b/resources/sql/autopatches/20150806.ponder.editpolicy.2.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_ponder.ponder_question + DROP COLUMN editPolicy; diff --git a/resources/sql/autopatches/20150806.ponder.status.1.sql b/resources/sql/autopatches/20150806.ponder.status.1.sql new file mode 100644 index 0000000000..b0307ca2dc --- /dev/null +++ b/resources/sql/autopatches/20150806.ponder.status.1.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_ponder.ponder_question + MODIFY status VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT}; diff --git a/resources/sql/autopatches/20150806.ponder.status.2.sql b/resources/sql/autopatches/20150806.ponder.status.2.sql new file mode 100644 index 0000000000..0827c5b106 --- /dev/null +++ b/resources/sql/autopatches/20150806.ponder.status.2.sql @@ -0,0 +1,2 @@ +UPDATE {$NAMESPACE}_ponder.ponder_question + SET status = 'open' WHERE status = '0'; diff --git a/resources/sql/autopatches/20150806.ponder.status.3.sql b/resources/sql/autopatches/20150806.ponder.status.3.sql new file mode 100644 index 0000000000..fdd71a5b20 --- /dev/null +++ b/resources/sql/autopatches/20150806.ponder.status.3.sql @@ -0,0 +1,2 @@ +UPDATE {$NAMESPACE}_ponder.ponder_question + SET status = 'resolved' WHERE status = '1'; diff --git a/resources/sql/autopatches/20150808.ponder.vote.1.sql b/resources/sql/autopatches/20150808.ponder.vote.1.sql new file mode 100644 index 0000000000..31ac0f8e96 --- /dev/null +++ b/resources/sql/autopatches/20150808.ponder.vote.1.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_ponder.ponder_question + DROP COLUMN heat; diff --git a/resources/sql/autopatches/20150808.ponder.vote.2.sql b/resources/sql/autopatches/20150808.ponder.vote.2.sql new file mode 100644 index 0000000000..d31e72885d --- /dev/null +++ b/resources/sql/autopatches/20150808.ponder.vote.2.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_ponder.ponder_question + DROP COLUMN voteCount; diff --git a/resources/sql/autopatches/20150812.ponder.answer.1.sql b/resources/sql/autopatches/20150812.ponder.answer.1.sql new file mode 100644 index 0000000000..1cad5667b9 --- /dev/null +++ b/resources/sql/autopatches/20150812.ponder.answer.1.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_ponder.ponder_answer + ADD status VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT}; diff --git a/resources/sql/autopatches/20150812.ponder.answer.2.sql b/resources/sql/autopatches/20150812.ponder.answer.2.sql new file mode 100644 index 0000000000..ded0b765d5 --- /dev/null +++ b/resources/sql/autopatches/20150812.ponder.answer.2.sql @@ -0,0 +1,2 @@ +UPDATE {$NAMESPACE}_ponder.ponder_answer + SET status = 'visible' WHERE status = ''; diff --git a/scripts/almanac/manage_almanac.php b/scripts/almanac/manage_almanac.php index 933d1b7229..761891e142 100755 --- a/scripts/almanac/manage_almanac.php +++ b/scripts/almanac/manage_almanac.php @@ -14,8 +14,8 @@ EOSYNOPSIS ); $args->parseStandardArguments(); -$workflows = id(new PhutilSymbolLoader()) +$workflows = id(new PhutilClassMapQuery()) ->setAncestorClass('AlmanacManagementWorkflow') - ->loadObjects(); + ->execute(); $workflows[] = new PhutilHelpArgumentWorkflow(); $args->parseWorkflows($workflows); diff --git a/scripts/cache/manage_cache.php b/scripts/cache/manage_cache.php index 80e8e80819..002ba2be58 100755 --- a/scripts/cache/manage_cache.php +++ b/scripts/cache/manage_cache.php @@ -14,8 +14,8 @@ EOSYNOPSIS ); $args->parseStandardArguments(); -$workflows = id(new PhutilSymbolLoader()) +$workflows = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorCacheManagementWorkflow') - ->loadObjects(); + ->execute(); $workflows[] = new PhutilHelpArgumentWorkflow(); $args->parseWorkflows($workflows); diff --git a/scripts/daemon/manage_daemons.php b/scripts/daemon/manage_daemons.php index 087c925b81..793bd608d3 100755 --- a/scripts/daemon/manage_daemons.php +++ b/scripts/daemon/manage_daemons.php @@ -16,8 +16,8 @@ EOSYNOPSIS ); $args->parseStandardArguments(); -$workflows = id(new PhutilSymbolLoader()) +$workflows = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorDaemonManagementWorkflow') - ->loadObjects(); + ->execute(); $workflows[] = new PhutilHelpArgumentWorkflow(); $args->parseWorkflows($workflows); diff --git a/scripts/diviner/diviner.php b/scripts/diviner/diviner.php index 03d15274f4..fa4c19caa7 100755 --- a/scripts/diviner/diviner.php +++ b/scripts/diviner/diviner.php @@ -14,8 +14,8 @@ EOHELP ); $args->parseStandardArguments(); -$workflows = id(new PhutilSymbolLoader()) +$workflows = id(new PhutilClassMapQuery()) ->setAncestorClass('DivinerWorkflow') - ->loadObjects(); + ->execute(); $workflows[] = new PhutilHelpArgumentWorkflow(); $args->parseWorkflows($workflows); diff --git a/scripts/drydock/drydock_control.php b/scripts/drydock/drydock_control.php index 57ad877586..21ff9f8ee8 100755 --- a/scripts/drydock/drydock_control.php +++ b/scripts/drydock/drydock_control.php @@ -14,8 +14,8 @@ EOSYNOPSIS ); $args->parseStandardArguments(); -$workflows = id(new PhutilSymbolLoader()) +$workflows = id(new PhutilClassMapQuery()) ->setAncestorClass('DrydockManagementWorkflow') - ->loadObjects(); + ->execute(); $workflows[] = new PhutilHelpArgumentWorkflow(); $args->parseWorkflows($workflows); diff --git a/scripts/fact/manage_facts.php b/scripts/fact/manage_facts.php index 8a351dd1a8..8a773cfbdc 100755 --- a/scripts/fact/manage_facts.php +++ b/scripts/fact/manage_facts.php @@ -15,8 +15,8 @@ EOSYNOPSIS ); $args->parseStandardArguments(); -$workflows = id(new PhutilSymbolLoader()) +$workflows = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorFactManagementWorkflow') - ->loadObjects(); + ->execute(); $workflows[] = new PhutilHelpArgumentWorkflow(); $args->parseWorkflows($workflows); diff --git a/scripts/files/manage_files.php b/scripts/files/manage_files.php index edd947467c..3d7ff887a5 100755 --- a/scripts/files/manage_files.php +++ b/scripts/files/manage_files.php @@ -14,8 +14,8 @@ EOSYNOPSIS ); $args->parseStandardArguments(); -$workflows = id(new PhutilSymbolLoader()) +$workflows = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorFilesManagementWorkflow') - ->loadObjects(); + ->execute(); $workflows[] = new PhutilHelpArgumentWorkflow(); $args->parseWorkflows($workflows); diff --git a/scripts/lipsum/manage_lipsum.php b/scripts/lipsum/manage_lipsum.php index 610e6e9aa3..c2fa8f3a3b 100755 --- a/scripts/lipsum/manage_lipsum.php +++ b/scripts/lipsum/manage_lipsum.php @@ -14,8 +14,8 @@ EOSYNOPSIS ); $args->parseStandardArguments(); -$workflows = id(new PhutilSymbolLoader()) +$workflows = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorLipsumManagementWorkflow') - ->loadObjects(); + ->execute(); $workflows[] = new PhutilHelpArgumentWorkflow(); $args->parseWorkflows($workflows); diff --git a/scripts/mail/manage_mail.php b/scripts/mail/manage_mail.php index 7f73303fdb..12b907a2ee 100755 --- a/scripts/mail/manage_mail.php +++ b/scripts/mail/manage_mail.php @@ -14,8 +14,8 @@ EOSYNOPSIS ); $args->parseStandardArguments(); -$workflows = id(new PhutilSymbolLoader()) +$workflows = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorMailManagementWorkflow') - ->loadObjects(); + ->execute(); $workflows[] = new PhutilHelpArgumentWorkflow(); $args->parseWorkflows($workflows); diff --git a/scripts/repository/manage_repositories.php b/scripts/repository/manage_repositories.php index ae0395e663..5023dc66c6 100755 --- a/scripts/repository/manage_repositories.php +++ b/scripts/repository/manage_repositories.php @@ -15,8 +15,8 @@ EOSYNOPSIS ); $args->parseStandardArguments(); -$workflows = id(new PhutilSymbolLoader()) +$workflows = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorRepositoryManagementWorkflow') - ->loadObjects(); + ->execute(); $workflows[] = new PhutilHelpArgumentWorkflow(); $args->parseWorkflows($workflows); diff --git a/scripts/search/manage_search.php b/scripts/search/manage_search.php index 4b119f28cc..acd659dffe 100755 --- a/scripts/search/manage_search.php +++ b/scripts/search/manage_search.php @@ -14,8 +14,8 @@ EOSYNOPSIS ); $args->parseStandardArguments(); -$workflows = id(new PhutilSymbolLoader()) +$workflows = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorSearchManagementWorkflow') - ->loadObjects(); + ->execute(); $workflows[] = new PhutilHelpArgumentWorkflow(); $args->parseWorkflows($workflows); diff --git a/scripts/setup/manage_audit.php b/scripts/setup/manage_audit.php index 26bd3044e6..89b9927ad6 100755 --- a/scripts/setup/manage_audit.php +++ b/scripts/setup/manage_audit.php @@ -14,8 +14,8 @@ EOSYNOPSIS ); $args->parseStandardArguments(); -$workflows = id(new PhutilSymbolLoader()) +$workflows = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorAuditManagementWorkflow') - ->loadObjects(); + ->execute(); $workflows[] = new PhutilHelpArgumentWorkflow(); $args->parseWorkflows($workflows); diff --git a/scripts/setup/manage_auth.php b/scripts/setup/manage_auth.php index cdd1fe377f..3ddbdf6e1e 100755 --- a/scripts/setup/manage_auth.php +++ b/scripts/setup/manage_auth.php @@ -14,8 +14,8 @@ EOSYNOPSIS ); $args->parseStandardArguments(); -$workflows = id(new PhutilSymbolLoader()) +$workflows = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorAuthManagementWorkflow') - ->loadObjects(); + ->execute(); $workflows[] = new PhutilHelpArgumentWorkflow(); $args->parseWorkflows($workflows); diff --git a/scripts/setup/manage_celerity.php b/scripts/setup/manage_celerity.php index 268a7abaa8..bcda6353e1 100755 --- a/scripts/setup/manage_celerity.php +++ b/scripts/setup/manage_celerity.php @@ -14,8 +14,8 @@ EOSYNOPSIS ); $args->parseStandardArguments(); -$workflows = id(new PhutilSymbolLoader()) +$workflows = id(new PhutilClassMapQuery()) ->setAncestorClass('CelerityManagementWorkflow') - ->loadObjects(); + ->execute(); $workflows[] = new PhutilHelpArgumentWorkflow(); $args->parseWorkflows($workflows); diff --git a/scripts/setup/manage_config.php b/scripts/setup/manage_config.php index 996c851a32..a8d73e50c2 100755 --- a/scripts/setup/manage_config.php +++ b/scripts/setup/manage_config.php @@ -14,8 +14,8 @@ EOSYNOPSIS ); $args->parseStandardArguments(); -$workflows = id(new PhutilSymbolLoader()) +$workflows = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorConfigManagementWorkflow') - ->loadObjects(); + ->execute(); $workflows[] = new PhutilHelpArgumentWorkflow(); $args->parseWorkflows($workflows); diff --git a/scripts/setup/manage_feed.php b/scripts/setup/manage_feed.php index 0afea5d01c..02516aa9a5 100755 --- a/scripts/setup/manage_feed.php +++ b/scripts/setup/manage_feed.php @@ -14,8 +14,8 @@ EOSYNOPSIS ); $args->parseStandardArguments(); -$workflows = id(new PhutilSymbolLoader()) +$workflows = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorFeedManagementWorkflow') - ->loadObjects(); + ->execute(); $workflows[] = new PhutilHelpArgumentWorkflow(); $args->parseWorkflows($workflows); diff --git a/scripts/setup/manage_harbormaster.php b/scripts/setup/manage_harbormaster.php index 1c86838937..484c671aa9 100755 --- a/scripts/setup/manage_harbormaster.php +++ b/scripts/setup/manage_harbormaster.php @@ -14,8 +14,8 @@ EOSYNOPSIS ); $args->parseStandardArguments(); -$workflows = id(new PhutilSymbolLoader()) +$workflows = id(new PhutilClassMapQuery()) ->setAncestorClass('HarbormasterManagementWorkflow') - ->loadObjects(); + ->execute(); $workflows[] = new PhutilHelpArgumentWorkflow(); $args->parseWorkflows($workflows); diff --git a/scripts/setup/manage_hunks.php b/scripts/setup/manage_hunks.php index bff11ca72b..a2d35779de 100755 --- a/scripts/setup/manage_hunks.php +++ b/scripts/setup/manage_hunks.php @@ -14,8 +14,8 @@ EOSYNOPSIS ); $args->parseStandardArguments(); -$workflows = id(new PhutilSymbolLoader()) +$workflows = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorHunksManagementWorkflow') - ->loadObjects(); + ->execute(); $workflows[] = new PhutilHelpArgumentWorkflow(); $args->parseWorkflows($workflows); diff --git a/scripts/setup/manage_i18n.php b/scripts/setup/manage_i18n.php index bbbbdcb294..5f01b6331c 100755 --- a/scripts/setup/manage_i18n.php +++ b/scripts/setup/manage_i18n.php @@ -14,8 +14,8 @@ EOSYNOPSIS ); $args->parseStandardArguments(); -$workflows = id(new PhutilSymbolLoader()) +$workflows = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorInternationalizationManagementWorkflow') - ->loadObjects(); + ->execute(); $workflows[] = new PhutilHelpArgumentWorkflow(); $args->parseWorkflows($workflows); diff --git a/scripts/setup/manage_phortune.php b/scripts/setup/manage_phortune.php index 81eacd4dca..a04ed0ccd6 100755 --- a/scripts/setup/manage_phortune.php +++ b/scripts/setup/manage_phortune.php @@ -14,8 +14,8 @@ EOSYNOPSIS ); $args->parseStandardArguments(); -$workflows = id(new PhutilSymbolLoader()) +$workflows = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorPhortuneManagementWorkflow') - ->loadObjects(); + ->execute(); $workflows[] = new PhutilHelpArgumentWorkflow(); $args->parseWorkflows($workflows); diff --git a/scripts/setup/manage_policy.php b/scripts/setup/manage_policy.php index 034f498c0a..3e36592abc 100755 --- a/scripts/setup/manage_policy.php +++ b/scripts/setup/manage_policy.php @@ -14,8 +14,8 @@ EOSYNOPSIS ); $args->parseStandardArguments(); -$workflows = id(new PhutilSymbolLoader()) +$workflows = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorPolicyManagementWorkflow') - ->loadObjects(); + ->execute(); $workflows[] = new PhutilHelpArgumentWorkflow(); $args->parseWorkflows($workflows); diff --git a/scripts/setup/manage_remove.php b/scripts/setup/manage_remove.php index 581cae2383..9a49e8774d 100755 --- a/scripts/setup/manage_remove.php +++ b/scripts/setup/manage_remove.php @@ -14,8 +14,8 @@ EOSYNOPSIS ); $args->parseStandardArguments(); -$workflows = id(new PhutilSymbolLoader()) +$workflows = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorSystemRemoveWorkflow') - ->loadObjects(); + ->execute(); $workflows[] = new PhutilHelpArgumentWorkflow(); $args->parseWorkflows($workflows); diff --git a/scripts/setup/manage_trigger.php b/scripts/setup/manage_trigger.php index 558015fb32..9bfcd0e692 100755 --- a/scripts/setup/manage_trigger.php +++ b/scripts/setup/manage_trigger.php @@ -14,8 +14,8 @@ EOSYNOPSIS ); $args->parseStandardArguments(); -$workflows = id(new PhutilSymbolLoader()) +$workflows = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorWorkerTriggerManagementWorkflow') - ->loadObjects(); + ->execute(); $workflows[] = new PhutilHelpArgumentWorkflow(); $args->parseWorkflows($workflows); diff --git a/scripts/setup/manage_worker.php b/scripts/setup/manage_worker.php index 111c66d3e2..588cb69972 100755 --- a/scripts/setup/manage_worker.php +++ b/scripts/setup/manage_worker.php @@ -14,8 +14,8 @@ EOSYNOPSIS ); $args->parseStandardArguments(); -$workflows = id(new PhutilSymbolLoader()) +$workflows = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorWorkerManagementWorkflow') - ->loadObjects(); + ->execute(); $workflows[] = new PhutilHelpArgumentWorkflow(); $args->parseWorkflows($workflows); diff --git a/scripts/sms/manage_sms.php b/scripts/sms/manage_sms.php index a66f66040a..25a41d5d48 100755 --- a/scripts/sms/manage_sms.php +++ b/scripts/sms/manage_sms.php @@ -14,8 +14,8 @@ EOSYNOPSIS ); $args->parseStandardArguments(); -$workflows = id(new PhutilSymbolLoader()) +$workflows = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorSMSManagementWorkflow') - ->loadObjects(); + ->execute(); $workflows[] = new PhutilHelpArgumentWorkflow(); $args->parseWorkflows($workflows); diff --git a/scripts/sql/manage_storage.php b/scripts/sql/manage_storage.php index 1225e21a82..64a50dcdb0 100755 --- a/scripts/sql/manage_storage.php +++ b/scripts/sql/manage_storage.php @@ -160,9 +160,9 @@ try { exit(1); } -$workflows = id(new PhutilSymbolLoader()) +$workflows = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorStorageManagementWorkflow') - ->loadObjects(); + ->execute(); $patches = PhabricatorSQLPatchList::buildAllPatches(); diff --git a/scripts/ssh/ssh-exec.php b/scripts/ssh/ssh-exec.php index 0426a30825..45dacddebd 100755 --- a/scripts/ssh/ssh-exec.php +++ b/scripts/ssh/ssh-exec.php @@ -190,11 +190,10 @@ try { $user->getUsername())); } - $workflows = id(new PhutilSymbolLoader()) + $workflows = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorSSHWorkflow') - ->loadObjects(); - - $workflow_names = mpull($workflows, 'getName', 'getName'); + ->setUniqueMethod('getName') + ->execute(); if (!$original_argv) { throw new Exception( @@ -210,7 +209,7 @@ try { $user->getUsername(), 'git clone', 'hg push', - implode(', ', $workflow_names))); + implode(', ', array_keys($workflows)))); } $log_argv = implode(' ', $original_argv); @@ -231,7 +230,7 @@ try { $parsed_args = new PhutilArgumentParser($parseable_argv); - if (empty($workflow_names[$command])) { + if (empty($workflows[$command])) { throw new Exception(pht('Invalid command.')); } diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 0f00bd7430..3bb3ad8c5b 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -378,7 +378,6 @@ phutil_register_library_map(array( 'DifferentialFieldParseException' => 'applications/differential/exception/DifferentialFieldParseException.php', 'DifferentialFieldValidationException' => 'applications/differential/exception/DifferentialFieldValidationException.php', 'DifferentialFindConduitAPIMethod' => 'applications/differential/conduit/DifferentialFindConduitAPIMethod.php', - 'DifferentialFinishPostponedLintersConduitAPIMethod' => 'applications/differential/conduit/DifferentialFinishPostponedLintersConduitAPIMethod.php', 'DifferentialGetAllDiffsConduitAPIMethod' => 'applications/differential/conduit/DifferentialGetAllDiffsConduitAPIMethod.php', 'DifferentialGetCommitMessageConduitAPIMethod' => 'applications/differential/conduit/DifferentialGetCommitMessageConduitAPIMethod.php', 'DifferentialGetCommitPathsConduitAPIMethod' => 'applications/differential/conduit/DifferentialGetCommitPathsConduitAPIMethod.php', @@ -495,7 +494,6 @@ phutil_register_library_map(array( 'DifferentialUnitStatus' => 'applications/differential/constants/DifferentialUnitStatus.php', 'DifferentialUnitTestResult' => 'applications/differential/constants/DifferentialUnitTestResult.php', 'DifferentialUpdateRevisionConduitAPIMethod' => 'applications/differential/conduit/DifferentialUpdateRevisionConduitAPIMethod.php', - 'DifferentialUpdateUnitResultsConduitAPIMethod' => 'applications/differential/conduit/DifferentialUpdateUnitResultsConduitAPIMethod.php', 'DifferentialViewPolicyField' => 'applications/differential/customfield/DifferentialViewPolicyField.php', 'DiffusionAuditorDatasource' => 'applications/diffusion/typeahead/DiffusionAuditorDatasource.php', 'DiffusionAuditorsAddAuditorsHeraldAction' => 'applications/diffusion/herald/DiffusionAuditorsAddAuditorsHeraldAction.php', @@ -1063,6 +1061,7 @@ phutil_register_library_map(array( 'HeraldNotifyActionGroup' => 'applications/herald/action/HeraldNotifyActionGroup.php', 'HeraldObjectTranscript' => 'applications/herald/storage/transcript/HeraldObjectTranscript.php', 'HeraldPholioMockAdapter' => 'applications/pholio/herald/HeraldPholioMockAdapter.php', + 'HeraldPonderQuestionAdapter' => 'applications/ponder/herald/HeraldPonderQuestionAdapter.php', 'HeraldPreCommitAdapter' => 'applications/diffusion/herald/HeraldPreCommitAdapter.php', 'HeraldPreCommitContentAdapter' => 'applications/diffusion/herald/HeraldPreCommitContentAdapter.php', 'HeraldPreCommitRefAdapter' => 'applications/diffusion/herald/HeraldPreCommitRefAdapter.php', @@ -1249,7 +1248,6 @@ phutil_register_library_map(array( 'ManiphestTransactionSaveController' => 'applications/maniphest/controller/ManiphestTransactionSaveController.php', 'ManiphestUpdateConduitAPIMethod' => 'applications/maniphest/conduit/ManiphestUpdateConduitAPIMethod.php', 'ManiphestView' => 'applications/maniphest/view/ManiphestView.php', - 'MetaMTAConstants' => 'applications/metamta/constants/MetaMTAConstants.php', 'MetaMTAEmailTransactionCommand' => 'applications/metamta/command/MetaMTAEmailTransactionCommand.php', 'MetaMTAEmailTransactionCommandTestCase' => 'applications/metamta/command/__tests__/MetaMTAEmailTransactionCommandTestCase.php', 'MetaMTAMailReceivedGarbageCollector' => 'applications/metamta/garbagecollector/MetaMTAMailReceivedGarbageCollector.php', @@ -1407,6 +1405,7 @@ phutil_register_library_map(array( 'PHUITimelineEventView' => 'view/phui/PHUITimelineEventView.php', 'PHUITimelineExample' => 'applications/uiexample/examples/PHUITimelineExample.php', 'PHUITimelineView' => 'view/phui/PHUITimelineView.php', + 'PHUITwoColumnView' => 'view/phui/PHUITwoColumnView.php', 'PHUITypeaheadExample' => 'applications/uiexample/examples/PHUITypeaheadExample.php', 'PHUIWorkboardView' => 'view/phui/PHUIWorkboardView.php', 'PHUIWorkpanelView' => 'view/phui/PHUIWorkpanelView.php', @@ -2258,6 +2257,7 @@ phutil_register_library_map(array( 'PhabricatorMailManagementShowOutboundWorkflow' => 'applications/metamta/management/PhabricatorMailManagementShowOutboundWorkflow.php', 'PhabricatorMailManagementVolumeWorkflow' => 'applications/metamta/management/PhabricatorMailManagementVolumeWorkflow.php', 'PhabricatorMailManagementWorkflow' => 'applications/metamta/management/PhabricatorMailManagementWorkflow.php', + 'PhabricatorMailOutboundStatus' => 'applications/metamta/constants/PhabricatorMailOutboundStatus.php', 'PhabricatorMailReceiver' => 'applications/metamta/receiver/PhabricatorMailReceiver.php', 'PhabricatorMailReceiverTestCase' => 'applications/metamta/receiver/__tests__/PhabricatorMailReceiverTestCase.php', 'PhabricatorMailReplyHandler' => 'applications/metamta/replyhandler/PhabricatorMailReplyHandler.php', @@ -3395,20 +3395,23 @@ phutil_register_library_map(array( 'PonderAnswerPHIDType' => 'applications/ponder/phid/PonderAnswerPHIDType.php', 'PonderAnswerQuery' => 'applications/ponder/query/PonderAnswerQuery.php', 'PonderAnswerSaveController' => 'applications/ponder/controller/PonderAnswerSaveController.php', + 'PonderAnswerStatus' => 'applications/ponder/constants/PonderAnswerStatus.php', 'PonderAnswerTransaction' => 'applications/ponder/storage/PonderAnswerTransaction.php', 'PonderAnswerTransactionComment' => 'applications/ponder/storage/PonderAnswerTransactionComment.php', 'PonderAnswerTransactionQuery' => 'applications/ponder/query/PonderAnswerTransactionQuery.php', + 'PonderAnswerView' => 'applications/ponder/view/PonderAnswerView.php', 'PonderConstants' => 'applications/ponder/constants/PonderConstants.php', 'PonderController' => 'applications/ponder/controller/PonderController.php', 'PonderDAO' => 'applications/ponder/storage/PonderDAO.php', + 'PonderDefaultViewCapability' => 'applications/ponder/capability/PonderDefaultViewCapability.php', 'PonderEditor' => 'applications/ponder/editor/PonderEditor.php', + 'PonderFooterView' => 'applications/ponder/view/PonderFooterView.php', + 'PonderHelpfulSaveController' => 'applications/ponder/controller/PonderHelpfulSaveController.php', + 'PonderModerateCapability' => 'applications/ponder/capability/PonderModerateCapability.php', 'PonderQuestion' => 'applications/ponder/storage/PonderQuestion.php', 'PonderQuestionCommentController' => 'applications/ponder/controller/PonderQuestionCommentController.php', - 'PonderQuestionDefaultEditCapability' => 'applications/ponder/capability/PonderQuestionDefaultEditCapability.php', - 'PonderQuestionDefaultViewCapability' => 'applications/ponder/capability/PonderQuestionDefaultViewCapability.php', 'PonderQuestionEditController' => 'applications/ponder/controller/PonderQuestionEditController.php', 'PonderQuestionEditor' => 'applications/ponder/editor/PonderQuestionEditor.php', - 'PonderQuestionHasVotingUserEdgeType' => 'applications/ponder/edge/PonderQuestionHasVotingUserEdgeType.php', 'PonderQuestionHistoryController' => 'applications/ponder/controller/PonderQuestionHistoryController.php', 'PonderQuestionListController' => 'applications/ponder/controller/PonderQuestionListController.php', 'PonderQuestionMailReceiver' => 'applications/ponder/mail/PonderQuestionMailReceiver.php', @@ -3427,12 +3430,9 @@ phutil_register_library_map(array( 'PonderSearchIndexer' => 'applications/ponder/search/PonderSearchIndexer.php', 'PonderTransactionFeedStory' => 'applications/ponder/feed/PonderTransactionFeedStory.php', 'PonderVotableInterface' => 'applications/ponder/storage/PonderVotableInterface.php', - 'PonderVotableView' => 'applications/ponder/view/PonderVotableView.php', 'PonderVote' => 'applications/ponder/constants/PonderVote.php', 'PonderVoteEditor' => 'applications/ponder/editor/PonderVoteEditor.php', - 'PonderVoteSaveController' => 'applications/ponder/controller/PonderVoteSaveController.php', 'PonderVotingUserHasAnswerEdgeType' => 'applications/ponder/edge/PonderVotingUserHasAnswerEdgeType.php', - 'PonderVotingUserHasQuestionEdgeType' => 'applications/ponder/edge/PonderVotingUserHasQuestionEdgeType.php', 'ProjectAddProjectsEmailCommand' => 'applications/project/command/ProjectAddProjectsEmailCommand.php', 'ProjectBoardTaskCard' => 'applications/project/view/ProjectBoardTaskCard.php', 'ProjectCanLockProjectsCapability' => 'applications/project/capability/ProjectCanLockProjectsCapability.php', @@ -4001,7 +4001,6 @@ phutil_register_library_map(array( 'DifferentialFieldParseException' => 'Exception', 'DifferentialFieldValidationException' => 'Exception', 'DifferentialFindConduitAPIMethod' => 'DifferentialConduitAPIMethod', - 'DifferentialFinishPostponedLintersConduitAPIMethod' => 'DifferentialConduitAPIMethod', 'DifferentialGetAllDiffsConduitAPIMethod' => 'DifferentialConduitAPIMethod', 'DifferentialGetCommitMessageConduitAPIMethod' => 'DifferentialConduitAPIMethod', 'DifferentialGetCommitPathsConduitAPIMethod' => 'DifferentialConduitAPIMethod', @@ -4138,7 +4137,6 @@ phutil_register_library_map(array( 'DifferentialUnitStatus' => 'Phobject', 'DifferentialUnitTestResult' => 'Phobject', 'DifferentialUpdateRevisionConduitAPIMethod' => 'DifferentialConduitAPIMethod', - 'DifferentialUpdateUnitResultsConduitAPIMethod' => 'DifferentialConduitAPIMethod', 'DifferentialViewPolicyField' => 'DifferentialCoreCustomField', 'DiffusionAuditorDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 'DiffusionAuditorsAddAuditorsHeraldAction' => 'DiffusionAuditorsHeraldAction', @@ -4789,6 +4787,7 @@ phutil_register_library_map(array( 'HeraldNotifyActionGroup' => 'HeraldActionGroup', 'HeraldObjectTranscript' => 'Phobject', 'HeraldPholioMockAdapter' => 'HeraldAdapter', + 'HeraldPonderQuestionAdapter' => 'HeraldAdapter', 'HeraldPreCommitAdapter' => 'HeraldAdapter', 'HeraldPreCommitContentAdapter' => 'HeraldPreCommitAdapter', 'HeraldPreCommitRefAdapter' => 'HeraldPreCommitAdapter', @@ -5014,12 +5013,11 @@ phutil_register_library_map(array( 'ManiphestTransactionSaveController' => 'ManiphestController', 'ManiphestUpdateConduitAPIMethod' => 'ManiphestConduitAPIMethod', 'ManiphestView' => 'AphrontView', - 'MetaMTAConstants' => 'Phobject', 'MetaMTAEmailTransactionCommand' => 'Phobject', 'MetaMTAEmailTransactionCommandTestCase' => 'PhabricatorTestCase', 'MetaMTAMailReceivedGarbageCollector' => 'PhabricatorGarbageCollector', 'MetaMTAMailSentGarbageCollector' => 'PhabricatorGarbageCollector', - 'MetaMTAReceivedMailStatus' => 'MetaMTAConstants', + 'MetaMTAReceivedMailStatus' => 'Phobject', 'MultimeterContext' => 'MultimeterDimension', 'MultimeterControl' => 'Phobject', 'MultimeterController' => 'PhabricatorController', @@ -5188,6 +5186,7 @@ phutil_register_library_map(array( 'PHUITimelineEventView' => 'AphrontView', 'PHUITimelineExample' => 'PhabricatorUIExample', 'PHUITimelineView' => 'AphrontView', + 'PHUITwoColumnView' => 'AphrontTagView', 'PHUITypeaheadExample' => 'PhabricatorUIExample', 'PHUIWorkboardView' => 'AphrontTagView', 'PHUIWorkpanelView' => 'AphrontTagView', @@ -6183,6 +6182,7 @@ phutil_register_library_map(array( 'PhabricatorMailManagementShowOutboundWorkflow' => 'PhabricatorMailManagementWorkflow', 'PhabricatorMailManagementVolumeWorkflow' => 'PhabricatorMailManagementWorkflow', 'PhabricatorMailManagementWorkflow' => 'PhabricatorManagementWorkflow', + 'PhabricatorMailOutboundStatus' => 'Phobject', 'PhabricatorMailReceiver' => 'Phobject', 'PhabricatorMailReceiverTestCase' => 'PhabricatorTestCase', 'PhabricatorMailReplyHandler' => 'Phobject', @@ -7572,7 +7572,6 @@ phutil_register_library_map(array( 'PhabricatorPolicyInterface', 'PhabricatorFlaggableInterface', 'PhabricatorSubscribableInterface', - 'PhabricatorTokenReceiverInterface', 'PhabricatorDestructibleInterface', ), 'PonderAnswerCommentController' => 'PonderController', @@ -7583,18 +7582,23 @@ phutil_register_library_map(array( 'PonderAnswerPHIDType' => 'PhabricatorPHIDType', 'PonderAnswerQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PonderAnswerSaveController' => 'PonderController', + 'PonderAnswerStatus' => 'PonderConstants', 'PonderAnswerTransaction' => 'PhabricatorApplicationTransaction', 'PonderAnswerTransactionComment' => 'PhabricatorApplicationTransactionComment', 'PonderAnswerTransactionQuery' => 'PhabricatorApplicationTransactionQuery', + 'PonderAnswerView' => 'AphrontTagView', 'PonderConstants' => 'Phobject', 'PonderController' => 'PhabricatorController', 'PonderDAO' => 'PhabricatorLiskDAO', + 'PonderDefaultViewCapability' => 'PhabricatorPolicyCapability', 'PonderEditor' => 'PhabricatorApplicationTransactionEditor', + 'PonderFooterView' => 'AphrontTagView', + 'PonderHelpfulSaveController' => 'PonderController', + 'PonderModerateCapability' => 'PhabricatorPolicyCapability', 'PonderQuestion' => array( 'PonderDAO', 'PhabricatorApplicationTransactionInterface', 'PhabricatorMarkupInterface', - 'PonderVotableInterface', 'PhabricatorSubscribableInterface', 'PhabricatorFlaggableInterface', 'PhabricatorPolicyInterface', @@ -7604,11 +7608,8 @@ phutil_register_library_map(array( 'PhabricatorSpacesInterface', ), 'PonderQuestionCommentController' => 'PonderController', - 'PonderQuestionDefaultEditCapability' => 'PhabricatorPolicyCapability', - 'PonderQuestionDefaultViewCapability' => 'PhabricatorPolicyCapability', 'PonderQuestionEditController' => 'PonderController', 'PonderQuestionEditor' => 'PonderEditor', - 'PonderQuestionHasVotingUserEdgeType' => 'PhabricatorEdgeType', 'PonderQuestionHistoryController' => 'PonderController', 'PonderQuestionListController' => 'PonderController', 'PonderQuestionMailReceiver' => 'PhabricatorObjectMailReceiver', @@ -7626,12 +7627,9 @@ phutil_register_library_map(array( 'PonderSchemaSpec' => 'PhabricatorConfigSchemaSpec', 'PonderSearchIndexer' => 'PhabricatorSearchDocumentIndexer', 'PonderTransactionFeedStory' => 'PhabricatorApplicationTransactionFeedStory', - 'PonderVotableView' => 'AphrontView', 'PonderVote' => 'PonderConstants', 'PonderVoteEditor' => 'PhabricatorEditor', - 'PonderVoteSaveController' => 'PonderController', 'PonderVotingUserHasAnswerEdgeType' => 'PhabricatorEdgeType', - 'PonderVotingUserHasQuestionEdgeType' => 'PhabricatorEdgeType', 'ProjectAddProjectsEmailCommand' => 'MetaMTAEmailTransactionCommand', 'ProjectBoardTaskCard' => 'Phobject', 'ProjectCanLockProjectsCapability' => 'PhabricatorPolicyCapability', diff --git a/src/__tests__/PhabricatorConduitTestCase.php b/src/__tests__/PhabricatorConduitTestCase.php index 71d6b744fc..3a9c6911bc 100644 --- a/src/__tests__/PhabricatorConduitTestCase.php +++ b/src/__tests__/PhabricatorConduitTestCase.php @@ -3,9 +3,9 @@ final class PhabricatorConduitTestCase extends PhabricatorTestCase { public function testConduitMethods() { - $methods = id(new PhutilSymbolLoader()) + $methods = id(new PhutilClassMapQuery()) ->setAncestorClass('ConduitAPIMethod') - ->loadObjects(); + ->execute(); // We're just looking for a side effect of ConduitCall construction // here: it will throw if any methods define reserved parameter names. diff --git a/src/aphront/configuration/AphrontApplicationConfiguration.php b/src/aphront/configuration/AphrontApplicationConfiguration.php index d215d2f67a..0a33f6f77c 100644 --- a/src/aphront/configuration/AphrontApplicationConfiguration.php +++ b/src/aphront/configuration/AphrontApplicationConfiguration.php @@ -246,13 +246,14 @@ abstract class AphrontApplicationConfiguration extends Phobject { if ($response instanceof AphrontWebpageResponse) { echo phutil_tag( 'div', - array('style' => - 'background: #eeddff;'. - 'white-space: pre-wrap;'. - 'z-index: 200000;'. - 'position: relative;'. - 'padding: 8px;'. - 'font-family: monospace', + array( + 'style' => + 'background: #eeddff;'. + 'white-space: pre-wrap;'. + 'z-index: 200000;'. + 'position: relative;'. + 'padding: 8px;'. + 'font-family: monospace', ), $unexpected_output); } diff --git a/src/applications/badges/storage/PhabricatorBadgesTransaction.php b/src/applications/badges/storage/PhabricatorBadgesTransaction.php index c50297f768..5d3967902f 100644 --- a/src/applications/badges/storage/PhabricatorBadgesTransaction.php +++ b/src/applications/badges/storage/PhabricatorBadgesTransaction.php @@ -10,7 +10,6 @@ final class PhabricatorBadgesTransaction const TYPE_STATUS = 'badges:status'; const TYPE_FLAVOR = 'badges:flavor'; - const MAILTAG_NAME = 'badges:name'; const MAILTAG_DETAILS = 'badges:details'; const MAILTAG_COMMENT = 'badges:comment'; const MAILTAG_OTHER = 'badges:other'; diff --git a/src/applications/celerity/controller/CelerityPhabricatorResourceController.php b/src/applications/celerity/controller/CelerityPhabricatorResourceController.php index 2f4cd113a5..c5f878b61e 100644 --- a/src/applications/celerity/controller/CelerityPhabricatorResourceController.php +++ b/src/applications/celerity/controller/CelerityPhabricatorResourceController.php @@ -17,14 +17,12 @@ final class CelerityPhabricatorResourceController return CelerityResourceMap::getNamedInstance($this->library); } - public function willProcessRequest(array $data) { - $this->path = $data['path']; - $this->hash = $data['hash']; - $this->library = $data['library']; - $this->postprocessorKey = idx($data, 'postprocessor'); - } + public function handleRequest(AphrontRequest $request) { + $this->path = $request->getURIData('path'); + $this->hash = $request->getURIData('hash'); + $this->library = $request->getURIData('library'); + $this->postprocessorKey = $request->getURIData('postprocessor'); - public function processRequest() { // Check that the resource library exists before trying to serve resources // from it. try { diff --git a/src/applications/conduit/method/ConduitAPIMethod.php b/src/applications/conduit/method/ConduitAPIMethod.php index 39fa9b66f4..afa75c53b3 100644 --- a/src/applications/conduit/method/ConduitAPIMethod.php +++ b/src/applications/conduit/method/ConduitAPIMethod.php @@ -139,34 +139,10 @@ abstract class ConduitAPIMethod } public static function loadAllConduitMethods() { - static $method_map = null; - - if ($method_map === null) { - $methods = id(new PhutilSymbolLoader()) - ->setAncestorClass(__CLASS__) - ->loadObjects(); - - foreach ($methods as $method) { - $name = $method->getAPIMethodName(); - - if (empty($method_map[$name])) { - $method_map[$name] = $method; - continue; - } - - $orig_class = get_class($method_map[$name]); - $this_class = get_class($method); - throw new Exception( - pht( - 'Two Conduit API method classes (%s, %s) both have the same '. - 'method name (%s). API methods must have unique method names.', - $orig_class, - $this_class, - $name)); - } - } - - return $method_map; + return id(new PhutilClassMapQuery()) + ->setAncestorClass(__CLASS__) + ->setUniqueMethod('getAPIMethodName') + ->execute(); } public static function getConduitMethod($method_name) { diff --git a/src/applications/conduit/method/ConduitQueryConduitAPIMethod.php b/src/applications/conduit/method/ConduitQueryConduitAPIMethod.php index 6cd9283b4c..9a982f49b6 100644 --- a/src/applications/conduit/method/ConduitQueryConduitAPIMethod.php +++ b/src/applications/conduit/method/ConduitQueryConduitAPIMethod.php @@ -19,10 +19,9 @@ final class ConduitQueryConduitAPIMethod extends ConduitAPIMethod { } protected function execute(ConduitAPIRequest $request) { - $classes = id(new PhutilSymbolLoader()) + $classes = id(new PhutilClassMapQuery()) ->setAncestorClass('ConduitAPIMethod') - ->setType('class') - ->loadObjects(); + ->execute(); $names_to_params = array(); foreach ($classes as $class) { diff --git a/src/applications/conduit/query/PhabricatorConduitMethodQuery.php b/src/applications/conduit/query/PhabricatorConduitMethodQuery.php index fa9592f8e8..e1e7e1914b 100644 --- a/src/applications/conduit/query/PhabricatorConduitMethodQuery.php +++ b/src/applications/conduit/query/PhabricatorConduitMethodQuery.php @@ -47,14 +47,10 @@ final class PhabricatorConduitMethodQuery } private function getAllMethods() { - static $methods; - if ($methods === null) { - $methods = id(new PhutilSymbolLoader()) - ->setAncestorClass('ConduitAPIMethod') - ->loadObjects(); - $methods = msort($methods, 'getSortOrder'); - } - return $methods; + return id(new PhutilClassMapQuery()) + ->setAncestorClass('ConduitAPIMethod') + ->setSortMethod('getSortOrder') + ->execute(); } private function filterMethods(array $methods) { diff --git a/src/applications/config/schema/PhabricatorConfigCoreSchemaSpec.php b/src/applications/config/schema/PhabricatorConfigCoreSchemaSpec.php index 0d8494d1ce..4219ad2cb4 100644 --- a/src/applications/config/schema/PhabricatorConfigCoreSchemaSpec.php +++ b/src/applications/config/schema/PhabricatorConfigCoreSchemaSpec.php @@ -9,9 +9,9 @@ final class PhabricatorConfigCoreSchemaSpec public function buildSchemata() { // Build all Lisk table schemata. - $lisk_objects = id(new PhutilSymbolLoader()) + $lisk_objects = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorLiskDAO') - ->loadObjects(); + ->execute(); $counters = array(); foreach ($lisk_objects as $object) { diff --git a/src/applications/config/schema/PhabricatorConfigSchemaQuery.php b/src/applications/config/schema/PhabricatorConfigSchemaQuery.php index 57db889956..c1e94619ce 100644 --- a/src/applications/config/schema/PhabricatorConfigSchemaQuery.php +++ b/src/applications/config/schema/PhabricatorConfigSchemaQuery.php @@ -155,9 +155,9 @@ final class PhabricatorConfigSchemaQuery extends Phobject { $databases = $this->getDatabaseNames(); $info = $this->getAPI()->getCharsetInfo(); - $specs = id(new PhutilSymbolLoader()) + $specs = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorConfigSchemaSpec') - ->loadObjects(); + ->execute(); $server_schema = new PhabricatorConfigServerSchema(); foreach ($specs as $spec) { diff --git a/src/applications/conpherence/__tests__/ConpherenceTestCase.php b/src/applications/conpherence/__tests__/ConpherenceTestCase.php index 8de135f24e..ce9dc8ceee 100644 --- a/src/applications/conpherence/__tests__/ConpherenceTestCase.php +++ b/src/applications/conpherence/__tests__/ConpherenceTestCase.php @@ -7,9 +7,10 @@ abstract class ConpherenceTestCase extends PhabricatorTestCase { ConpherenceThread $conpherence, array $participant_phids) { - $xactions = array(id(new ConpherenceTransaction()) - ->setTransactionType(ConpherenceTransaction::TYPE_PARTICIPANTS) - ->setNewValue(array('+' => $participant_phids)), + $xactions = array( + id(new ConpherenceTransaction()) + ->setTransactionType(ConpherenceTransaction::TYPE_PARTICIPANTS) + ->setNewValue(array('+' => $participant_phids)), ); $editor = id(new ConpherenceEditor()) ->setActor($actor) @@ -23,9 +24,10 @@ abstract class ConpherenceTestCase extends PhabricatorTestCase { ConpherenceThread $conpherence, array $participant_phids) { - $xactions = array(id(new ConpherenceTransaction()) - ->setTransactionType(ConpherenceTransaction::TYPE_PARTICIPANTS) - ->setNewValue(array('-' => $participant_phids)), + $xactions = array( + id(new ConpherenceTransaction()) + ->setTransactionType(ConpherenceTransaction::TYPE_PARTICIPANTS) + ->setNewValue(array('-' => $participant_phids)), ); $editor = id(new ConpherenceEditor()) ->setActor($actor) diff --git a/src/applications/console/core/DarkConsoleCore.php b/src/applications/console/core/DarkConsoleCore.php index 09398df73a..2898a56e41 100644 --- a/src/applications/console/core/DarkConsoleCore.php +++ b/src/applications/console/core/DarkConsoleCore.php @@ -6,19 +6,13 @@ final class DarkConsoleCore extends Phobject { const STORAGE_VERSION = 1; public function __construct() { - $symbols = id(new PhutilSymbolLoader()) - ->setType('class') + $this->plugins = id(new PhutilClassMapQuery()) ->setAncestorClass('DarkConsolePlugin') - ->selectAndLoadSymbols(); + ->execute(); - foreach ($symbols as $symbol) { - $plugin = newv($symbol['name'], array()); - if (!$plugin->shouldStartup()) { - continue; - } + foreach ($this->plugins as $plugin) { $plugin->setConsoleCore($this); $plugin->didStartup(); - $this->plugins[$symbol['name']] = $plugin; } } diff --git a/src/applications/console/plugin/DarkConsolePlugin.php b/src/applications/console/plugin/DarkConsolePlugin.php index 13d237d9d5..3ec9c76823 100644 --- a/src/applications/console/plugin/DarkConsolePlugin.php +++ b/src/applications/console/plugin/DarkConsolePlugin.php @@ -62,10 +62,6 @@ abstract class DarkConsolePlugin extends Phobject { return $this->getRequest()->getRequestURI(); } - public function shouldStartup() { - return true; - } - public function didStartup() { return null; } diff --git a/src/applications/daemon/controller/PhabricatorDaemonLogEventViewController.php b/src/applications/daemon/controller/PhabricatorDaemonLogEventViewController.php index 2991b0b62b..772ee87fd1 100644 --- a/src/applications/daemon/controller/PhabricatorDaemonLogEventViewController.php +++ b/src/applications/daemon/controller/PhabricatorDaemonLogEventViewController.php @@ -3,16 +3,10 @@ final class PhabricatorDaemonLogEventViewController extends PhabricatorDaemonController { - private $id; + public function handleRequest(AphrontRequest $request) { + $id = $request->getURIData('id'); - public function willProcessRequest(array $data) { - $this->id = $data['id']; - } - - public function processRequest() { - $request = $this->getRequest(); - - $event = id(new PhabricatorDaemonLogEvent())->load($this->id); + $event = id(new PhabricatorDaemonLogEvent())->load($id); if (!$event) { return new Aphront404Response(); } diff --git a/src/applications/daemon/controller/PhabricatorDaemonLogListController.php b/src/applications/daemon/controller/PhabricatorDaemonLogListController.php index 7b2324ae59..c1de0b892f 100644 --- a/src/applications/daemon/controller/PhabricatorDaemonLogListController.php +++ b/src/applications/daemon/controller/PhabricatorDaemonLogListController.php @@ -3,9 +3,8 @@ final class PhabricatorDaemonLogListController extends PhabricatorDaemonController { - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); $pager = new AphrontCursorPagerView(); $pager->readFromRequest($request); diff --git a/src/applications/daemon/controller/PhabricatorDaemonLogViewController.php b/src/applications/daemon/controller/PhabricatorDaemonLogViewController.php index baab0528e3..60bf290ae5 100644 --- a/src/applications/daemon/controller/PhabricatorDaemonLogViewController.php +++ b/src/applications/daemon/controller/PhabricatorDaemonLogViewController.php @@ -3,19 +3,13 @@ final class PhabricatorDaemonLogViewController extends PhabricatorDaemonController { - private $id; - - public function willProcessRequest(array $data) { - $this->id = $data['id']; - } - - public function processRequest() { - $request = $this->getRequest(); - $user = $request->getUser(); + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); + $id = $request->getURIData('id'); $log = id(new PhabricatorDaemonLogQuery()) - ->setViewer($user) - ->withIDs(array($this->id)) + ->setViewer($viewer) + ->withIDs(array($id)) ->setAllowStatusWrites(true) ->executeOne(); if (!$log) { @@ -76,7 +70,7 @@ final class PhabricatorDaemonLogViewController $properties = $this->buildPropertyListView($log); $event_view = id(new PhabricatorDaemonLogEventsView()) - ->setUser($user) + ->setUser($viewer) ->setEvents($events); $event_panel = new PHUIObjectBoxView(); diff --git a/src/applications/daemon/controller/PhabricatorWorkerTaskDetailController.php b/src/applications/daemon/controller/PhabricatorWorkerTaskDetailController.php index 66925ffd36..ad8f673fd7 100644 --- a/src/applications/daemon/controller/PhabricatorWorkerTaskDetailController.php +++ b/src/applications/daemon/controller/PhabricatorWorkerTaskDetailController.php @@ -3,20 +3,14 @@ final class PhabricatorWorkerTaskDetailController extends PhabricatorDaemonController { - private $id; + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); + $id = $request->getURIData('id'); - public function willProcessRequest(array $data) { - $this->id = $data['id']; - } - - public function processRequest() { - $request = $this->getRequest(); - $user = $request->getUser(); - - $task = id(new PhabricatorWorkerActiveTask())->load($this->id); + $task = id(new PhabricatorWorkerActiveTask())->load($id); if (!$task) { $tasks = id(new PhabricatorWorkerArchiveTaskQuery()) - ->withIDs(array($this->id)) + ->withIDs(array($id)) ->execute(); $task = reset($tasks); } @@ -155,9 +149,9 @@ final class PhabricatorWorkerTaskDetailController $worker = $task->getWorkerInstance(); $data = $worker->renderForDisplay($viewer); - $view->addProperty( - pht('Data'), - $data); + if ($data !== null) { + $view->addProperty(pht('Data'), $data); + } return $view; } diff --git a/src/applications/daemon/management/PhabricatorDaemonManagementWorkflow.php b/src/applications/daemon/management/PhabricatorDaemonManagementWorkflow.php index f62dcecff9..1cfa2499b2 100644 --- a/src/applications/daemon/management/PhabricatorDaemonManagementWorkflow.php +++ b/src/applications/daemon/management/PhabricatorDaemonManagementWorkflow.php @@ -6,8 +6,7 @@ abstract class PhabricatorDaemonManagementWorkflow private $runDaemonsAsUser = null; final protected function loadAvailableDaemonClasses() { - $loader = new PhutilSymbolLoader(); - return $loader + return id(new PhutilSymbolLoader()) ->setAncestorClass('PhutilDaemon') ->setConcreteOnly(true) ->selectSymbolsWithoutLoading(); diff --git a/src/applications/dashboard/controller/PhabricatorDashboardManageController.php b/src/applications/dashboard/controller/PhabricatorDashboardManageController.php index 1719687b0d..6af8dc7b5e 100644 --- a/src/applications/dashboard/controller/PhabricatorDashboardManageController.php +++ b/src/applications/dashboard/controller/PhabricatorDashboardManageController.php @@ -3,16 +3,10 @@ final class PhabricatorDashboardManageController extends PhabricatorDashboardController { - private $id; + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); + $id = $request->getURIData('id'); - public function willProcessRequest(array $data) { - $this->id = $data['id']; - } - - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); - $id = $this->id; $dashboard_uri = $this->getApplicationURI('view/'.$id.'/'); // TODO: This UI should drop a lot of capabilities if the user can't @@ -21,7 +15,7 @@ final class PhabricatorDashboardManageController $dashboard = id(new PhabricatorDashboardQuery()) ->setViewer($viewer) - ->withIDs(array($this->id)) + ->withIDs(array($id)) ->needPanels(true) ->executeOne(); if (!$dashboard) { diff --git a/src/applications/dashboard/customfield/PhabricatorDashboardPanelSearchApplicationCustomField.php b/src/applications/dashboard/customfield/PhabricatorDashboardPanelSearchApplicationCustomField.php index bcc08f7ebf..35517f520a 100644 --- a/src/applications/dashboard/customfield/PhabricatorDashboardPanelSearchApplicationCustomField.php +++ b/src/applications/dashboard/customfield/PhabricatorDashboardPanelSearchApplicationCustomField.php @@ -12,11 +12,11 @@ final class PhabricatorDashboardPanelSearchApplicationCustomField } public function renderEditControl(array $handles) { - - $engines = id(new PhutilSymbolLoader()) + $engines = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorApplicationSearchEngine') - ->loadObjects(); - $engines = mfilter($engines, 'canUseInPanelContext'); + ->setFilterMethod('canUseInPanelContext') + ->execute(); + $all_apps = id(new PhabricatorApplicationQuery()) ->setViewer($this->getViewer()) ->withUnlisted(false) diff --git a/src/applications/dashboard/customfield/PhabricatorDashboardPanelSearchQueryCustomField.php b/src/applications/dashboard/customfield/PhabricatorDashboardPanelSearchQueryCustomField.php index 5c74ab1685..87870bf6ff 100644 --- a/src/applications/dashboard/customfield/PhabricatorDashboardPanelSearchQueryCustomField.php +++ b/src/applications/dashboard/customfield/PhabricatorDashboardPanelSearchQueryCustomField.php @@ -12,11 +12,10 @@ final class PhabricatorDashboardPanelSearchQueryCustomField } public function renderEditControl(array $handles) { - - $engines = id(new PhutilSymbolLoader()) + $engines = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorApplicationSearchEngine') - ->loadObjects(); - $engines = mfilter($engines, 'canUseInPanelContext'); + ->setFilterMethod('canUseInPanelContext') + ->execute(); $value = $this->getFieldValue(); diff --git a/src/applications/differential/conduit/DifferentialCreateDiffConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialCreateDiffConduitAPIMethod.php index 4a5b749268..41f5f34770 100644 --- a/src/applications/differential/conduit/DifferentialCreateDiffConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialCreateDiffConduitAPIMethod.php @@ -143,9 +143,10 @@ final class DifferentialCreateDiffConduitAPIMethod 'unitStatus' => $unit_status, ); - $xactions = array(id(new DifferentialTransaction()) - ->setTransactionType(DifferentialDiffTransaction::TYPE_DIFF_CREATE) - ->setNewValue($diff_data_dict), + $xactions = array( + id(new DifferentialTransaction()) + ->setTransactionType(DifferentialDiffTransaction::TYPE_DIFF_CREATE) + ->setNewValue($diff_data_dict), ); id(new DifferentialDiffEditor()) diff --git a/src/applications/differential/conduit/DifferentialCreateRawDiffConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialCreateRawDiffConduitAPIMethod.php index e8dd29f86a..c69a06bd9a 100644 --- a/src/applications/differential/conduit/DifferentialCreateRawDiffConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialCreateRawDiffConduitAPIMethod.php @@ -51,9 +51,10 @@ final class DifferentialCreateRawDiffConduitAPIMethod 'unitStatus' => DifferentialUnitStatus::UNIT_SKIP, ); - $xactions = array(id(new DifferentialTransaction()) - ->setTransactionType(DifferentialDiffTransaction::TYPE_DIFF_CREATE) - ->setNewValue($diff_data_dict), + $xactions = array( + id(new DifferentialTransaction()) + ->setTransactionType(DifferentialDiffTransaction::TYPE_DIFF_CREATE) + ->setNewValue($diff_data_dict), ); if ($request->getValue('viewPolicy')) { diff --git a/src/applications/differential/conduit/DifferentialFinishPostponedLintersConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialFinishPostponedLintersConduitAPIMethod.php deleted file mode 100644 index f53340f06b..0000000000 --- a/src/applications/differential/conduit/DifferentialFinishPostponedLintersConduitAPIMethod.php +++ /dev/null @@ -1,118 +0,0 @@ - 'required diffID', - 'linters' => 'required dict', - ); - } - - protected function defineReturnType() { - return 'void'; - } - - protected function defineErrorTypes() { - return array( - 'ERR-BAD-DIFF' => pht('Bad diff ID.'), - 'ERR-BAD-LINTER' => pht('No postponed linter by the given name.'), - 'ERR-NO-LINT' => pht('No postponed lint field available in diff.'), - ); - } - - protected function execute(ConduitAPIRequest $request) { - $diff_id = $request->getValue('diffID'); - $linter_map = $request->getValue('linters'); - - $diff = id(new DifferentialDiffQuery()) - ->setViewer($request->getUser()) - ->withIDs(array($diff_id)) - ->executeOne(); - if (!$diff) { - throw new ConduitException('ERR-BAD-DIFF'); - } - - // Extract the finished linters and messages from the linter map. - $finished_linters = array_keys($linter_map); - $new_messages = array(); - foreach ($linter_map as $linter => $messages) { - $new_messages = array_merge($new_messages, $messages); - } - - // Load the postponed linters attached to this diff. - $postponed_linters_property = id( - new DifferentialDiffProperty())->loadOneWhere( - 'diffID = %d AND name = %s', - $diff_id, - 'arc:lint-postponed'); - if ($postponed_linters_property) { - $postponed_linters = $postponed_linters_property->getData(); - } else { - $postponed_linters = array(); - } - - foreach ($finished_linters as $linter) { - if (!in_array($linter, $postponed_linters)) { - throw new ConduitException('ERR-BAD-LINTER'); - } - } - - foreach ($postponed_linters as $idx => $linter) { - if (in_array($linter, $finished_linters)) { - unset($postponed_linters[$idx]); - } - } - - // Load the lint messages currenty attached to the diff. If this - // diff property doesn't exist, create it. - $messages_property = id(new DifferentialDiffProperty())->loadOneWhere( - 'diffID = %d AND name = %s', - $diff_id, - 'arc:lint'); - if ($messages_property) { - $messages = $messages_property->getData(); - } else { - $messages = array(); - } - - // Add new lint messages, removing duplicates. - foreach ($new_messages as $new_message) { - if (!in_array($new_message, $messages)) { - $messages[] = $new_message; - } - } - - // Use setdiffproperty to update the postponed linters and messages, - // as these will also update the lint status correctly. - $call = new ConduitCall( - 'differential.setdiffproperty', - array( - 'diff_id' => $diff_id, - 'name' => 'arc:lint', - 'data' => json_encode($messages), - )); - $call->setUser($request->getUser()); - $call->execute(); - $call = new ConduitCall( - 'differential.setdiffproperty', - array( - 'diff_id' => $diff_id, - 'name' => 'arc:lint-postponed', - 'data' => json_encode($postponed_linters), - )); - $call->setUser($request->getUser()); - $call->execute(); - } -} diff --git a/src/applications/differential/conduit/DifferentialSetDiffPropertyConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialSetDiffPropertyConduitAPIMethod.php index f9db11e3fb..12264ca598 100644 --- a/src/applications/differential/conduit/DifferentialSetDiffPropertyConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialSetDiffPropertyConduitAPIMethod.php @@ -29,72 +29,12 @@ final class DifferentialSetDiffPropertyConduitAPIMethod ); } - private static function updateLintStatus($diff_id) { - - $diff = id(new DifferentialDiff())->load($diff_id); - if (!$diff) { - throw new ConduitException('ERR_NOT_FOUND'); - } - - // Load the postponed linters attached to this diff. - $postponed_linters_property = id( - new DifferentialDiffProperty())->loadOneWhere( - 'diffID = %d AND name = %s', - $diff_id, - 'arc:lint-postponed'); - if ($postponed_linters_property) { - $postponed_linters = $postponed_linters_property->getData(); - } else { - $postponed_linters = array(); - } - - // Load the lint messages currenty attached to the diff - $messages_property = id(new DifferentialDiffProperty())->loadOneWhere( - 'diffID = %d AND name = %s', - $diff_id, - 'arc:lint'); - if ($messages_property) { - $results = $messages_property->getData(); - } else { - $results = array(); - } - - $has_error = false; - $has_warning = false; - foreach ($results as $result) { - if ($result['severity'] === ArcanistLintSeverity::SEVERITY_ERROR) { - $has_error = true; - break; - } else if ($result['severity'] === - ArcanistLintSeverity::SEVERITY_WARNING) { - $has_warning = true; - } - } - - if ($has_error) { - $diff->setLintStatus(DifferentialLintStatus::LINT_FAIL); - } else if ($has_warning) { - $diff->setLintStatus(DifferentialLintStatus::LINT_WARN); - } else if (!empty($postponed_linters)) { - $diff->setLintStatus(DifferentialLintStatus::LINT_POSTPONED); - } else if ($diff->getLintStatus() != DifferentialLintStatus::LINT_SKIP) { - $diff->setLintStatus(DifferentialLintStatus::LINT_OKAY); - } - $diff->save(); - } - protected function execute(ConduitAPIRequest $request) { $diff_id = $request->getValue('diff_id'); $name = $request->getValue('name'); $data = json_decode($request->getValue('data'), true); self::updateDiffProperty($diff_id, $name, $data); - - if ($name === 'arc:lint' || $name == 'arc:lint-postponed') { - self::updateLintStatus($diff_id); - } - - return; } private static function updateDiffProperty($diff_id, $name, $data) { diff --git a/src/applications/differential/conduit/DifferentialUpdateUnitResultsConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialUpdateUnitResultsConduitAPIMethod.php deleted file mode 100644 index 7b1a250666..0000000000 --- a/src/applications/differential/conduit/DifferentialUpdateUnitResultsConduitAPIMethod.php +++ /dev/null @@ -1,154 +0,0 @@ - 'required diff_id', - 'file' => 'required string', - 'name' => 'required string', - 'link' => 'optional string', - 'result' => 'required string', - 'message' => 'required string', - 'coverage' => 'optional map', - ); - } - - protected function defineReturnType() { - return 'void'; - } - - protected function defineErrorTypes() { - return array( - 'ERR_BAD_DIFF' => pht('Bad diff ID.'), - 'ERR_NO_RESULTS' => pht('Could not find the postponed test'), - ); - } - - protected function execute(ConduitAPIRequest $request) { - $diff_id = $request->getValue('diff_id'); - if (!$diff_id) { - throw new ConduitException('ERR_BAD_DIFF'); - } - - $file = $request->getValue('file'); - $name = $request->getValue('name'); - $link = $request->getValue('link'); - $message = $request->getValue('message'); - $result = $request->getValue('result'); - $coverage = $request->getValue('coverage', array()); - - $diff_property = id(new DifferentialDiffProperty())->loadOneWhere( - 'diffID = %d AND name = %s', - $diff_id, - 'arc:unit'); - - if (!$diff_property) { - throw new ConduitException('ERR_NO_RESULTS'); - } - - $diff = id(new DifferentialDiffQuery()) - ->setViewer($request->getUser()) - ->withIDs(array($diff_id)) - ->executeOne(); - - $unit_results = $diff_property->getData(); - $postponed_count = 0; - $unit_status = null; - - // If the test result already exists, then update it with - // the new info. - foreach ($unit_results as &$unit_result) { - if ($unit_result['name'] === $name || - $unit_result['name'] === $file || - $unit_result['name'] === $diff->getSourcePath().$file) { - $unit_result['name'] = $name; - $unit_result['link'] = $link; - $unit_result['file'] = $file; - $unit_result['result'] = $result; - $unit_result['userdata'] = $message; - $unit_result['coverage'] = $coverage; - $unit_status = $result; - break; - } - } - unset($unit_result); - - // If the test result doesn't exist, just add it. - if (!$unit_status) { - $unit_result = array(); - $unit_result['file'] = $file; - $unit_result['name'] = $name; - $unit_result['link'] = $link; - $unit_result['result'] = $result; - $unit_result['userdata'] = $message; - $unit_result['coverage'] = $coverage; - $unit_status = $result; - $unit_results[] = $unit_result; - } - unset($unit_result); - - $diff_property->setData($unit_results); - $diff_property->save(); - - // Map external unit test status to internal overall diff status - $status_codes = - array( - DifferentialUnitTestResult::RESULT_PASS => - DifferentialUnitStatus::UNIT_OKAY, - DifferentialUnitTestResult::RESULT_UNSOUND => - DifferentialUnitStatus::UNIT_WARN, - DifferentialUnitTestResult::RESULT_FAIL => - DifferentialUnitStatus::UNIT_FAIL, - DifferentialUnitTestResult::RESULT_BROKEN => - DifferentialUnitStatus::UNIT_FAIL, - DifferentialUnitTestResult::RESULT_SKIP => - DifferentialUnitStatus::UNIT_OKAY, - DifferentialUnitTestResult::RESULT_POSTPONED => - DifferentialUnitStatus::UNIT_POSTPONED, - ); - - // These are the relative priorities for the unit test results - $status_codes_priority = - array( - DifferentialUnitStatus::UNIT_OKAY => 1, - DifferentialUnitStatus::UNIT_WARN => 2, - DifferentialUnitStatus::UNIT_POSTPONED => 3, - DifferentialUnitStatus::UNIT_FAIL => 4, - ); - - // Walk the now-current list of status codes to find the overall diff - // status - $final_diff_status = DifferentialUnitStatus::UNIT_NONE; - foreach ($unit_results as $unit_result) { - // Convert the text result into a diff unit status value - $status_code = idx($status_codes, - $unit_result['result'], - DifferentialUnitStatus::UNIT_NONE); - - // Convert the unit status into a relative value - $diff_status_priority = idx($status_codes_priority, $status_code, 0); - - // If the relative value of this result is "more bad" than previous - // results, use it as the new final diff status - if ($diff_status_priority > idx($status_codes_priority, - $final_diff_status, 0)) { - $final_diff_status = $status_code; - } - } - - // Update our unit test result status with the final value - $diff->setUnitStatus($final_diff_status); - $diff->save(); - } - -} diff --git a/src/applications/differential/controller/DifferentialChangesetViewController.php b/src/applications/differential/controller/DifferentialChangesetViewController.php index 2373d3ef2c..a73875c8f8 100644 --- a/src/applications/differential/controller/DifferentialChangesetViewController.php +++ b/src/applications/differential/controller/DifferentialChangesetViewController.php @@ -127,29 +127,32 @@ final class DifferentialChangesetViewController extends DifferentialController { $changeset = $choice; } - $coverage = null; - if ($right && $right->getDiffID()) { - $unit = id(new DifferentialDiffProperty())->loadOneWhere( - 'diffID = %d AND name = %s', - $right->getDiffID(), - 'arc:unit'); - - if ($unit) { - $coverage = array(); - foreach ($unit->getData() as $result) { - $result_coverage = idx($result, 'coverage'); - if (!$result_coverage) { - continue; - } - $file_coverage = idx($result_coverage, $right->getFileName()); - if (!$file_coverage) { - continue; - } - $coverage[] = $file_coverage; - } - - $coverage = ArcanistUnitTestResult::mergeCoverage($coverage); + if ($left_new || $right_new) { + $diff_map = array(); + if ($left) { + $diff_map[] = $left->getDiff(); } + if ($right) { + $diff_map[] = $right->getDiff(); + } + $diff_map = mpull($diff_map, null, 'getPHID'); + + $buildables = id(new HarbormasterBuildableQuery()) + ->setViewer($viewer) + ->withBuildablePHIDs(array_keys($diff_map)) + ->withManualBuildables(false) + ->needBuilds(true) + ->needTargets(true) + ->execute(); + $buildables = mpull($buildables, null, 'getBuildablePHID'); + foreach ($diff_map as $diff_phid => $changeset_diff) { + $changeset_diff->attachBuildable(idx($buildables, $diff_phid)); + } + } + + $coverage = null; + if ($right_new) { + $coverage = $this->loadCoverage($right); } $spec = $request->getStr('range'); @@ -248,15 +251,19 @@ final class DifferentialChangesetViewController extends DifferentialController { ->setMask($mask); if ($request->isAjax()) { + // NOTE: We must render the changeset before we render coverage + // information, since it builds some caches. + $rendered_changeset = $parser->renderChangeset(); + $mcov = $parser->renderModifiedCoverage(); - $coverage = array( + $coverage_data = array( 'differential-mcoverage-'.md5($changeset->getFilename()) => $mcov, ); return id(new PhabricatorChangesetResponse()) - ->setRenderedChangeset($parser->renderChangeset()) - ->setCoverage($coverage) + ->setRenderedChangeset($rendered_changeset) + ->setCoverage($coverage_data) ->setUndoTemplates($parser->getRenderer()->renderUndoTemplates()); } @@ -356,33 +363,62 @@ final class DifferentialChangesetViewController extends DifferentialController { } private function buildLintInlineComments($changeset) { - $lint = id(new DifferentialDiffProperty())->loadOneWhere( - 'diffID = %d AND name = %s', - $changeset->getDiffID(), - 'arc:lint'); - if (!$lint) { + $diff = $changeset->getDiff(); + + $target_phids = $diff->getBuildTargetPHIDs(); + if (!$target_phids) { return array(); } - $lint = $lint->getData(); + + $messages = id(new HarbormasterBuildLintMessage())->loadAllWhere( + 'buildTargetPHID IN (%Ls) AND path = %s', + $target_phids, + $changeset->getFilename()); + + if (!$messages) { + return array(); + } + + $template = id(new DifferentialInlineComment()) + ->setChangesetID($changeset->getID()) + ->setIsNewFile(1) + ->setLineLength(0); $inlines = array(); - foreach ($lint as $msg) { - if ($msg['path'] != $changeset->getFilename()) { - continue; - } - $inline = new DifferentialInlineComment(); - $inline->setChangesetID($changeset->getID()); - $inline->setIsNewFile(1); - $inline->setSyntheticAuthor(pht('Lint: %s', $msg['name'])); - $inline->setLineNumber($msg['line']); - $inline->setLineLength(0); + foreach ($messages as $message) { + $description = $message->getProperty('description'); + $description = '%%%'.$description.'%%%'; - $inline->setContent('%%%'.$msg['description'].'%%%'); - - $inlines[] = $inline; + $inlines[] = id(clone $template) + ->setSyntheticAuthor(pht('Lint: %s', $message->getName())) + ->setLineNumber($message->getLine()) + ->setContent($description); } return $inlines; } + private function loadCoverage(DifferentialChangeset $changeset) { + $target_phids = $changeset->getDiff()->getBuildTargetPHIDs(); + if (!$target_phids) { + return array(); + } + + $unit = id(new HarbormasterBuildUnitMessage())->loadAllWhere( + 'buildTargetPHID IN (%Ls)', + $target_phids); + + $coverage = array(); + foreach ($unit as $message) { + $test_coverage = $message->getProperty('coverage', array()); + $coverage_data = idx($test_coverage, $changeset->getFileName()); + if (!strlen($coverage_data)) { + continue; + } + $coverage[] = $coverage_data; + } + + return ArcanistUnitTestResult::mergeCoverage($coverage); + } + } diff --git a/src/applications/differential/controller/DifferentialDiffViewController.php b/src/applications/differential/controller/DifferentialDiffViewController.php index d095465604..28e7c2fc54 100644 --- a/src/applications/differential/controller/DifferentialDiffViewController.php +++ b/src/applications/differential/controller/DifferentialDiffViewController.php @@ -29,6 +29,17 @@ final class DifferentialDiffViewController extends DifferentialController { ->setURI('/D'.$diff->getRevisionID().'?id='.$diff->getID()); } + $diff_phid = $diff->getPHID(); + $buildables = id(new HarbormasterBuildableQuery()) + ->setViewer($viewer) + ->withBuildablePHIDs(array($diff_phid)) + ->withManualBuildables(false) + ->needBuilds(true) + ->needTargets(true) + ->execute(); + $buildables = mpull($buildables, null, 'getBuildablePHID'); + $diff->attachBuildable(idx($buildables, $diff_phid)); + // TODO: implement optgroup support in AphrontFormSelectControl? $select = array(); $select[] = hsprintf('', pht('Create New Revision')); @@ -108,7 +119,7 @@ final class DifferentialDiffViewController extends DifferentialController { $table_of_contents = id(new DifferentialDiffTableOfContentsView()) ->setChangesets($changesets) ->setVisibleChangesets($changesets) - ->setUnitTestData(idx($props, 'arc:unit', array())); + ->setCoverageMap($diff->loadCoverageMap($viewer)); $refs = array(); foreach ($changesets as $changeset) { diff --git a/src/applications/differential/controller/DifferentialRevisionViewController.php b/src/applications/differential/controller/DifferentialRevisionViewController.php index 94d9db0716..085e888fe5 100644 --- a/src/applications/differential/controller/DifferentialRevisionViewController.php +++ b/src/applications/differential/controller/DifferentialRevisionViewController.php @@ -191,16 +191,6 @@ final class DifferentialRevisionViewController extends DifferentialController { $visible_changesets[$changeset_id] = $changesets[$changeset_id]; } } - - if (!empty($props['arc:lint'])) { - $changeset_paths = mpull($changesets, null, 'getFilename'); - foreach ($props['arc:lint'] as $lint) { - $changeset = idx($changeset_paths, $lint['path']); - if ($changeset) { - $visible_changesets[$changeset->getID()] = $changeset; - } - } - } } else { $warning = null; $visible_changesets = $changesets; @@ -363,7 +353,7 @@ final class DifferentialRevisionViewController extends DifferentialController { $toc_view->setChangesets($changesets); $toc_view->setVisibleChangesets($visible_changesets); $toc_view->setRenderingReferences($rendering_references); - $toc_view->setUnitTestData(idx($props, 'arc:unit', array())); + $toc_view->setCoverageMap($target->loadCoverageMap($user)); if ($repository) { $toc_view->setRepository($repository); } diff --git a/src/applications/differential/customfield/DifferentialHarbormasterField.php b/src/applications/differential/customfield/DifferentialHarbormasterField.php index f84bb74f29..c9b573bda2 100644 --- a/src/applications/differential/customfield/DifferentialHarbormasterField.php +++ b/src/applications/differential/customfield/DifferentialHarbormasterField.php @@ -29,20 +29,11 @@ abstract class DifferentialHarbormasterField $diff->attachProperty($key, idx($properties, $key)); } - $messages = array(); - - $buildable = $diff->getBuildable(); - if ($buildable) { - $target_phids = array(); - foreach ($buildable->getBuilds() as $build) { - foreach ($build->getBuildTargets() as $target) { - $target_phids[] = $target->getPHID(); - } - } - - if ($target_phids) { - $messages = $this->loadHarbormasterTargetMessages($target_phids); - } + $target_phids = $diff->getBuildTargetPHIDs(); + if ($target_phids) { + $messages = $this->loadHarbormasterTargetMessages($target_phids); + } else { + $messages = array(); } if (!$messages) { diff --git a/src/applications/differential/landing/DifferentialLandingActionMenuEventListener.php b/src/applications/differential/landing/DifferentialLandingActionMenuEventListener.php index 32dda41932..ad713db943 100644 --- a/src/applications/differential/landing/DifferentialLandingActionMenuEventListener.php +++ b/src/applications/differential/landing/DifferentialLandingActionMenuEventListener.php @@ -37,9 +37,10 @@ final class DifferentialLandingActionMenuEventListener return null; } - $strategies = id(new PhutilSymbolLoader()) + $strategies = id(new PhutilClassMapQuery()) ->setAncestorClass('DifferentialLandingStrategy') - ->loadObjects(); + ->execute(); + foreach ($strategies as $strategy) { $viewer = $event->getUser(); $action = $strategy->createMenuItem($viewer, $revision, $repository); diff --git a/src/applications/differential/parser/DifferentialHunkParser.php b/src/applications/differential/parser/DifferentialHunkParser.php index 8903c85f93..22b82e002e 100644 --- a/src/applications/differential/parser/DifferentialHunkParser.php +++ b/src/applications/differential/parser/DifferentialHunkParser.php @@ -603,9 +603,9 @@ final class DifferentialHunkParser extends Phobject { $start = $start - $add_context; $end = $end + $add_context; $hunk_content = array(); - $hunk_pos = array( '-' => 0, '+' => 0 ); - $hunk_offset = array( '-' => null, '+' => null ); - $hunk_last = array( '-' => null, '+' => null ); + $hunk_pos = array('-' => 0, '+' => 0); + $hunk_offset = array('-' => null, '+' => null); + $hunk_last = array('-' => null, '+' => null); foreach (explode("\n", $hunk->getChanges()) as $line) { $in_common = strncmp($line, ' ', 1) === 0; $in_old = strncmp($line, '-', 1) === 0 || $in_common; diff --git a/src/applications/differential/storage/DifferentialDiff.php b/src/applications/differential/storage/DifferentialDiff.php index 4a0e63d5a7..e48ed5630d 100644 --- a/src/applications/differential/storage/DifferentialDiff.php +++ b/src/applications/differential/storage/DifferentialDiff.php @@ -331,6 +331,47 @@ final class DifferentialDiff return $this->assertAttached($this->buildable); } + public function getBuildTargetPHIDs() { + $buildable = $this->getBuildable(); + + if (!$buildable) { + return array(); + } + + $target_phids = array(); + foreach ($buildable->getBuilds() as $build) { + foreach ($build->getBuildTargets() as $target) { + $target_phids[] = $target->getPHID(); + } + } + + return $target_phids; + } + + public function loadCoverageMap(PhabricatorUser $viewer) { + $target_phids = $this->getBuildTargetPHIDs(); + if (!$target_phids) { + return array(); + } + + $unit = id(new HarbormasterBuildUnitMessage())->loadAllWhere( + 'buildTargetPHID IN (%Ls)', + $target_phids); + + $map = array(); + foreach ($unit as $message) { + $coverage = $message->getProperty('coverage', array()); + foreach ($coverage as $path => $coverage_data) { + $map[$path][] = $coverage_data; + } + } + + foreach ($map as $path => $coverage_items) { + $map[$path] = ArcanistUnitTestResult::mergeCoverage($coverage_items); + } + + return $map; + } /* -( PhabricatorPolicyInterface )----------------------------------------- */ diff --git a/src/applications/differential/view/DifferentialDiffTableOfContentsView.php b/src/applications/differential/view/DifferentialDiffTableOfContentsView.php index f20c7ce19b..26161096fe 100644 --- a/src/applications/differential/view/DifferentialDiffTableOfContentsView.php +++ b/src/applications/differential/view/DifferentialDiffTableOfContentsView.php @@ -10,7 +10,7 @@ final class DifferentialDiffTableOfContentsView extends AphrontView { private $renderURI = '/differential/changeset/'; private $revisionID; private $whitespace; - private $unitTestData; + private $coverageMap; public function setChangesets($changesets) { $this->changesets = $changesets; @@ -37,8 +37,8 @@ final class DifferentialDiffTableOfContentsView extends AphrontView { return $this; } - public function setUnitTestData($unit_test_data) { - $this->unitTestData = $unit_test_data; + public function setCoverageMap(array $coverage_map) { + $this->coverageMap = $coverage_map; return $this; } @@ -60,23 +60,6 @@ final class DifferentialDiffTableOfContentsView extends AphrontView { $rows = array(); - $coverage = array(); - if ($this->unitTestData) { - $coverage_by_file = array(); - foreach ($this->unitTestData as $result) { - $test_coverage = idx($result, 'coverage'); - if (!$test_coverage) { - continue; - } - foreach ($test_coverage as $file => $results) { - $coverage_by_file[$file][] = $results; - } - } - foreach ($coverage_by_file as $file => $coverages) { - $coverage[$file] = ArcanistUnitTestResult::mergeCoverage($coverages); - } - } - $changesets = $this->changesets; $paths = array(); foreach ($changesets as $id => $changeset) { @@ -144,7 +127,7 @@ final class DifferentialDiffTableOfContentsView extends AphrontView { 'M'); $fname = $changeset->getFilename(); - $cov = $this->renderCoverage($coverage, $fname); + $cov = $this->renderCoverage($this->coverageMap, $fname); if ($cov === null) { $mcov = $cov = phutil_tag('em', array(), '-'); } else { diff --git a/src/applications/diffusion/controller/DiffusionSymbolController.php b/src/applications/diffusion/controller/DiffusionSymbolController.php index 9011003fc7..131e8774bd 100644 --- a/src/applications/diffusion/controller/DiffusionSymbolController.php +++ b/src/applications/diffusion/controller/DiffusionSymbolController.php @@ -64,9 +64,10 @@ final class DiffusionSymbolController extends DiffusionController { $external_query->withLanguages(array($request->getStr('lang'))); } - $external_sources = id(new PhutilSymbolLoader()) + $external_sources = id(new PhutilClassMapQuery()) ->setAncestorClass('DiffusionExternalSymbolsSource') - ->loadObjects(); + ->execute(); + $results = array($symbols); foreach ($external_sources as $source) { $results[] = $source->executeQuery($external_query); diff --git a/src/applications/diviner/workflow/DivinerGenerateWorkflow.php b/src/applications/diviner/workflow/DivinerGenerateWorkflow.php index a3847391c5..78f43a826f 100644 --- a/src/applications/diviner/workflow/DivinerGenerateWorkflow.php +++ b/src/applications/diviner/workflow/DivinerGenerateWorkflow.php @@ -414,10 +414,9 @@ final class DivinerGenerateWorkflow extends DivinerWorkflow { $version['atom'] = DivinerAtom::getAtomSerializationVersion(); $version['rules'] = $this->getRules(); - $atomizers = id(new PhutilSymbolLoader()) + $atomizers = id(new PhutilClassMapQuery()) ->setAncestorClass('DivinerAtomizer') - ->setConcreteOnly(true) - ->selectAndLoadSymbols(); + ->execute(); $atomizer_versions = array(); foreach ($atomizers as $atomizer) { diff --git a/src/applications/doorkeeper/controller/DoorkeeperTagsController.php b/src/applications/doorkeeper/controller/DoorkeeperTagsController.php index 12ff8e72c5..9957b82026 100644 --- a/src/applications/doorkeeper/controller/DoorkeeperTagsController.php +++ b/src/applications/doorkeeper/controller/DoorkeeperTagsController.php @@ -2,9 +2,8 @@ final class DoorkeeperTagsController extends PhabricatorController { - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); $tags = $request->getStr('tags'); try { diff --git a/src/applications/doorkeeper/engine/DoorkeeperImportEngine.php b/src/applications/doorkeeper/engine/DoorkeeperImportEngine.php index bbbef3191b..e4e55b2528 100644 --- a/src/applications/doorkeeper/engine/DoorkeeperImportEngine.php +++ b/src/applications/doorkeeper/engine/DoorkeeperImportEngine.php @@ -88,14 +88,12 @@ final class DoorkeeperImportEngine extends Phobject { } if (!$this->localOnly) { - $bridges = id(new PhutilSymbolLoader()) + $bridges = id(new PhutilClassMapQuery()) ->setAncestorClass('DoorkeeperBridge') - ->loadObjects(); + ->setFilterMethod('isEnabled') + ->execute(); foreach ($bridges as $key => $bridge) { - if (!$bridge->isEnabled()) { - unset($bridges[$key]); - } $bridge->setViewer($viewer); $bridge->setThrowOnMissingLink($this->throwOnMissingLink); } diff --git a/src/applications/doorkeeper/option/PhabricatorAsanaConfigOptions.php b/src/applications/doorkeeper/option/PhabricatorAsanaConfigOptions.php index 5adf6725e9..f0cdb5a819 100644 --- a/src/applications/doorkeeper/option/PhabricatorAsanaConfigOptions.php +++ b/src/applications/doorkeeper/option/PhabricatorAsanaConfigOptions.php @@ -120,9 +120,9 @@ final class PhabricatorAsanaConfigOptions $viewer = $request->getUser(); - $publishers = id(new PhutilSymbolLoader()) + $publishers = id(new PhutilClassMapQuery()) ->setAncestorClass('DoorkeeperFeedStoryPublisher') - ->loadObjects(); + ->execute(); $out = array(); $out[] = pht( diff --git a/src/applications/doorkeeper/worker/DoorkeeperFeedWorker.php b/src/applications/doorkeeper/worker/DoorkeeperFeedWorker.php index d015b19791..404ee348f7 100644 --- a/src/applications/doorkeeper/worker/DoorkeeperFeedWorker.php +++ b/src/applications/doorkeeper/worker/DoorkeeperFeedWorker.php @@ -125,9 +125,9 @@ abstract class DoorkeeperFeedWorker extends FeedPushWorker { $viewer = $this->getViewer(); $object = $this->getStoryObject(); - $publishers = id(new PhutilSymbolLoader()) + $publishers = id(new PhutilClassMapQuery()) ->setAncestorClass('DoorkeeperFeedStoryPublisher') - ->loadObjects(); + ->execute(); foreach ($publishers as $publisher) { if (!$publisher->canPublishStory($story, $object)) { diff --git a/src/applications/feed/worker/FeedPublisherWorker.php b/src/applications/feed/worker/FeedPublisherWorker.php index 0fc8171a49..c2aeed0f3d 100644 --- a/src/applications/feed/worker/FeedPublisherWorker.php +++ b/src/applications/feed/worker/FeedPublisherWorker.php @@ -23,6 +23,7 @@ final class FeedPublisherWorker extends FeedPushWorker { ); // Find and schedule all the enabled Doorkeeper publishers. + // TODO: Use PhutilClassMapQuery? $doorkeeper_workers = id(new PhutilSymbolLoader()) ->setAncestorClass('DoorkeeperFeedWorker') ->loadObjects($argv); diff --git a/src/applications/files/storage/PhabricatorFile.php b/src/applications/files/storage/PhabricatorFile.php index 6a71c9aeac..4ca26fc03b 100644 --- a/src/applications/files/storage/PhabricatorFile.php +++ b/src/applications/files/storage/PhabricatorFile.php @@ -882,18 +882,9 @@ final class PhabricatorFile extends PhabricatorFileDAO } public static function buildAllEngines() { - $engines = id(new PhutilSymbolLoader()) - ->setType('class') - ->setConcreteOnly(true) + return id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorFileStorageEngine') - ->selectAndLoadSymbols(); - - $results = array(); - foreach ($engines as $engine_class) { - $results[] = newv($engine_class['name'], array()); - } - - return $results; + ->execute(); } public function getViewableMimeType() { diff --git a/src/applications/flag/query/PhabricatorFlagSearchEngine.php b/src/applications/flag/query/PhabricatorFlagSearchEngine.php index 7cea7ac42d..a433e4241c 100644 --- a/src/applications/flag/query/PhabricatorFlagSearchEngine.php +++ b/src/applications/flag/query/PhabricatorFlagSearchEngine.php @@ -100,11 +100,13 @@ final class PhabricatorFlagSearchEngine } private function getObjectFilterOptions() { - $objects = id(new PhutilSymbolLoader()) + $objects = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorFlaggableInterface') - ->loadObjects(); + ->execute(); + $all_types = PhabricatorPHIDType::getAllTypes(); $options = array(); + foreach ($objects as $object) { $phid = $object->generatePHID(); $phid_type = phid_get_type($phid); diff --git a/src/applications/harbormaster/controller/HarbormasterBuildViewController.php b/src/applications/harbormaster/controller/HarbormasterBuildViewController.php index 2b5d2c7222..cdc5baec60 100644 --- a/src/applications/harbormaster/controller/HarbormasterBuildViewController.php +++ b/src/applications/harbormaster/controller/HarbormasterBuildViewController.php @@ -172,34 +172,26 @@ final class HarbormasterBuildViewController } $details = $build_target->getDetails(); - if ($details) { - $properties = new PHUIPropertyListView(); - foreach ($details as $key => $value) { - $properties->addProperty($key, $value); - } - $target_box->addPropertyList($properties, pht('Configuration')); + $properties = new PHUIPropertyListView(); + foreach ($details as $key => $value) { + $properties->addProperty($key, $value); } + $target_box->addPropertyList($properties, pht('Configuration')); $variables = $build_target->getVariables(); - if ($variables) { - $properties = new PHUIPropertyListView(); - $properties->addRawContent($this->buildProperties($variables)); - $target_box->addPropertyList($properties, pht('Variables')); - } + $properties = new PHUIPropertyListView(); + $properties->addRawContent($this->buildProperties($variables)); + $target_box->addPropertyList($properties, pht('Variables')); $artifacts = $this->buildArtifacts($build_target); - if ($artifacts) { - $properties = new PHUIPropertyListView(); - $properties->addRawContent($artifacts); - $target_box->addPropertyList($properties, pht('Artifacts')); - } + $properties = new PHUIPropertyListView(); + $properties->addRawContent($artifacts); + $target_box->addPropertyList($properties, pht('Artifacts')); $build_messages = idx($messages, $build_target->getPHID(), array()); - if ($build_messages) { - $properties = new PHUIPropertyListView(); - $properties->addRawContent($this->buildMessages($build_messages)); - $target_box->addPropertyList($properties, pht('Messages')); - } + $properties = new PHUIPropertyListView(); + $properties->addRawContent($this->buildMessages($build_messages)); + $target_box->addPropertyList($properties, pht('Messages')); $properties = new PHUIPropertyListView(); $properties->addProperty( @@ -243,11 +235,8 @@ final class HarbormasterBuildViewController ->withBuildTargetPHIDs(array($build_target->getPHID())) ->execute(); - if (count($artifacts) === 0) { - return null; - } - $list = id(new PHUIObjectItemListView()) + ->setNoDataString(pht('This target has no associated artifacts.')) ->setFlush(true); foreach ($artifacts as $artifact) { diff --git a/src/applications/harbormaster/controller/HarbormasterPlanRunController.php b/src/applications/harbormaster/controller/HarbormasterPlanRunController.php index 0f5673087d..980c7fad4b 100644 --- a/src/applications/harbormaster/controller/HarbormasterPlanRunController.php +++ b/src/applications/harbormaster/controller/HarbormasterPlanRunController.php @@ -22,6 +22,15 @@ final class HarbormasterPlanRunController extends HarbormasterController { return new Aphront404Response(); } + $cancel_uri = $this->getApplicationURI("plan/{$plan_id}/"); + + if (!$plan->canRunManually()) { + return $this->newDialog() + ->setTitle(pht('Can Not Run Plan')) + ->appendParagraph(pht('This plan can not be run manually.')) + ->addCancelButton($cancel_uri); + } + $e_name = true; $v_name = null; @@ -65,7 +74,6 @@ final class HarbormasterPlanRunController extends HarbormasterController { } $title = pht('Run Build Plan Manually'); - $cancel_uri = $this->getApplicationURI("plan/{$plan_id}/"); $save_button = pht('Run Plan Manually'); $form = id(new PHUIFormLayoutView()) diff --git a/src/applications/harbormaster/controller/HarbormasterPlanViewController.php b/src/applications/harbormaster/controller/HarbormasterPlanViewController.php index da35e389a0..95db4c52c0 100644 --- a/src/applications/harbormaster/controller/HarbormasterPlanViewController.php +++ b/src/applications/harbormaster/controller/HarbormasterPlanViewController.php @@ -289,12 +289,14 @@ final class HarbormasterPlanViewController extends HarbormasterPlanController { ->setIcon('fa-ban')); } + $can_run = ($has_manage && $plan->canRunManually()); + $list->addAction( id(new PhabricatorActionView()) ->setName(pht('Run Plan Manually')) ->setHref($this->getApplicationURI("plan/run/{$id}/")) ->setWorkflow(true) - ->setDisabled(!$has_manage) + ->setDisabled(!$can_run) ->setIcon('fa-play-circle')); return $list; diff --git a/src/applications/harbormaster/management/HarbormasterManagementBuildWorkflow.php b/src/applications/harbormaster/management/HarbormasterManagementBuildWorkflow.php index 86b8596fe3..fc0f670633 100644 --- a/src/applications/harbormaster/management/HarbormasterManagementBuildWorkflow.php +++ b/src/applications/harbormaster/management/HarbormasterManagementBuildWorkflow.php @@ -64,6 +64,11 @@ final class HarbormasterManagementBuildWorkflow pht('Build plan "%s" does not exist.', $plan_id)); } + if (!$plan->canRunManually()) { + throw new PhutilArgumentUsageException( + pht('This build plan can not be run manually.')); + } + $console = PhutilConsole::getConsole(); $buildable = HarbormasterBuildable::initializeNewBuildable($viewer) diff --git a/src/applications/harbormaster/phid/HarbormasterBuildPHIDType.php b/src/applications/harbormaster/phid/HarbormasterBuildPHIDType.php index 5ca684a098..5669df4ee9 100644 --- a/src/applications/harbormaster/phid/HarbormasterBuildPHIDType.php +++ b/src/applications/harbormaster/phid/HarbormasterBuildPHIDType.php @@ -27,12 +27,11 @@ final class HarbormasterBuildPHIDType extends PhabricatorPHIDType { foreach ($handles as $phid => $handle) { $build = $objects[$phid]; - $handles[$phid]->setName(pht( - 'Build %d: %s', - $build->getID(), - $build->getName())); - $handles[$phid]->setURI( - '/harbormaster/build/'.$build->getID()); + $build_id = $build->getID(); + $name = $build->getName(); + + $handle->setName(pht('Build %d: %s', $build_id, $name)); + $handle->setURI("/harbormaster/build/{$build_id}/"); } } diff --git a/src/applications/harbormaster/phid/HarbormasterBuildTargetPHIDType.php b/src/applications/harbormaster/phid/HarbormasterBuildTargetPHIDType.php index 8aa69abad6..e16db13ccb 100644 --- a/src/applications/harbormaster/phid/HarbormasterBuildTargetPHIDType.php +++ b/src/applications/harbormaster/phid/HarbormasterBuildTargetPHIDType.php @@ -26,7 +26,17 @@ final class HarbormasterBuildTargetPHIDType extends PhabricatorPHIDType { array $objects) { foreach ($handles as $phid => $handle) { - $build_target = $objects[$phid]; + $target = $objects[$phid]; + $target_id = $target->getID(); + + // Build target don't currently have their own page, so just point + // the user at the build until we have one. + $build = $target->getBuild(); + $build_id = $build->getID(); + $uri = "/harbormaster/build/{$build_id}/"; + + $handle->setName(pht('Build Target %d', $target_id)); + $handle->setURI($uri); } } diff --git a/src/applications/harbormaster/storage/build/HarbormasterBuild.php b/src/applications/harbormaster/storage/build/HarbormasterBuild.php index 8d1128d009..5ea9f2f07b 100644 --- a/src/applications/harbormaster/storage/build/HarbormasterBuild.php +++ b/src/applications/harbormaster/storage/build/HarbormasterBuild.php @@ -291,9 +291,9 @@ final class HarbormasterBuild extends HarbormasterDAO } public static function getAvailableBuildVariables() { - $objects = id(new PhutilSymbolLoader()) + $objects = id(new PhutilClassMapQuery()) ->setAncestorClass('HarbormasterBuildableInterface') - ->loadObjects(); + ->execute(); $variables = array(); $variables[] = array( diff --git a/src/applications/harbormaster/storage/configuration/HarbormasterBuildPlan.php b/src/applications/harbormaster/storage/configuration/HarbormasterBuildPlan.php index 7b15b0034c..cf3d9ee7d7 100644 --- a/src/applications/harbormaster/storage/configuration/HarbormasterBuildPlan.php +++ b/src/applications/harbormaster/storage/configuration/HarbormasterBuildPlan.php @@ -85,6 +85,15 @@ final class HarbormasterBuildPlan extends HarbormasterDAO } + public function canRunManually() { + if ($this->isAutoplan()) { + return false; + } + + return true; + } + + public function getName() { $autoplan = $this->getAutoplan(); if ($autoplan) { diff --git a/src/applications/harbormaster/worker/HarbormasterBuildWorker.php b/src/applications/harbormaster/worker/HarbormasterBuildWorker.php index c71563a8a5..67eb6b3e78 100644 --- a/src/applications/harbormaster/worker/HarbormasterBuildWorker.php +++ b/src/applications/harbormaster/worker/HarbormasterBuildWorker.php @@ -5,11 +5,31 @@ */ final class HarbormasterBuildWorker extends HarbormasterWorker { + public function renderForDisplay(PhabricatorUser $viewer) { + try { + $build = $this->loadBuild(); + } catch (Exception $ex) { + return null; + } + + return $viewer->renderHandle($build->getPHID()); + } + protected function doWork() { + $viewer = $this->getViewer(); + $build = $this->loadBuild(); + + id(new HarbormasterBuildEngine()) + ->setViewer($viewer) + ->setBuild($build) + ->continueBuild(); + } + + private function loadBuild() { $data = $this->getTaskData(); $id = idx($data, 'buildID'); - $viewer = $this->getViewer(); + $viewer = $this->getViewer(); $build = id(new HarbormasterBuildQuery()) ->setViewer($viewer) ->withIDs(array($id)) @@ -19,10 +39,7 @@ final class HarbormasterBuildWorker extends HarbormasterWorker { pht('Invalid build ID "%s".', $id)); } - id(new HarbormasterBuildEngine()) - ->setViewer($viewer) - ->setBuild($build) - ->continueBuild(); + return $build; } } diff --git a/src/applications/harbormaster/worker/HarbormasterTargetWorker.php b/src/applications/harbormaster/worker/HarbormasterTargetWorker.php index a13a9de163..db355b145c 100644 --- a/src/applications/harbormaster/worker/HarbormasterTargetWorker.php +++ b/src/applications/harbormaster/worker/HarbormasterTargetWorker.php @@ -11,6 +11,16 @@ final class HarbormasterTargetWorker extends HarbormasterWorker { return phutil_units('24 hours in seconds'); } + public function renderForDisplay(PhabricatorUser $viewer) { + try { + $target = $this->loadBuildTarget(); + } catch (Exception $ex) { + return null; + } + + return $viewer->renderHandle($target->getPHID()); + } + private function loadBuildTarget() { $data = $this->getTaskData(); $id = idx($data, 'targetID'); diff --git a/src/applications/herald/controller/HeraldTestConsoleController.php b/src/applications/herald/controller/HeraldTestConsoleController.php index a7741ba3ce..9b5091664f 100644 --- a/src/applications/herald/controller/HeraldTestConsoleController.php +++ b/src/applications/herald/controller/HeraldTestConsoleController.php @@ -45,6 +45,9 @@ final class HeraldTestConsoleController extends HeraldController { } else if ($object instanceof PhrictionDocument) { $adapter = id(new PhrictionDocumentHeraldAdapter()) ->setDocument($object); + } else if ($object instanceof PonderQuestion) { + $adapter = id(new HeraldPonderQuestionAdapter()) + ->setQuestion($object); } else { throw new Exception(pht('Can not build adapter for object!')); } diff --git a/src/applications/home/controller/PhabricatorHomeMainController.php b/src/applications/home/controller/PhabricatorHomeMainController.php index 2442d69bd8..6307374c7f 100644 --- a/src/applications/home/controller/PhabricatorHomeMainController.php +++ b/src/applications/home/controller/PhabricatorHomeMainController.php @@ -219,7 +219,7 @@ final class PhabricatorHomeMainController extends PhabricatorHomeController { $revisions = $revision_query->execute(); - list($blocking, $active, ) = DifferentialRevisionQuery::splitResponsible( + list($blocking, $active,) = DifferentialRevisionQuery::splitResponsible( $revisions, array($user_phid)); diff --git a/src/applications/lipsum/management/PhabricatorLipsumGenerateWorkflow.php b/src/applications/lipsum/management/PhabricatorLipsumGenerateWorkflow.php index a8050a9940..ca0b63e263 100644 --- a/src/applications/lipsum/management/PhabricatorLipsumGenerateWorkflow.php +++ b/src/applications/lipsum/management/PhabricatorLipsumGenerateWorkflow.php @@ -20,9 +20,9 @@ final class PhabricatorLipsumGenerateWorkflow public function execute(PhutilArgumentParser $args) { $console = PhutilConsole::getConsole(); - $supported_types = id(new PhutilSymbolLoader()) + $supported_types = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorTestDataGenerator') - ->loadObjects(); + ->execute(); $console->writeOut( "%s:\n\t%s\n", diff --git a/src/applications/meta/controller/PhabricatorApplicationUninstallController.php b/src/applications/meta/controller/PhabricatorApplicationUninstallController.php index ae1c5f8748..68518413bf 100644 --- a/src/applications/meta/controller/PhabricatorApplicationUninstallController.php +++ b/src/applications/meta/controller/PhabricatorApplicationUninstallController.php @@ -6,17 +6,13 @@ final class PhabricatorApplicationUninstallController private $application; private $action; - public function willProcessRequest(array $data) { - $this->application = $data['application']; - $this->action = $data['action']; - } - - public function processRequest() { - $request = $this->getRequest(); - $user = $request->getUser(); + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); + $this->action = $request->getURIData('action'); + $this->application = $request->getURIData('application'); $selected = id(new PhabricatorApplicationQuery()) - ->setViewer($user) + ->setViewer($viewer) ->withClasses(array($this->application)) ->requireCapabilities( array( @@ -35,7 +31,7 @@ final class PhabricatorApplicationUninstallController 'phabricator.show-prototypes'); $dialog = id(new AphrontDialogView()) - ->setUser($user) + ->setUser($viewer) ->addCancelButton($view_uri); if ($selected->isPrototype() && !$prototypes_enabled) { @@ -118,7 +114,7 @@ final class PhabricatorApplicationUninstallController } PhabricatorConfigEditor::storeNewValue( - $this->getRequest()->getUser(), + $this->getViewer(), $config_entry, $list, PhabricatorContentSource::newFromRequest($this->getRequest())); diff --git a/src/applications/meta/controller/PhabricatorApplicationsListController.php b/src/applications/meta/controller/PhabricatorApplicationsListController.php index f6aa650334..cbbbbcf97f 100644 --- a/src/applications/meta/controller/PhabricatorApplicationsListController.php +++ b/src/applications/meta/controller/PhabricatorApplicationsListController.php @@ -3,19 +3,13 @@ final class PhabricatorApplicationsListController extends PhabricatorApplicationsController { - private $queryKey; - public function shouldAllowPublic() { return true; } - public function willProcessRequest(array $data) { - $this->queryKey = idx($data, 'queryKey'); - } - - public function processRequest() { + public function handleRequest(AphrontRequest $request) { $controller = id(new PhabricatorApplicationSearchController()) - ->setQueryKey($this->queryKey) + ->setQueryKey($request->getURIData('queryKey')) ->setSearchEngine(new PhabricatorAppSearchEngine()) ->setNavigation($this->buildSideNavView()); diff --git a/src/applications/metamta/PhabricatorMetaMTAWorker.php b/src/applications/metamta/PhabricatorMetaMTAWorker.php index e600bb3dc5..af655f4e0f 100644 --- a/src/applications/metamta/PhabricatorMetaMTAWorker.php +++ b/src/applications/metamta/PhabricatorMetaMTAWorker.php @@ -18,7 +18,7 @@ final class PhabricatorMetaMTAWorker pht('Unable to load message!')); } - if ($message->getStatus() != PhabricatorMetaMTAMail::STATUS_QUEUE) { + if ($message->getStatus() != PhabricatorMailOutboundStatus::STATUS_QUEUE) { return; } diff --git a/src/applications/metamta/application/PhabricatorMetaMTAApplication.php b/src/applications/metamta/application/PhabricatorMetaMTAApplication.php index 9f6633f9c4..5617c39c35 100644 --- a/src/applications/metamta/application/PhabricatorMetaMTAApplication.php +++ b/src/applications/metamta/application/PhabricatorMetaMTAApplication.php @@ -3,7 +3,7 @@ final class PhabricatorMetaMTAApplication extends PhabricatorApplication { public function getName() { - return pht('MetaMTA'); + return pht('Mail'); } public function getBaseURI() { @@ -15,11 +15,11 @@ final class PhabricatorMetaMTAApplication extends PhabricatorApplication { } public function getShortDescription() { - return pht('Delivers Mail'); + return pht('Send and Receive Mail'); } public function getFlavorText() { - return pht('Yo dawg, we heard you like MTAs.'); + return pht('Every program attempts to expand until it can read mail.'); } public function getApplicationGroup() { @@ -30,12 +30,8 @@ final class PhabricatorMetaMTAApplication extends PhabricatorApplication { return false; } - public function isLaunchable() { - return false; - } - public function getTypeaheadURI() { - return null; + return '/mail/'; } public function getRoutes() { diff --git a/src/applications/metamta/constants/MetaMTAConstants.php b/src/applications/metamta/constants/MetaMTAConstants.php deleted file mode 100644 index 9b3fe7121f..0000000000 --- a/src/applications/metamta/constants/MetaMTAConstants.php +++ /dev/null @@ -1,3 +0,0 @@ - pht('Queued'), + self::STATUS_FAIL => pht('Delivery Failed'), + self::STATUS_SENT => pht('Sent'), + self::STATUS_VOID => pht('Voided'), + ); + $status_code = coalesce($status_code, '?'); + return idx($names, $status_code, $status_code); + } + + public static function getStatusIcon($status_code) { + $icons = array( + self::STATUS_QUEUE => 'fa-clock-o', + self::STATUS_FAIL => 'fa-warning', + self::STATUS_SENT => 'fa-envelope', + self::STATUS_VOID => 'fa-trash', + ); + return idx($icons, $status_code, 'fa-question-circle'); + } + + public static function getStatusColor($status_code) { + $colors = array( + self::STATUS_QUEUE => 'blue', + self::STATUS_FAIL => 'red', + self::STATUS_SENT => 'green', + self::STATUS_VOID => 'black', + ); + + return idx($colors, $status_code, 'yellow'); + } + +} diff --git a/src/applications/metamta/controller/PhabricatorMetaMTAMailViewController.php b/src/applications/metamta/controller/PhabricatorMetaMTAMailViewController.php index 938e6f656c..faaa08e472 100644 --- a/src/applications/metamta/controller/PhabricatorMetaMTAMailViewController.php +++ b/src/applications/metamta/controller/PhabricatorMetaMTAMailViewController.php @@ -4,7 +4,7 @@ final class PhabricatorMetaMTAMailViewController extends PhabricatorMetaMTAController { public function handleRequest(AphrontRequest $request) { - $viewer = $request->getUser(); + $viewer = $this->getViewer(); $mail = id(new PhabricatorMetaMTAMailQuery()) ->setViewer($viewer) @@ -19,17 +19,27 @@ final class PhabricatorMetaMTAMailViewController } else { $title = $mail->getSubject(); } + $header = id(new PHUIHeaderView()) ->setHeader($title) - ->setUser($this->getRequest()->getUser()) + ->setUser($viewer) ->setPolicyObject($mail); + $status = $mail->getStatus(); + $name = PhabricatorMailOutboundStatus::getStatusName($status); + $icon = PhabricatorMailOutboundStatus::getStatusIcon($status); + $color = PhabricatorMailOutboundStatus::getStatusColor($status); + $header->setStatus($icon, $color, $name); + $crumbs = $this->buildApplicationCrumbs() - ->addTextCrumb( - 'Mail '.$mail->getID()); + ->addTextCrumb(pht('Mail %d', $mail->getID())); + $object_box = id(new PHUIObjectBoxView()) ->setHeader($header) - ->addPropertyList($this->buildPropertyView($mail)); + ->addPropertyList($this->buildMessageProperties($mail), pht('Message')) + ->addPropertyList($this->buildHeaderProperties($mail), pht('Headers')) + ->addPropertyList($this->buildDeliveryProperties($mail), pht('Delivery')) + ->addPropertyList($this->buildMetadataProperties($mail), pht('Metadata')); return $this->buildApplicationPage( array( @@ -42,42 +52,13 @@ final class PhabricatorMetaMTAMailViewController )); } - private function buildPropertyView(PhabricatorMetaMTAMail $mail) { + private function buildMessageProperties(PhabricatorMetaMTAMail $mail) { $viewer = $this->getViewer(); $properties = id(new PHUIPropertyListView()) ->setUser($viewer) ->setObject($mail); - $properties->addProperty( - pht('ID'), - $mail->getID()); - - $properties->addProperty( - pht('Status'), - $mail->getStatus()); - - if ($mail->getMessage()) { - $properties->addProperty( - pht('Status Details'), - $mail->getMessage()); - } - - if ($mail->getRelatedPHID()) { - $properties->addProperty( - pht('Related Object'), - $viewer->renderHandle($mail->getRelatedPHID())); - } - - if ($mail->getActorPHID()) { - $actor_str = $viewer->renderHandle($mail->getActorPHID()); - } else { - $actor_str = pht('Generated by Phabricator'); - } - $properties->addProperty( - pht('Actor'), - $actor_str); - if ($mail->getFrom()) { $from_str = $viewer->renderHandle($mail->getFrom()); } else { @@ -105,6 +86,169 @@ final class PhabricatorMetaMTAMailViewController pht('Cc'), $cc_list); + $properties->addSectionHeader( + pht('Message'), + PHUIPropertyListView::ICON_SUMMARY); + + if ($mail->hasSensitiveContent()) { + $body = phutil_tag( + 'em', + array(), + pht( + 'The content of this mail is sensitive and it can not be '. + 'viewed from the web UI.')); + } else { + $body = phutil_tag( + 'div', + array( + 'style' => 'white-space: pre-wrap', + ), + $mail->getBody()); + } + + $properties->addTextContent($body); + + + return $properties; + } + + private function buildHeaderProperties(PhabricatorMetaMTAMail $mail) { + $viewer = $this->getViewer(); + + $properties = id(new PHUIPropertyListView()) + ->setUser($viewer) + ->setStacked(true); + + $headers = $mail->getDeliveredHeaders(); + if ($headers === null) { + $headers = $mail->generateHeaders(); + } + + // Sort headers by name. + $headers = isort($headers, 0); + + foreach ($headers as $header) { + list($key, $value) = $header; + $properties->addProperty($key, $value); + } + + return $properties; + } + + private function buildDeliveryProperties(PhabricatorMetaMTAMail $mail) { + $viewer = $this->getViewer(); + + $properties = id(new PHUIPropertyListView()) + ->setUser($viewer); + + $actors = $mail->getDeliveredActors(); + $reasons = null; + if (!$actors) { + // TODO: We can get rid of this special-cased message after these changes + // have been live for a while, but provide a more tailored message for + // now so things are a little less confusing for users. + if ($mail->getStatus() == PhabricatorMetaMTAMail::STATUS_SENT) { + $delivery = phutil_tag( + 'em', + array(), + pht( + 'This is an older message that predates recording delivery '. + 'information, so none is available.')); + } else { + $delivery = phutil_tag( + 'em', + array(), + pht( + 'This message has not been delivered yet, so delivery information '. + 'is not available.')); + } + } else { + $actor = idx($actors, $viewer->getPHID()); + if (!$actor) { + $delivery = phutil_tag( + 'em', + array(), + pht('This message was not delivered to you.')); + } else { + $deliverable = $actor['deliverable']; + if ($deliverable) { + $delivery = pht('Delivered'); + } else { + $delivery = pht('Voided'); + } + + $reasons = id(new PHUIStatusListView()); + + $reason_codes = $actor['reasons']; + if (!$reason_codes) { + $reason_codes = array( + PhabricatorMetaMTAActor::REASON_NONE, + ); + } + + $icon_yes = 'fa-check green'; + $icon_no = 'fa-times red'; + + foreach ($reason_codes as $reason) { + $target = phutil_tag( + 'strong', + array(), + PhabricatorMetaMTAActor::getReasonName($reason)); + + if (PhabricatorMetaMTAActor::isDeliveryReason($reason)) { + $icon = $icon_yes; + } else { + $icon = $icon_no; + } + + $item = id(new PHUIStatusItemView()) + ->setIcon($icon) + ->setTarget($target) + ->setNote(PhabricatorMetaMTAActor::getReasonDescription($reason)); + + $reasons->addItem($item); + } + } + } + + $properties->addProperty(pht('Delivery'), $delivery); + if ($reasons) { + $properties->addProperty(pht('Reasons'), $reasons); + } + + return $properties; + } + + private function buildMetadataProperties(PhabricatorMetaMTAMail $mail) { + $viewer = $this->getViewer(); + + $properties = id(new PHUIPropertyListView()) + ->setUser($viewer); + + $properties->addProperty(pht('Message PHID'), $mail->getPHID()); + + $details = $mail->getMessage(); + if (!strlen($details)) { + $details = phutil_tag('em', array(), pht('None')); + } + $properties->addProperty(pht('Status Details'), $details); + + $actor_phid = $mail->getActorPHID(); + if ($actor_phid) { + $actor_str = $viewer->renderHandle($actor_phid); + } else { + $actor_str = pht('Generated by Phabricator'); + } + $properties->addProperty(pht('Actor'), $actor_str); + + $related_phid = $mail->getRelatedPHID(); + if ($related_phid) { + $related = $viewer->renderHandle($mail->getRelatedPHID()); + } else { + $related = phutil_tag('em', array(), pht('None')); + } + $properties->addProperty(pht('Related Object'), $related); + return $properties; } diff --git a/src/applications/metamta/management/PhabricatorMailManagementListOutboundWorkflow.php b/src/applications/metamta/management/PhabricatorMailManagementListOutboundWorkflow.php index 366f5bdeca..b4aa76379e 100644 --- a/src/applications/metamta/management/PhabricatorMailManagementListOutboundWorkflow.php +++ b/src/applications/metamta/management/PhabricatorMailManagementListOutboundWorkflow.php @@ -45,7 +45,7 @@ final class PhabricatorMailManagementListOutboundWorkflow $table->addRow(array( 'id' => $mail->getID(), - 'status' => PhabricatorMetaMTAMail::getReadableStatus($status), + 'status' => PhabricatorMailOutboundStatus::getStatusName($status), 'subject' => $mail->getSubject(), )); } diff --git a/src/applications/metamta/management/PhabricatorMailManagementReceiveTestWorkflow.php b/src/applications/metamta/management/PhabricatorMailManagementReceiveTestWorkflow.php index eb4d05d274..3cbe2345eb 100644 --- a/src/applications/metamta/management/PhabricatorMailManagementReceiveTestWorkflow.php +++ b/src/applications/metamta/management/PhabricatorMailManagementReceiveTestWorkflow.php @@ -105,15 +105,13 @@ final class PhabricatorMailManagementReceiveTestWorkflow 'to' => $to.'+1+'.$pseudohash, )); - $receivers = id(new PhutilSymbolLoader()) + $receivers = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorMailReceiver') - ->loadObjects(); + ->setFilterMethod('isEnabled') + ->execute(); $receiver = null; foreach ($receivers as $possible_receiver) { - if (!$possible_receiver->isEnabled()) { - continue; - } if (!$possible_receiver->canAcceptMail($pseudomail)) { continue; } diff --git a/src/applications/metamta/management/PhabricatorMailManagementResendWorkflow.php b/src/applications/metamta/management/PhabricatorMailManagementResendWorkflow.php index 7a09324179..1eb843a82a 100644 --- a/src/applications/metamta/management/PhabricatorMailManagementResendWorkflow.php +++ b/src/applications/metamta/management/PhabricatorMailManagementResendWorkflow.php @@ -47,7 +47,7 @@ final class PhabricatorMailManagementResendWorkflow } foreach ($messages as $message) { - $message->setStatus(PhabricatorMetaMTAMail::STATUS_QUEUE); + $message->setStatus(PhabricatorMailOutboundStatus::STATUS_QUEUE); $message->save(); $mailer_task = PhabricatorWorker::scheduleTask( diff --git a/src/applications/metamta/management/PhabricatorMailManagementShowOutboundWorkflow.php b/src/applications/metamta/management/PhabricatorMailManagementShowOutboundWorkflow.php index bc8f4ccbaf..b30f2685e6 100644 --- a/src/applications/metamta/management/PhabricatorMailManagementShowOutboundWorkflow.php +++ b/src/applications/metamta/management/PhabricatorMailManagementShowOutboundWorkflow.php @@ -76,23 +76,20 @@ final class PhabricatorMailManagementShowOutboundWorkflow $info[] = pht('Related PHID: %s', $message->getRelatedPHID()); $info[] = pht('Message: %s', $message->getMessage()); + $ignore = array( + 'body' => true, + 'html-body' => true, + 'headers' => true, + 'attachments' => true, + 'headers.sent' => true, + 'authors.sent' => true, + ); + $info[] = null; $info[] = pht('PARAMETERS'); $parameters = $message->getParameters(); foreach ($parameters as $key => $value) { - if ($key == 'body') { - continue; - } - - if ($key == 'html-body') { - continue; - } - - if ($key == 'headers') { - continue; - } - - if ($key == 'attachments') { + if (isset($ignore[$key])) { continue; } @@ -105,7 +102,13 @@ final class PhabricatorMailManagementShowOutboundWorkflow $info[] = null; $info[] = pht('HEADERS'); - foreach (idx($parameters, 'headers', array()) as $header) { + + $headers = $message->getDeliveredHeaders(); + if (!$headers) { + $headers = $message->generateHeaders(); + } + + foreach ($headers as $header) { list($name, $value) = $header; $info[] = "{$name}: {$value}"; } @@ -119,21 +122,33 @@ final class PhabricatorMailManagementShowOutboundWorkflow } } - $actors = $message->loadAllActors(); - $actors = array_select_keys( - $actors, - array_merge($message->getToPHIDs(), $message->getCcPHIDs())); - $info[] = null; - $info[] = pht('RECIPIENTS'); - foreach ($actors as $actor) { - if ($actor->isDeliverable()) { - $info[] = ' '.coalesce($actor->getName(), $actor->getPHID()); - } else { - $info[] = '! '.coalesce($actor->getName(), $actor->getPHID()); - } - foreach ($actor->getDeliverabilityReasons() as $reason) { - $desc = PhabricatorMetaMTAActor::getReasonDescription($reason); - $info[] = ' - '.$desc; + $all_actors = $message->loadAllActors(); + + $actors = $message->getDeliveredActors(); + if ($actors) { + $info[] = null; + $info[] = pht('RECIPIENTS'); + foreach ($actors as $actor_phid => $actor_info) { + $actor = idx($all_actors, $actor_phid); + if ($actor) { + $actor_name = coalesce($actor->getName(), $actor_phid); + } else { + $actor_name = $actor_phid; + } + + $deliverable = $actor_info['deliverable']; + if ($deliverable) { + $info[] = ' '.$actor_name; + } else { + $info[] = '! '.$actor_name; + } + + $reasons = $actor_info['reasons']; + foreach ($reasons as $reason) { + $name = PhabricatorMetaMTAActor::getReasonName($reason); + $desc = PhabricatorMetaMTAActor::getReasonDescription($reason); + $info[] = ' - '.$name.': '.$desc; + } } } diff --git a/src/applications/metamta/management/PhabricatorMailManagementVolumeWorkflow.php b/src/applications/metamta/management/PhabricatorMailManagementVolumeWorkflow.php index c907bf1050..dcc548e0f5 100644 --- a/src/applications/metamta/management/PhabricatorMailManagementVolumeWorkflow.php +++ b/src/applications/metamta/management/PhabricatorMailManagementVolumeWorkflow.php @@ -28,8 +28,11 @@ final class PhabricatorMailManagementVolumeWorkflow ->execute(); $unfiltered = array(); + $delivered = array(); foreach ($mails as $mail) { + // Count messages we attempted to deliver. This includes messages which + // were voided by preferences or other rules. $unfiltered_actors = mpull($mail->loadAllActors(), 'getPHID'); foreach ($unfiltered_actors as $phid) { if (empty($unfiltered[$phid])) { @@ -37,9 +40,26 @@ final class PhabricatorMailManagementVolumeWorkflow } $unfiltered[$phid]++; } + + // Now, count mail we actually delivered. + $result = $mail->getDeliveredActors(); + if ($result) { + foreach ($result as $actor_phid => $actor_info) { + if (!$actor_info['deliverable']) { + continue; + } + if (empty($delivered[$actor_phid])) { + $delivered[$actor_phid] = 0; + } + $delivered[$actor_phid]++; + } + } } + // Sort users by delivered mail, then unfiltered mail. + arsort($delivered); arsort($unfiltered); + $delivered = $delivered + array_fill_keys(array_keys($unfiltered), 0); $table = id(new PhutilConsoleTable()) ->setBorders(true) @@ -52,16 +72,23 @@ final class PhabricatorMailManagementVolumeWorkflow 'unfiltered', array( 'title' => pht('Unfiltered'), + )) + ->addColumn( + 'delivered', + array( + 'title' => pht('Delivered'), )); $handles = $viewer->loadHandles(array_keys($unfiltered)); $names = mpull(iterator_to_array($handles), 'getName', 'getPHID'); - foreach ($unfiltered as $phid => $count) { + foreach ($delivered as $phid => $delivered_count) { + $unfiltered_count = idx($unfiltered, $phid, 0); $table->addRow( array( 'user' => idx($names, $phid), - 'unfiltered' => $count, + 'unfiltered' => $unfiltered_count, + 'delivered' => $delivered_count, )); } @@ -70,7 +97,9 @@ final class PhabricatorMailManagementVolumeWorkflow echo "\n"; echo pht('Mail sent in the last 30 days.')."\n"; echo pht( - '"Unfiltered" is raw volume before preferences were applied.')."\n"; + '"Unfiltered" is raw volume before rules applied.')."\n"; + echo pht( + '"Delivered" shows email actually sent.')."\n"; echo "\n"; return 0; diff --git a/src/applications/metamta/query/PhabricatorMetaMTAActor.php b/src/applications/metamta/query/PhabricatorMetaMTAActor.php index 281e7fb90c..59e3f297de 100644 --- a/src/applications/metamta/query/PhabricatorMetaMTAActor.php +++ b/src/applications/metamta/query/PhabricatorMetaMTAActor.php @@ -5,6 +5,7 @@ final class PhabricatorMetaMTAActor extends Phobject { const STATUS_DELIVERABLE = 'deliverable'; const STATUS_UNDELIVERABLE = 'undeliverable'; + const REASON_NONE = 'none'; const REASON_UNLOADABLE = 'unloadable'; const REASON_UNMAILABLE = 'unmailable'; const REASON_NO_ADDRESS = 'noaddress'; @@ -71,8 +72,42 @@ final class PhabricatorMetaMTAActor extends Phobject { return $this->reasons; } + public static function isDeliveryReason($reason) { + switch ($reason) { + case self::REASON_NONE: + case self::REASON_FORCE: + case self::REASON_FORCE_HERALD: + return true; + default: + // All other reasons cause the message to not be delivered. + return false; + } + } + + public static function getReasonName($reason) { + $names = array( + self::REASON_NONE => pht('None'), + self::REASON_DISABLED => pht('Disabled Recipient'), + self::REASON_BOT => pht('Bot Recipient'), + self::REASON_NO_ADDRESS => pht('No Address'), + self::REASON_EXTERNAL_TYPE => pht('External Recipient'), + self::REASON_UNMAILABLE => pht('Not Mailable'), + self::REASON_RESPONSE => pht('Similar Reply'), + self::REASON_SELF => pht('Self Mail'), + self::REASON_MAIL_DISABLED => pht('Mail Disabled'), + self::REASON_MAILTAGS => pht('Mail Tags'), + self::REASON_UNLOADABLE => pht('Bad Recipient'), + self::REASON_FORCE => pht('Forced Mail'), + self::REASON_FORCE_HERALD => pht('Forced by Herald'), + ); + + return idx($names, $reason, pht('Unknown ("%s")', $reason)); + } + public static function getReasonDescription($reason) { $descriptions = array( + self::REASON_NONE => pht( + 'No special rules affected this mail.'), self::REASON_DISABLED => pht( 'This user is disabled; disabled users do not receive mail.'), self::REASON_BOT => pht( diff --git a/src/applications/metamta/query/PhabricatorMetaMTAMailSearchEngine.php b/src/applications/metamta/query/PhabricatorMetaMTAMailSearchEngine.php index d4bdf041ef..11ca1116ea 100644 --- a/src/applications/metamta/query/PhabricatorMetaMTAMailSearchEngine.php +++ b/src/applications/metamta/query/PhabricatorMetaMTAMailSearchEngine.php @@ -101,7 +101,7 @@ final class PhabricatorMetaMTAMailSearchEngine foreach ($mails as $mail) { if ($mail->hasSensitiveContent()) { - $header = pht('< content redacted >'); + $header = phutil_tag('em', array(), pht('Content Redacted')); } else { $header = $mail->getSubject(); } @@ -110,9 +110,16 @@ final class PhabricatorMetaMTAMailSearchEngine ->setUser($viewer) ->setObject($mail) ->setEpoch($mail->getDateCreated()) - ->setObjectName('Mail '.$mail->getID()) + ->setObjectName(pht('Mail %d', $mail->getID())) ->setHeader($header) - ->setHref($this->getURI('detail/'.$mail->getID())); + ->setHref($this->getURI('detail/'.$mail->getID().'/')); + + $status = $mail->getStatus(); + $status_name = PhabricatorMailOutboundStatus::getStatusName($status); + $status_icon = PhabricatorMailOutboundStatus::getStatusIcon($status); + $status_color = PhabricatorMailOutboundStatus::getStatusColor($status); + $item->setStatusIcon($status_icon.' '.$status_color, $status_name); + $list->addItem($item); } diff --git a/src/applications/metamta/storage/PhabricatorMetaMTAMail.php b/src/applications/metamta/storage/PhabricatorMetaMTAMail.php index ee09fa730f..b68ac93c5c 100644 --- a/src/applications/metamta/storage/PhabricatorMetaMTAMail.php +++ b/src/applications/metamta/storage/PhabricatorMetaMTAMail.php @@ -7,11 +7,6 @@ final class PhabricatorMetaMTAMail extends PhabricatorMetaMTADAO implements PhabricatorPolicyInterface { - const STATUS_QUEUE = 'queued'; - const STATUS_SENT = 'sent'; - const STATUS_FAIL = 'fail'; - const STATUS_VOID = 'void'; - const RETRY_DELAY = 5; protected $actorPHID; @@ -24,7 +19,7 @@ final class PhabricatorMetaMTAMail public function __construct() { - $this->status = self::STATUS_QUEUE; + $this->status = PhabricatorMailOutboundStatus::STATUS_QUEUE; $this->parameters = array('sensitive' => true); parent::__construct(); @@ -430,12 +425,14 @@ final class PhabricatorMetaMTAMail } if (!$force_send) { - if ($this->getStatus() != self::STATUS_QUEUE) { + if ($this->getStatus() != PhabricatorMailOutboundStatus::STATUS_QUEUE) { throw new Exception(pht('Trying to send an already-sent mail!')); } } try { + $headers = $this->generateHeaders(); + $params = $this->parameters; $actors = $this->loadAllActors(); @@ -535,16 +532,6 @@ final class PhabricatorMetaMTAMail $add_cc, mpull($cc_actors, 'getEmailAddress')); break; - case 'headers': - foreach ($value as $pair) { - list($header_key, $header_value) = $pair; - - // NOTE: If we have \n in a header, SES rejects the email. - $header_value = str_replace("\n", ' ', $header_value); - - $mailer->addHeader($header_key, $header_value); - } - break; case 'attachments': $value = $this->getAttachments(); foreach ($value as $attachment) { @@ -593,11 +580,6 @@ final class PhabricatorMetaMTAMail $mailer->setSubject(implode(' ', array_filter($subject))); break; - case 'is-bulk': - if ($value) { - $mailer->addHeader('Precedence', 'bulk'); - } - break; case 'thread-id': // NOTE: Gmail freaks out about In-Reply-To and References which @@ -608,7 +590,7 @@ final class PhabricatorMetaMTAMail $value = '<'.$value.'@'.$domain.'>'; if ($is_first && $mailer->supportsMessageIDHeader()) { - $mailer->addHeader('Message-ID', $value); + $headers[] = array('Message-ID', $value); } else { $in_reply_to = $value; $references = array($value); @@ -620,21 +602,16 @@ final class PhabricatorMetaMTAMail $references[] = $parent_id; } $references = implode(' ', $references); - $mailer->addHeader('In-Reply-To', $in_reply_to); - $mailer->addHeader('References', $references); + $headers[] = array('In-Reply-To', $in_reply_to); + $headers[] = array('References', $references); } $thread_index = $this->generateThreadIndex($value, $is_first); - $mailer->addHeader('Thread-Index', $thread_index); - break; - case 'mailtags': - // Handled below. - break; - case 'subject-prefix': - case 'vary-subject-prefix': - // Handled above. + $headers[] = array('Thread-Index', $thread_index); break; default: - // Just discard. + // Other parameters are handled elsewhere or are not relevant to + // constructing the message. + break; } } @@ -660,8 +637,27 @@ final class PhabricatorMetaMTAMail $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) { + list($header_key, $header_value) = $header; + $mailer->addHeader($header_key, $header_value); + } + $this->setParam('headers.sent', $headers); + + // Save the final deliverability outcomes and reasoning so we can + // explain why things happened the way they did. + $actor_list = array(); + foreach ($actors as $actor) { + $actor_list[$actor->getPHID()] = array( + 'deliverable' => $actor->isDeliverable(), + 'reasons' => $actor->getDeliverabilityReasons(), + ); + } + $this->setParam('actors.sent', $actor_list); + if (!$add_to && !$add_cc) { - $this->setStatus(self::STATUS_VOID); + $this->setStatus(PhabricatorMailOutboundStatus::STATUS_VOID); $this->setMessage( pht( 'Message has no valid recipients: all To/Cc are disabled, '. @@ -672,7 +668,7 @@ final class PhabricatorMetaMTAMail if ($this->getIsErrorEmail()) { $all_recipients = array_merge($add_to, $add_cc); if ($this->shouldRateLimitMail($all_recipients)) { - $this->setStatus(self::STATUS_VOID); + $this->setStatus(PhabricatorMailOutboundStatus::STATUS_VOID); $this->setMessage( pht( 'This is an error email, but one or more recipients have '. @@ -683,7 +679,7 @@ final class PhabricatorMetaMTAMail } if (PhabricatorEnv::getEnvConfig('phabricator.silent')) { - $this->setStatus(self::STATUS_VOID); + $this->setStatus(PhabricatorMailOutboundStatus::STATUS_VOID); $this->setMessage( pht( 'Phabricator is running in silent mode. See `%s` '. @@ -692,24 +688,6 @@ final class PhabricatorMetaMTAMail return $this->save(); } - $mailer->addHeader('X-Phabricator-Sent-This-Message', 'Yes'); - $mailer->addHeader('X-Mail-Transport-Agent', 'MetaMTA'); - - // Some clients respect this to suppress OOF and other auto-responses. - $mailer->addHeader('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(); - foreach ($mailtags as $mailtag) { - $tag_header[] = '<'.$mailtag.'>'; - } - $tag_header = implode(', ', $tag_header); - $mailer->addHeader('X-Phabricator-Mail-Tags', $tag_header); - } - // Some mailers require a valid "To:" in order to deliver mail. If we // don't have any "To:", try to fill it in with a placeholder "To:". // If that also fails, move the "Cc:" line to "To:". @@ -733,7 +711,7 @@ final class PhabricatorMetaMTAMail } } catch (Exception $ex) { $this - ->setStatus(self::STATUS_FAIL) + ->setStatus(PhabricatorMailOutboundStatus::STATUS_FAIL) ->setMessage($ex->getMessage()) ->save(); @@ -749,13 +727,13 @@ final class PhabricatorMetaMTAMail pht('Mail adapter encountered an unexpected, unspecified failure.')); } - $this->setStatus(self::STATUS_SENT); + $this->setStatus(PhabricatorMailOutboundStatus::STATUS_SENT); $this->save(); return $this; } catch (PhabricatorMetaMTAPermanentFailureException $ex) { $this - ->setStatus(self::STATUS_FAIL) + ->setStatus(PhabricatorMailOutboundStatus::STATUS_FAIL) ->setMessage($ex->getMessage()) ->save(); @@ -769,17 +747,6 @@ final class PhabricatorMetaMTAMail } } - public static function getReadableStatus($status_code) { - $readable = array( - self::STATUS_QUEUE => pht('Queued for Delivery'), - self::STATUS_FAIL => pht('Delivery Failed'), - self::STATUS_SENT => pht('Sent'), - self::STATUS_VOID => pht('Void'), - ); - $status_code = coalesce($status_code, '?'); - return idx($readable, $status_code, $status_code); - } - private function generateThreadIndex($seed, $is_first_mail) { // When threading, Outlook ignores the 'References' and 'In-Reply-To' // headers that most clients use. Instead, it uses a custom 'Thread-Index' @@ -1052,6 +1019,52 @@ final class PhabricatorMetaMTAMail return $ret; } + public function generateHeaders() { + $headers = array(); + + $headers[] = array('X-Phabricator-Sent-This-Message', 'Yes'); + $headers[] = array('X-Mail-Transport-Agent', 'MetaMTA'); + + // 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(); + foreach ($mailtags as $mailtag) { + $tag_header[] = '<'.$mailtag.'>'; + } + $tag_header = implode(', ', $tag_header); + $headers[] = array('X-Phabricator-Mail-Tags', $tag_header); + } + + $value = $this->getParam('headers', array()); + foreach ($value as $pair) { + list($header_key, $header_value) = $pair; + + // NOTE: If we have \n in a header, SES rejects the email. + $header_value = str_replace("\n", ' ', $header_value); + $headers[] = array($header_key, $header_value); + } + + $is_bulk = $this->getParam('is-bulk'); + if ($is_bulk) { + $headers[] = array('Precedence', 'bulk'); + } + + return $headers; + } + + public function getDeliveredHeaders() { + return $this->getParam('headers.sent'); + } + + public function getDeliveredActors() { + return $this->getParam('actors.sent'); + } + /* -( PhabricatorPolicyInterface )----------------------------------------- */ diff --git a/src/applications/metamta/storage/PhabricatorMetaMTAReceivedMail.php b/src/applications/metamta/storage/PhabricatorMetaMTAReceivedMail.php index 4b98bc904e..60e8a5e468 100644 --- a/src/applications/metamta/storage/PhabricatorMetaMTAReceivedMail.php +++ b/src/applications/metamta/storage/PhabricatorMetaMTAReceivedMail.php @@ -265,15 +265,13 @@ final class PhabricatorMetaMTAReceivedMail extends PhabricatorMetaMTADAO { * accepts this mail, if one exists. */ private function loadReceiver() { - $receivers = id(new PhutilSymbolLoader()) + $receivers = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorMailReceiver') - ->loadObjects(); + ->setFilterMethod('isEnabled') + ->execute(); $accept = array(); foreach ($receivers as $key => $receiver) { - if (!$receiver->isEnabled()) { - continue; - } if ($receiver->canAcceptMail($this)) { $accept[$key] = $receiver; } diff --git a/src/applications/metamta/storage/__tests__/PhabricatorMetaMTAMailTestCase.php b/src/applications/metamta/storage/__tests__/PhabricatorMetaMTAMailTestCase.php index 97369f054a..952d7808da 100644 --- a/src/applications/metamta/storage/__tests__/PhabricatorMetaMTAMailTestCase.php +++ b/src/applications/metamta/storage/__tests__/PhabricatorMetaMTAMailTestCase.php @@ -20,7 +20,7 @@ final class PhabricatorMetaMTAMailTestCase extends PhabricatorTestCase { $mailer = new PhabricatorMailImplementationTestAdapter(); $mail->sendNow($force = true, $mailer); $this->assertEqual( - PhabricatorMetaMTAMail::STATUS_SENT, + PhabricatorMailOutboundStatus::STATUS_SENT, $mail->getStatus()); @@ -36,7 +36,7 @@ final class PhabricatorMetaMTAMailTestCase extends PhabricatorTestCase { // Ignore. } $this->assertEqual( - PhabricatorMetaMTAMail::STATUS_QUEUE, + PhabricatorMailOutboundStatus::STATUS_QUEUE, $mail->getStatus()); @@ -52,7 +52,7 @@ final class PhabricatorMetaMTAMailTestCase extends PhabricatorTestCase { // Ignore. } $this->assertEqual( - PhabricatorMetaMTAMail::STATUS_FAIL, + PhabricatorMailOutboundStatus::STATUS_FAIL, $mail->getStatus()); } diff --git a/src/applications/passphrase/controller/PassphraseCredentialRevealController.php b/src/applications/passphrase/controller/PassphraseCredentialRevealController.php index c61086b0e7..fb784fb5b8 100644 --- a/src/applications/passphrase/controller/PassphraseCredentialRevealController.php +++ b/src/applications/passphrase/controller/PassphraseCredentialRevealController.php @@ -67,9 +67,10 @@ final class PassphraseCredentialRevealController ->addCancelButton($view_uri, pht('Done')); $type_secret = PassphraseCredentialTransaction::TYPE_LOOKEDATSECRET; - $xactions = array(id(new PassphraseCredentialTransaction()) - ->setTransactionType($type_secret) - ->setNewValue(true), + $xactions = array( + id(new PassphraseCredentialTransaction()) + ->setTransactionType($type_secret) + ->setNewValue(true), ); $editor = id(new PassphraseCredentialTransactionEditor()) diff --git a/src/applications/paste/query/PhabricatorPasteQuery.php b/src/applications/paste/query/PhabricatorPasteQuery.php index 5c31b65931..768f0cf20b 100644 --- a/src/applications/paste/query/PhabricatorPasteQuery.php +++ b/src/applications/paste/query/PhabricatorPasteQuery.php @@ -14,6 +14,8 @@ final class PhabricatorPasteQuery private $includeNoLanguage; private $dateCreatedAfter; private $dateCreatedBefore; + private $statuses; + public function withIDs(array $ids) { $this->ids = $ids; @@ -67,6 +69,11 @@ final class PhabricatorPasteQuery return $this; } + public function withStatuses(array $statuses) { + $this->statuses = $statuses; + return $this; + } + public function newResultObject() { return new PhabricatorPaste(); } @@ -139,6 +146,13 @@ final class PhabricatorPasteQuery $this->dateCreatedBefore); } + if ($this->statuses !== null) { + $where[] = qsprintf( + $conn, + 'status IN (%Ls)', + $this->statuses); + } + return $where; } diff --git a/src/applications/paste/query/PhabricatorPasteSearchEngine.php b/src/applications/paste/query/PhabricatorPasteSearchEngine.php index 935799ae0d..e27445d078 100644 --- a/src/applications/paste/query/PhabricatorPasteSearchEngine.php +++ b/src/applications/paste/query/PhabricatorPasteSearchEngine.php @@ -35,6 +35,10 @@ final class PhabricatorPasteSearchEngine $query->withDateCreatedBefore($map['createdEnd']); } + if ($map['statuses']) { + $query->withStatuses($map['statuses']); + } + return $query; } @@ -53,6 +57,12 @@ final class PhabricatorPasteSearchEngine id(new PhabricatorSearchDateField()) ->setKey('createdEnd') ->setLabel(pht('Created Before')), + id(new PhabricatorSearchCheckboxesField()) + ->setKey('statuses') + ->setLabel(pht('Status')) + ->setOptions( + id(new PhabricatorPaste()) + ->getStatusNameMap()), ); } @@ -70,6 +80,7 @@ final class PhabricatorPasteSearchEngine protected function getBuiltinQueryNames() { $names = array( + 'active' => pht('Active Pastes'), 'all' => pht('All Pastes'), ); @@ -86,6 +97,12 @@ final class PhabricatorPasteSearchEngine $query->setQueryKey($query_key); switch ($query_key) { + case 'active': + return $query->setParameter( + 'statuses', + array( + PhabricatorPaste::STATUS_ACTIVE, + )); case 'all': return $query; case 'authored': @@ -151,6 +168,10 @@ final class PhabricatorPasteSearchEngine ->addIcon('none', $line_count) ->appendChild($source_code); + if ($paste->isArchived()) { + $item->setDisabled(true); + } + $lang_name = $paste->getLanguage(); if ($lang_name) { $lang_name = idx($lang_map, $lang_name, $lang_name); diff --git a/src/applications/policy/__tests__/PhabricatorPolicyTestCase.php b/src/applications/policy/__tests__/PhabricatorPolicyTestCase.php index 9e7a9d11a9..96ad88d2a4 100644 --- a/src/applications/policy/__tests__/PhabricatorPolicyTestCase.php +++ b/src/applications/policy/__tests__/PhabricatorPolicyTestCase.php @@ -307,9 +307,9 @@ final class PhabricatorPolicyTestCase extends PhabricatorTestCase { } public function testAllQueriesBelongToActualApplications() { - $queries = id(new PhutilSymbolLoader()) + $queries = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorPolicyAwareQuery') - ->loadObjects(); + ->execute(); foreach ($queries as $qclass => $query) { $class = $query->getQueryApplicationClass(); diff --git a/src/applications/policy/config/PolicyLockOptionType.php b/src/applications/policy/config/PolicyLockOptionType.php index e9a51d706c..9dd147bbe4 100644 --- a/src/applications/policy/config/PolicyLockOptionType.php +++ b/src/applications/policy/config/PolicyLockOptionType.php @@ -4,10 +4,10 @@ final class PolicyLockOptionType extends PhabricatorConfigJSONOptionType { public function validateOption(PhabricatorConfigOption $option, $value) { - $capabilities = id(new PhutilSymbolLoader()) + $capabilities = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorPolicyCapability') - ->loadObjects(); - $capabilities = mpull($capabilities, null, 'getCapabilityKey'); + ->setUniqueMethod('getCapabilityKey') + ->execute(); $policy_phids = array(); foreach ($value as $capability_key => $policy) { diff --git a/src/applications/policy/controller/PhabricatorPolicyEditController.php b/src/applications/policy/controller/PhabricatorPolicyEditController.php index abd9c5b41b..be380aa0b1 100644 --- a/src/applications/policy/controller/PhabricatorPolicyEditController.php +++ b/src/applications/policy/controller/PhabricatorPolicyEditController.php @@ -37,9 +37,9 @@ final class PhabricatorPolicyEditController PhabricatorPolicy::ACTION_DENY => pht('Deny'), ); - $rules = id(new PhutilSymbolLoader()) + $rules = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorPolicyRule') - ->loadObjects(); + ->execute(); foreach ($rules as $key => $rule) { if (!$rule->canApplyToObject($object)) { diff --git a/src/applications/policy/query/PhabricatorPolicyQuery.php b/src/applications/policy/query/PhabricatorPolicyQuery.php index daa7012223..7ad2630503 100644 --- a/src/applications/policy/query/PhabricatorPolicyQuery.php +++ b/src/applications/policy/query/PhabricatorPolicyQuery.php @@ -305,9 +305,9 @@ final class PhabricatorPolicyQuery } public static function getObjectPolicyRules($object) { - $rules = id(new PhutilSymbolLoader()) + $rules = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorPolicyRule') - ->loadObjects(); + ->execute(); $results = array(); foreach ($rules as $rule) { diff --git a/src/applications/ponder/application/PhabricatorPonderApplication.php b/src/applications/ponder/application/PhabricatorPonderApplication.php index 11e90dc274..c91ba6803c 100644 --- a/src/applications/ponder/application/PhabricatorPonderApplication.php +++ b/src/applications/ponder/application/PhabricatorPonderApplication.php @@ -61,6 +61,8 @@ final class PhabricatorPonderApplication extends PhabricatorApplication { => 'PonderAnswerCommentController', 'answer/history/(?P\d+)/' => 'PonderAnswerHistoryController', + 'answer/helpful/(?Padd|remove)/(?P[1-9]\d*)/' + => 'PonderHelpfulSaveController', 'question/edit/(?:(?P\d+)/)?' => 'PonderQuestionEditController', 'question/create/' @@ -71,9 +73,8 @@ final class PhabricatorPonderApplication extends PhabricatorApplication { => 'PonderQuestionHistoryController', 'preview/' => 'PhabricatorMarkupPreviewController', - 'question/(?Popen|close)/(?P[1-9]\d*)/' + 'question/status/(?P[1-9]\d*)/' => 'PonderQuestionStatusController', - 'vote/' => 'PonderVoteSaveController', ), ); } @@ -93,11 +94,12 @@ final class PhabricatorPonderApplication extends PhabricatorApplication { protected function getCustomCapabilities() { return array( - PonderQuestionDefaultViewCapability::CAPABILITY => array( + PonderDefaultViewCapability::CAPABILITY => array( 'template' => PonderQuestionPHIDType::TYPECONST, 'capability' => PhabricatorPolicyCapability::CAN_VIEW, ), - PonderQuestionDefaultEditCapability::CAPABILITY => array( + PonderModerateCapability::CAPABILITY => array( + 'default' => PhabricatorPolicies::POLICY_ADMIN, 'template' => PonderQuestionPHIDType::TYPECONST, 'capability' => PhabricatorPolicyCapability::CAN_EDIT, ), diff --git a/src/applications/ponder/capability/PonderQuestionDefaultViewCapability.php b/src/applications/ponder/capability/PonderDefaultViewCapability.php similarity index 53% rename from src/applications/ponder/capability/PonderQuestionDefaultViewCapability.php rename to src/applications/ponder/capability/PonderDefaultViewCapability.php index 593ea1321e..0019714727 100644 --- a/src/applications/ponder/capability/PonderQuestionDefaultViewCapability.php +++ b/src/applications/ponder/capability/PonderDefaultViewCapability.php @@ -1,12 +1,12 @@ pht('Visible'), + self::ANSWER_STATUS_HIDDEN => pht('Hidden'), + ); + } + + public static function getAnswerStatusName($status) { + $map = array( + self::ANSWER_STATUS_VISIBLE => pht('Visible'), + self::ANSWER_STATUS_HIDDEN => pht('Hidden'), + ); + return idx($map, $status, pht('Unknown')); + } + + +} diff --git a/src/applications/ponder/constants/PonderQuestionStatus.php b/src/applications/ponder/constants/PonderQuestionStatus.php index 2d26b9cb66..61a446353e 100644 --- a/src/applications/ponder/constants/PonderQuestionStatus.php +++ b/src/applications/ponder/constants/PonderQuestionStatus.php @@ -2,20 +2,50 @@ final class PonderQuestionStatus extends PonderConstants { - const STATUS_OPEN = 0; - const STATUS_CLOSED = 1; + const STATUS_OPEN = 'open'; + const STATUS_CLOSED_RESOLVED = 'resolved'; + const STATUS_CLOSED_OBSOLETE = 'obsolete'; + const STATUS_CLOSED_DUPLICATE = 'duplicate'; public static function getQuestionStatusMap() { return array( - self::STATUS_OPEN => pht('Open'), - self::STATUS_CLOSED => pht('Closed'), + self::STATUS_OPEN => pht('Open'), + self::STATUS_CLOSED_RESOLVED => pht('Closed, Resolved'), + self::STATUS_CLOSED_OBSOLETE => pht('Closed, Obsolete'), + self::STATUS_CLOSED_DUPLICATE => pht('Closed, Duplicate'), ); } public static function getQuestionStatusFullName($status) { $map = array( - self::STATUS_OPEN => pht('Open'), - self::STATUS_CLOSED => pht('Closed by author'), + self::STATUS_OPEN => pht('Open'), + self::STATUS_CLOSED_RESOLVED => pht('Closed, Resolved'), + self::STATUS_CLOSED_OBSOLETE => pht('Closed, Obsolete'), + self::STATUS_CLOSED_DUPLICATE => pht('Closed, Duplicate'), + ); + return idx($map, $status, pht('Unknown')); + } + + public static function getQuestionStatusName($status) { + $map = array( + self::STATUS_OPEN => pht('Open'), + self::STATUS_CLOSED_RESOLVED => pht('Resolved'), + self::STATUS_CLOSED_OBSOLETE => pht('Obsolete'), + self::STATUS_CLOSED_DUPLICATE => pht('Duplicate'), + ); + return idx($map, $status, pht('Unknown')); + } + + public static function getQuestionStatusDescription($status) { + $map = array( + self::STATUS_OPEN => + pht('This question is open for answers.'), + self::STATUS_CLOSED_RESOLVED => + pht('This question has been answered or resolved.'), + self::STATUS_CLOSED_OBSOLETE => + pht('This question is no longer valid or out of date.'), + self::STATUS_CLOSED_DUPLICATE => + pht('This question is a duplicate of another question.'), ); return idx($map, $status, pht('Unknown')); } @@ -23,7 +53,9 @@ final class PonderQuestionStatus extends PonderConstants { public static function getQuestionStatusTagColor($status) { $map = array( self::STATUS_OPEN => PHUITagView::COLOR_BLUE, - self::STATUS_CLOSED => PHUITagView::COLOR_BLACK, + self::STATUS_CLOSED_RESOLVED => PHUITagView::COLOR_BLACK, + self::STATUS_CLOSED_OBSOLETE => PHUITagView::COLOR_BLACK, + self::STATUS_CLOSED_DUPLICATE => PHUITagView::COLOR_BLACK, ); return idx($map, $status); @@ -32,10 +64,26 @@ final class PonderQuestionStatus extends PonderConstants { public static function getQuestionStatusIcon($status) { $map = array( self::STATUS_OPEN => 'fa-question-circle', - self::STATUS_CLOSED => 'fa-check-square-o', + self::STATUS_CLOSED_RESOLVED => 'fa-check', + self::STATUS_CLOSED_OBSOLETE => 'fa-ban', + self::STATUS_CLOSED_DUPLICATE => 'fa-clone', ); return idx($map, $status); } + public static function getQuestionStatusOpenMap() { + return array( + self::STATUS_OPEN, + ); + } + + public static function getQuestionStatusClosedMap() { + return array( + self::STATUS_CLOSED_RESOLVED, + self::STATUS_CLOSED_OBSOLETE, + self::STATUS_CLOSED_DUPLICATE, + ); + } + } diff --git a/src/applications/ponder/constants/PonderVote.php b/src/applications/ponder/constants/PonderVote.php index 5ac1efdd3a..c3e9745eec 100644 --- a/src/applications/ponder/constants/PonderVote.php +++ b/src/applications/ponder/constants/PonderVote.php @@ -4,6 +4,5 @@ final class PonderVote extends PonderConstants { const VOTE_UP = 1; const VOTE_NONE = 0; - const VOTE_DOWN = -1; } diff --git a/src/applications/ponder/controller/PonderAnswerEditController.php b/src/applications/ponder/controller/PonderAnswerEditController.php index 328eed1f91..ff2fd35920 100644 --- a/src/applications/ponder/controller/PonderAnswerEditController.php +++ b/src/applications/ponder/controller/PonderAnswerEditController.php @@ -20,6 +20,7 @@ final class PonderAnswerEditController extends PonderController { } $v_content = $answer->getContent(); + $v_status = $answer->getStatus(); $e_content = true; @@ -31,6 +32,7 @@ final class PonderAnswerEditController extends PonderController { $errors = array(); if ($request->isFormPost()) { $v_content = $request->getStr('content'); + $v_status = $request->getStr('status'); if (!strlen($v_content)) { $errors[] = pht('You must provide some substance in your answer.'); @@ -43,6 +45,10 @@ final class PonderAnswerEditController extends PonderController { ->setTransactionType(PonderAnswerTransaction::TYPE_CONTENT) ->setNewValue($v_content); + $xactions[] = id(new PonderAnswerTransaction()) + ->setTransactionType(PonderAnswerTransaction::TYPE_STATUS) + ->setNewValue($v_status); + $editor = id(new PonderAnswerEditor()) ->setActor($viewer) ->setContentSourceFromRequest($request) @@ -63,6 +69,12 @@ final class PonderAnswerEditController extends PonderController { id(new AphrontFormStaticControl()) ->setLabel(pht('Question')) ->setValue($question->getTitle())) + ->appendChild( + id(new AphrontFormSelectControl()) + ->setLabel(pht('Status')) + ->setName('status') + ->setValue($v_status) + ->setOptions(PonderAnswerStatus::getAnswerStatusMap())) ->appendChild( id(new PhabricatorRemarkupControl()) ->setUser($viewer) @@ -73,7 +85,7 @@ final class PonderAnswerEditController extends PonderController { ->setError($e_content)) ->appendChild( id(new AphrontFormSubmitControl()) - ->setValue(pht('Update Answer')) + ->setValue(pht('Submit')) ->addCancelButton($answer_uri)); $crumbs = $this->buildApplicationCrumbs(); diff --git a/src/applications/ponder/controller/PonderAnswerSaveController.php b/src/applications/ponder/controller/PonderAnswerSaveController.php index 2e2b965c3b..3953fed23f 100644 --- a/src/applications/ponder/controller/PonderAnswerSaveController.php +++ b/src/applications/ponder/controller/PonderAnswerSaveController.php @@ -19,9 +19,9 @@ final class PonderAnswerSaveController extends PonderController { return new Aphront404Response(); } - $answer = $request->getStr('answer'); + $content = $request->getStr('answer'); - if (!strlen(trim($answer))) { + if (!strlen(trim($content))) { $dialog = id(new AphrontDialogView()) ->setUser($viewer) ->setTitle(pht('Empty Answer')) @@ -32,18 +32,9 @@ final class PonderAnswerSaveController extends PonderController { return id(new AphrontDialogResponse())->setDialog($dialog); } - $content_source = PhabricatorContentSource::newForSource( - PhabricatorContentSource::SOURCE_WEB, - array( - 'ip' => $request->getRemoteAddr(), - )); + $answer = PonderAnswer::initializeNewAnswer($viewer); - $res = id(new PonderAnswer()) - ->setAuthorPHID($viewer->getPHID()) - ->setQuestionID($question->getID()) - ->setContent($answer) - ->setVoteCount(0) - ->setContentSource($content_source); + // Question Editor $xactions = array(); $xactions[] = id(new PonderQuestionTransaction()) @@ -51,7 +42,7 @@ final class PonderAnswerSaveController extends PonderController { ->setNewValue( array( '+' => array( - array('answer' => $res), + array('answer' => $answer), ), )); @@ -61,6 +52,27 @@ final class PonderAnswerSaveController extends PonderController { $editor->applyTransactions($question, $xactions); + // Answer Editor + + $template = id(new PonderAnswerTransaction()); + $xactions = array(); + + $xactions[] = id(clone $template) + ->setTransactionType(PonderAnswerTransaction::TYPE_QUESTION_ID) + ->setNewValue($question->getID()); + + $xactions[] = id(clone $template) + ->setTransactionType(PonderAnswerTransaction::TYPE_CONTENT) + ->setNewValue($content); + + $editor = id(new PonderAnswerEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true); + + $editor->applyTransactions($answer, $xactions); + + return id(new AphrontRedirectResponse())->setURI( id(new PhutilURI('/Q'.$question->getID()))); } diff --git a/src/applications/ponder/controller/PonderHelpfulSaveController.php b/src/applications/ponder/controller/PonderHelpfulSaveController.php new file mode 100644 index 0000000000..2345efcb6c --- /dev/null +++ b/src/applications/ponder/controller/PonderHelpfulSaveController.php @@ -0,0 +1,60 @@ +getViewer(); + $id = $request->getURIData('id'); + $action = $request->getURIData('action'); + + $answer = id(new PonderAnswerQuery()) + ->setViewer($viewer) + ->withIDs(array($id)) + ->needViewerVotes(true) + ->executeOne(); + + if (!$answer) { + return new Aphront404Response(); + } + + $edit_uri = '/Q'.$answer->getQuestionID(); + + switch ($action) { + case 'add': + $newvote = PonderVote::VOTE_UP; + break; + case 'remove': + $newvote = PonderVote::VOTE_NONE; + break; + } + + if ($request->isFormPost()) { + + $editor = id(new PonderVoteEditor()) + ->setVotable($answer) + ->setActor($viewer) + ->setVote($newvote) + ->saveVote(); + + return id(new AphrontRedirectResponse())->setURI($edit_uri); + } + + if ($action == 'add') { + $title = pht('Mark Answer as Helpful?'); + $body = pht('This answer will be marked as helpful.'); + $button = pht('Mark Helpful'); + } else { + $title = pht('Remove Helpful From Answer?'); + $body = pht('This answer will no longer be marked as helpful.'); + $button = pht('Remove Helpful'); + } + + $dialog = $this->newDialog(); + $dialog->setTitle($title); + $dialog->appendChild($body); + $dialog->addCancelButton($edit_uri); + $dialog->addSubmitButton($button); + + return id(new AphrontDialogResponse())->setDialog($dialog); + } +} diff --git a/src/applications/ponder/controller/PonderQuestionEditController.php b/src/applications/ponder/controller/PonderQuestionEditController.php index cf8b5f9de1..41123d6049 100644 --- a/src/applications/ponder/controller/PonderQuestionEditController.php +++ b/src/applications/ponder/controller/PonderQuestionEditController.php @@ -23,7 +23,9 @@ final class PonderQuestionEditController extends PonderController { $question->getPHID(), PhabricatorProjectObjectHasProjectEdgeType::EDGECONST); $v_projects = array_reverse($v_projects); + $is_new = false; } else { + $is_new = true; $question = PonderQuestion::initializeNewQuestion($viewer); $v_projects = array(); } @@ -31,8 +33,9 @@ final class PonderQuestionEditController extends PonderController { $v_title = $question->getTitle(); $v_content = $question->getContent(); $v_view = $question->getViewPolicy(); - $v_edit = $question->getEditPolicy(); $v_space = $question->getSpacePHID(); + $v_status = $question->getStatus(); + $errors = array(); $e_title = true; @@ -41,8 +44,8 @@ final class PonderQuestionEditController extends PonderController { $v_content = $request->getStr('content'); $v_projects = $request->getArr('projects'); $v_view = $request->getStr('viewPolicy'); - $v_edit = $request->getStr('editPolicy'); $v_space = $request->getStr('spacePHID'); + $v_status = $request->getStr('status'); $len = phutil_utf8_strlen($v_title); if ($len < 1) { @@ -65,14 +68,16 @@ final class PonderQuestionEditController extends PonderController { ->setTransactionType(PonderQuestionTransaction::TYPE_CONTENT) ->setNewValue($v_content); + if (!$is_new) { + $xactions[] = id(clone $template) + ->setTransactionType(PonderQuestionTransaction::TYPE_STATUS) + ->setNewValue($v_status); + } + $xactions[] = id(clone $template) ->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY) ->setNewValue($v_view); - $xactions[] = id(clone $template) - ->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY) - ->setNewValue($v_edit); - $xactions[] = id(clone $template) ->setTransactionType(PhabricatorTransactions::TYPE_SPACE) ->setNewValue($v_space); @@ -123,14 +128,17 @@ final class PonderQuestionEditController extends PonderController { ->setSpacePHID($v_space) ->setPolicies($policies) ->setValue($v_view) - ->setCapability(PhabricatorPolicyCapability::CAN_VIEW)) - ->appendControl( - id(new AphrontFormPolicyControl()) - ->setName('editPolicy') - ->setPolicyObject($question) - ->setPolicies($policies) - ->setValue($v_edit) - ->setCapability(PhabricatorPolicyCapability::CAN_EDIT)); + ->setCapability(PhabricatorPolicyCapability::CAN_VIEW)); + + + if (!$is_new) { + $form->appendChild( + id(new AphrontFormSelectControl()) + ->setLabel(pht('Status')) + ->setName('status') + ->setValue($v_status) + ->setOptions(PonderQuestionStatus::getQuestionStatusMap())); + } $form->appendControl( id(new AphrontFormTokenizerControl()) @@ -142,28 +150,30 @@ final class PonderQuestionEditController extends PonderController { $form->appendChild( id(new AphrontFormSubmitControl()) ->addCancelButton($this->getApplicationURI()) - ->setValue(pht('Ask Away!'))); + ->setValue(pht('Submit'))); $preview = id(new PHUIRemarkupPreviewPanel()) ->setHeader(pht('Question Preview')) ->setControlID('content') ->setPreviewURI($this->getApplicationURI('preview/')); - $form_box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Ask New Question')) - ->setFormErrors($errors) - ->setForm($form); - $crumbs = $this->buildApplicationCrumbs(); $id = $question->getID(); if ($id) { $crumbs->addTextCrumb("Q{$id}", "/Q{$id}"); $crumbs->addTextCrumb(pht('Edit')); + $title = pht('Edit Question'); } else { $crumbs->addTextCrumb(pht('Ask Question')); + $title = pht('Ask New Question'); } + $form_box = id(new PHUIObjectBoxView()) + ->setHeaderText($title) + ->setFormErrors($errors) + ->setForm($form); + return $this->buildApplicationPage( array( $crumbs, @@ -171,7 +181,7 @@ final class PonderQuestionEditController extends PonderController { $preview, ), array( - 'title' => pht('Ask New Question'), + 'title' => $title, )); } diff --git a/src/applications/ponder/controller/PonderQuestionStatusController.php b/src/applications/ponder/controller/PonderQuestionStatusController.php index db2d971c92..a4671d5915 100644 --- a/src/applications/ponder/controller/PonderQuestionStatusController.php +++ b/src/applications/ponder/controller/PonderQuestionStatusController.php @@ -6,7 +6,6 @@ final class PonderQuestionStatusController public function handleRequest(AphrontRequest $request) { $viewer = $request->getViewer(); $id = $request->getURIData('id'); - $status = $request->getURIData('status'); $question = id(new PonderQuestionQuery()) ->setViewer($viewer) @@ -21,29 +20,46 @@ final class PonderQuestionStatusController return new Aphront404Response(); } - switch ($status) { - case 'open': - $status = PonderQuestionStatus::STATUS_OPEN; - break; - case 'close': - $status = PonderQuestionStatus::STATUS_CLOSED; - break; - default: - return new Aphront400Response(); + $view_uri = '/Q'.$question->getID(); + $v_status = $question->getStatus(); + + if ($request->isFormPost()) { + $v_status = $request->getStr('status'); + + $xactions = array(); + $xactions[] = id(new PonderQuestionTransaction()) + ->setTransactionType(PonderQuestionTransaction::TYPE_STATUS) + ->setNewValue($v_status); + + $editor = id(new PonderQuestionEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request); + + $editor->applyTransactions($question, $xactions); + + return id(new AphrontRedirectResponse())->setURI($view_uri); } - $xactions = array(); - $xactions[] = id(new PonderQuestionTransaction()) - ->setTransactionType(PonderQuestionTransaction::TYPE_STATUS) - ->setNewValue($status); + $radio = id(new AphrontFormRadioButtonControl()) + ->setLabel(pht('Status')) + ->setName('status') + ->setValue($v_status); - $editor = id(new PonderQuestionEditor()) - ->setActor($viewer) - ->setContentSourceFromRequest($request); + foreach (PonderQuestionStatus::getQuestionStatusMap() as $value => $name) { + $description = PonderQuestionStatus::getQuestionStatusDescription($value); + $radio->addButton($value, $name, $description); + } - $editor->applyTransactions($question, $xactions); + $form = id(new AphrontFormView()) + ->setUser($viewer) + ->appendChild($radio); + + return $this->newDialog() + ->setTitle(pht('Change Question Status')) + ->appendChild($form->buildLayoutView()) + ->addSubmitButton(pht('Submit')) + ->addCancelButton($view_uri); - return id(new AphrontRedirectResponse())->setURI('/Q'.$question->getID()); } } diff --git a/src/applications/ponder/controller/PonderQuestionViewController.php b/src/applications/ponder/controller/PonderQuestionViewController.php index 51f165b408..06f924510f 100644 --- a/src/applications/ponder/controller/PonderQuestionViewController.php +++ b/src/applications/ponder/controller/PonderQuestionViewController.php @@ -10,32 +10,18 @@ final class PonderQuestionViewController extends PonderController { ->setViewer($viewer) ->withIDs(array($id)) ->needAnswers(true) - ->needViewerVotes(true) + ->needProjectPHIDs(true) ->executeOne(); if (!$question) { return new Aphront404Response(); } - $question->attachVotes($viewer->getPHID()); - - $question_xactions = $this->buildQuestionTransactions($question); $answers = $this->buildAnswers($question->getAnswers()); - $authors = mpull($question->getAnswers(), null, 'getAuthorPHID'); - if (isset($authors[$viewer->getPHID()])) { - $answer_add_panel = id(new PHUIInfoView()) - ->setSeverity(PHUIInfoView::SEVERITY_NOTICE) - ->appendChild( - pht( - 'You have already answered this question. You can not answer '. - 'twice, but you can edit your existing answer.')); - } else { - $answer_add_panel = new PonderAddAnswerView(); - $answer_add_panel - ->setQuestion($question) - ->setUser($viewer) - ->setActionURI('/ponder/answer/add/'); - } + $answer_add_panel = id(new PonderAddAnswerView()) + ->setQuestion($question) + ->setUser($viewer) + ->setActionURI('/ponder/answer/add/'); $header = new PHUIHeaderView(); $header->setHeader($question->getTitle()); @@ -45,34 +31,68 @@ final class PonderQuestionViewController extends PonderController { if ($question->getStatus() == PonderQuestionStatus::STATUS_OPEN) { $header->setStatus('fa-square-o', 'bluegrey', pht('Open')); } else { - $header->setStatus('fa-check-square-o', 'dark', pht('Closed')); + $text = PonderQuestionStatus::getQuestionStatusFullName( + $question->getStatus()); + $icon = PonderQuestionStatus::getQuestionStatusIcon( + $question->getStatus()); + $header->setStatus($icon, 'dark', $text); } $actions = $this->buildActionListView($question); $properties = $this->buildPropertyListView($question, $actions); + $sidebar = $this->buildSidebar($question); + + $content_id = celerity_generate_unique_node_id(); + $timeline = $this->buildTransactionTimeline( + $question, + id(new PonderQuestionTransactionQuery()) + ->withTransactionTypes(array(PhabricatorTransactions::TYPE_COMMENT))); + $xactions = $timeline->getTransactions(); + + $add_comment = id(new PhabricatorApplicationTransactionCommentView()) + ->setUser($viewer) + ->setObjectPHID($question->getPHID()) + ->setShowPreview(false) + ->setHeaderText(pht('Question Comment')) + ->setAction($this->getApplicationURI("/question/comment/{$id}/")) + ->setSubmitButtonName(pht('Comment')); + + $comment_view = phutil_tag( + 'div', + array( + 'id' => $content_id, + 'style' => 'display: none;', + ), + array( + $timeline, + $add_comment, + )); + + $footer = id(new PonderFooterView()) + ->setContentID($content_id) + ->setCount(count($xactions)); $object_box = id(new PHUIObjectBoxView()) ->setHeader($header) - ->addPropertyList($properties); + ->addPropertyList($properties) + ->appendChild($footer); $crumbs = $this->buildApplicationCrumbs($this->buildSideNavView()); $crumbs->addTextCrumb('Q'.$id, '/Q'.$id); - $ponder_view = phutil_tag( - 'div', - array( - 'class' => 'ponder-question-view', - ), - array( - $crumbs, - $object_box, - $question_xactions, - $answers, - $answer_add_panel, - )); + $ponder_view = id(new PHUITwoColumnView()) + ->setMainColumn(array( + $object_box, + $comment_view, + $answers, + $answer_add_panel, + )) + ->setSideColumn($sidebar) + ->addClass('ponder-question-view'); return $this->buildApplicationPage( array( + $crumbs, $ponder_view, ), array( @@ -109,21 +129,18 @@ final class PonderQuestionViewController extends PonderController { if ($question->getStatus() == PonderQuestionStatus::STATUS_OPEN) { $name = pht('Close Question'); $icon = 'fa-check-square-o'; - $href = 'close'; } else { $name = pht('Reopen Question'); $icon = 'fa-square-o'; - $href = 'open'; } $view->addAction( id(new PhabricatorActionView()) ->setName($name) ->setIcon($icon) - ->setRenderAsForm($can_edit) - ->setWorkflow(!$can_edit) + ->setWorkflow(true) ->setDisabled(!$can_edit) - ->setHref($this->getApplicationURI("/question/{$href}/{$id}/"))); + ->setHref($this->getApplicationURI("/question/status/{$id}/"))); $view->addAction( id(new PhabricatorActionView()) @@ -154,57 +171,30 @@ final class PonderQuestionViewController extends PonderController { $view->invokeWillRenderEvent(); - $votable = id(new PonderVotableView()) - ->setPHID($question->getPHID()) - ->setURI($this->getApplicationURI('vote/')) - ->setCount($question->getVoteCount()) - ->setVote($question->getUserVote()); - - $view->addSectionHeader(pht('Question')); - $view->addTextContent( - array( - $votable, - phutil_tag( - 'div', - array( - 'class' => 'phabricator-remarkup', - ), - PhabricatorMarkupEngine::renderOneObject( + $details = PhabricatorMarkupEngine::renderOneObject( $question, $question->getMarkupField(), - $viewer)), - )); + $viewer); + if ($details) { + $view->addSectionHeader( + pht('Details'), + PHUIPropertyListView::ICON_SUMMARY); + + $view->addTextContent( + array( + phutil_tag( + 'div', + array( + 'class' => 'phabricator-remarkup', + ), + $details), + )); + } return $view; } - private function buildQuestionTransactions(PonderQuestion $question) { - $viewer = $this->getViewer(); - $id = $question->getID(); - - $timeline = $this->buildTransactionTimeline( - $question, - id(new PonderQuestionTransactionQuery()) - ->withTransactionTypes(array(PhabricatorTransactions::TYPE_COMMENT))); - $xactions = $timeline->getTransactions(); - - $add_comment = id(new PhabricatorApplicationTransactionCommentView()) - ->setUser($viewer) - ->setObjectPHID($question->getPHID()) - ->setShowPreview(false) - ->setHeaderText(pht('Question Comment')) - ->setAction($this->getApplicationURI("/question/comment/{$id}/")) - ->setSubmitButtonName(pht('Comment')); - - return $this->wrapComments( - count($xactions), - array( - $timeline, - $add_comment, - )); - } - /** * This is fairly non-standard; building N timelines at once (N = number of * answers) is tricky business. @@ -215,8 +205,6 @@ final class PonderQuestionViewController extends PonderController { private function buildAnswers(array $answers) { $viewer = $this->getViewer(); - $out = array(); - $xactions = id(new PonderAnswerTransactionQuery()) ->setViewer($viewer) ->withTransactionTypes(array(PhabricatorTransactions::TYPE_COMMENT)) @@ -236,168 +224,62 @@ final class PonderQuestionViewController extends PonderController { $xaction_groups = mgroup($xactions, 'getObjectPHID'); + $view = array(); foreach ($answers as $answer) { - $author_phid = $answer->getAuthorPHID(); $xactions = idx($xaction_groups, $answer->getPHID(), array()); $id = $answer->getID(); - $out[] = phutil_tag('br'); - $out[] = phutil_tag('br'); - $out[] = id(new PhabricatorAnchorView()) - ->setAnchorName("A$id"); - $header = id(new PHUIHeaderView()) - ->setHeader($viewer->renderHandle($author_phid)); - - $actions = $this->buildAnswerActions($answer); - $properties = $this->buildAnswerProperties($answer, $actions); - - $object_box = id(new PHUIObjectBoxView()) - ->setHeader($header) - ->addPropertyList($properties); - - $out[] = $object_box; - $details = array(); - - $details[] = id(new PhabricatorApplicationTransactionView()) + $view[] = id(new PonderAnswerView()) ->setUser($viewer) - ->setObjectPHID($answer->getPHID()) + ->setAnswer($answer) ->setTransactions($xactions) ->setMarkupEngine($engine); - $form = id(new PhabricatorApplicationTransactionCommentView()) - ->setUser($viewer) - ->setObjectPHID($answer->getPHID()) - ->setShowPreview(false) - ->setHeaderText(pht('Answer Comment')) - ->setAction($this->getApplicationURI("/answer/comment/{$id}/")) - ->setSubmitButtonName(pht('Comment')); - - $details[] = $form; - - $out[] = $this->wrapComments( - count($xactions), - $details); } - $out[] = phutil_tag('br'); - $out[] = phutil_tag('br'); - - return $out; - } - - private function buildAnswerActions(PonderAnswer $answer) { - $viewer = $this->getViewer(); - $request = $this->getRequest(); - $id = $answer->getID(); - - $can_edit = PhabricatorPolicyFilter::hasCapability( - $viewer, - $answer, - PhabricatorPolicyCapability::CAN_EDIT); - - $view = id(new PhabricatorActionListView()) - ->setUser($viewer) - ->setObject($answer) - ->setObjectURI($request->getRequestURI()); - - $view->addAction( - id(new PhabricatorActionView()) - ->setIcon('fa-pencil') - ->setName(pht('Edit Answer')) - ->setHref($this->getApplicationURI("/answer/edit/{$id}/")) - ->setDisabled(!$can_edit) - ->setWorkflow(!$can_edit)); - - $view->addAction( - id(new PhabricatorActionView()) - ->setIcon('fa-list') - ->setName(pht('View History')) - ->setHref($this->getApplicationURI("/answer/history/{$id}/"))); - return $view; } - private function buildAnswerProperties( - PonderAnswer $answer, - PhabricatorActionListView $actions) { - + private function buildSidebar(PonderQuestion $question) { $viewer = $this->getViewer(); - $view = id(new PHUIPropertyListView()) + $status = $question->getStatus(); + $id = $question->getID(); + + $questions = id(new PonderQuestionQuery()) + ->setViewer($viewer) + ->withStatuses(array($status)) + ->withEdgeLogicPHIDs( + PhabricatorProjectObjectHasProjectEdgeType::EDGECONST, + PhabricatorQueryConstraint::OPERATOR_OR, + $question->getProjectPHIDs()) + ->setLimit(10) + ->execute(); + + $list = id(new PHUIObjectItemListView()) ->setUser($viewer) - ->setObject($answer) - ->setActionList($actions); + ->setNoDataString(pht('No similar questions found.')); - $view->addProperty( - pht('Created'), - phabricator_datetime($answer->getDateCreated(), $viewer)); + foreach ($questions as $question) { + if ($id == $question->getID()) { + continue; + } + $item = new PHUIObjectItemView(); + $item->setObjectName('Q'.$question->getID()); + $item->setHeader($question->getTitle()); + $item->setHref('/Q'.$question->getID()); + $item->setObject($question); - $view->invokeWillRenderEvent(); + $item->addAttribute( + pht('%d Answer(s)', $question->getAnswerCount())); - $votable = id(new PonderVotableView()) - ->setPHID($answer->getPHID()) - ->setURI($this->getApplicationURI('vote/')) - ->setCount($answer->getVoteCount()) - ->setVote($answer->getUserVote()); - - $view->addSectionHeader(pht('Answer')); - $view->addTextContent( - array( - $votable, - phutil_tag( - 'div', - array( - 'class' => 'phabricator-remarkup', - ), - PhabricatorMarkupEngine::renderOneObject( - $answer, - $answer->getMarkupField(), - $viewer)), - )); - - return $view; - } - - private function wrapComments($n, $stuff) { - if ($n == 0) { - $text = pht('Add a Comment'); - } else { - $text = pht('Show %s Comments', new PhutilNumber($n)); + $list->addItem($item); } - $show_id = celerity_generate_unique_node_id(); - $hide_id = celerity_generate_unique_node_id(); + $box = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Similar Questions')) + ->setObjectList($list); - Javelin::initBehavior('phabricator-reveal-content'); - require_celerity_resource('ponder-view-css'); - - $show = phutil_tag( - 'div', - array( - 'id' => $show_id, - 'class' => 'ponder-show-comments', - ), - javelin_tag( - 'a', - array( - 'href' => '#', - 'sigil' => 'reveal-content', - 'meta' => array( - 'showIDs' => array($hide_id), - 'hideIDs' => array($show_id), - ), - ), - $text)); - - $hide = phutil_tag( - 'div', - array( - 'class' => 'ponder-comments-view', - 'id' => $hide_id, - 'style' => 'display: none', - ), - $stuff); - - return array($show, $hide); + return $box; } } diff --git a/src/applications/ponder/controller/PonderVoteSaveController.php b/src/applications/ponder/controller/PonderVoteSaveController.php deleted file mode 100644 index 12aa4dfb15..0000000000 --- a/src/applications/ponder/controller/PonderVoteSaveController.php +++ /dev/null @@ -1,32 +0,0 @@ -getViewer(); - $phid = $request->getStr('phid'); - $newvote = $request->getInt('vote'); - - if (1 < $newvote || $newvote < -1) { - return new Aphront400Response(); - } - - $target = null; - - $object = id(new PhabricatorObjectQuery()) - ->setViewer($viewer) - ->withPHIDs(array($phid)) - ->executeOne(); - if (!$object) { - return new Aphront404Response(); - } - - $editor = id(new PonderVoteEditor()) - ->setVotable($object) - ->setActor($viewer) - ->setVote($newvote) - ->saveVote(); - - return id(new AphrontAjaxResponse())->setContent(array()); - } -} diff --git a/src/applications/ponder/edge/PonderQuestionHasVotingUserEdgeType.php b/src/applications/ponder/edge/PonderQuestionHasVotingUserEdgeType.php deleted file mode 100644 index a035942d24..0000000000 --- a/src/applications/ponder/edge/PonderQuestionHasVotingUserEdgeType.php +++ /dev/null @@ -1,105 +0,0 @@ -getTransactionType()) { case PonderAnswerTransaction::TYPE_CONTENT: + case PonderAnswerTransaction::TYPE_STATUS: return $object->getContent(); + case PonderAnswerTransaction::TYPE_QUESTION_ID: + return $object->getQuestionID(); } } @@ -31,6 +37,8 @@ final class PonderAnswerEditor extends PonderEditor { switch ($xaction->getTransactionType()) { case PonderAnswerTransaction::TYPE_CONTENT: + case PonderAnswerTransaction::TYPE_STATUS: + case PonderAnswerTransaction::TYPE_QUESTION_ID: return $xaction->getNewValue(); } } @@ -43,6 +51,12 @@ final class PonderAnswerEditor extends PonderEditor { case PonderAnswerTransaction::TYPE_CONTENT: $object->setContent($xaction->getNewValue()); break; + case PonderAnswerTransaction::TYPE_STATUS: + $object->setStatus($xaction->getNewValue()); + break; + case PonderAnswerTransaction::TYPE_QUESTION_ID: + $object->setQuestionID($xaction->getNewValue()); + break; } } diff --git a/src/applications/ponder/editor/PonderQuestionEditor.php b/src/applications/ponder/editor/PonderQuestionEditor.php index af984f4b4d..eeac4b3414 100644 --- a/src/applications/ponder/editor/PonderQuestionEditor.php +++ b/src/applications/ponder/editor/PonderQuestionEditor.php @@ -212,6 +212,19 @@ final class PonderQuestionEditor return true; } + public function getMailTagsMap() { + return array( + PonderQuestionTransaction::MAILTAG_DETAILS => + pht('Someone changes the questions details.'), + PonderQuestionTransaction::MAILTAG_ANSWERS => + pht('Someone adds a new answer.'), + PonderQuestionTransaction::MAILTAG_COMMENT => + pht('Someone comments on the question.'), + PonderQuestionTransaction::MAILTAG_OTHER => + pht('Other question activity not listed above occurs.'), + ); + } + protected function buildReplyHandler(PhabricatorLiskDAO $object) { return id(new PonderQuestionReplyHandler()) ->setMailReceiver($object); @@ -252,4 +265,18 @@ final class PonderQuestionEditor return $body; } + protected function shouldApplyHeraldRules( + PhabricatorLiskDAO $object, + array $xactions) { + return true; + } + + protected function buildHeraldAdapter( + PhabricatorLiskDAO $object, + array $xactions) { + + return id(new HeraldPonderQuestionAdapter()) + ->setQuestion($object); + } + } diff --git a/src/applications/ponder/herald/HeraldPonderQuestionAdapter.php b/src/applications/ponder/herald/HeraldPonderQuestionAdapter.php new file mode 100644 index 0000000000..3a21457c3e --- /dev/null +++ b/src/applications/ponder/herald/HeraldPonderQuestionAdapter.php @@ -0,0 +1,62 @@ +question = $this->newObject(); + } + + public function supportsApplicationEmail() { + return true; + } + + public function getRepetitionOptions() { + return array( + HeraldRepetitionPolicyConfig::EVERY, + HeraldRepetitionPolicyConfig::FIRST, + ); + } + + public function supportsRuleType($rule_type) { + switch ($rule_type) { + case HeraldRuleTypeConfig::RULE_TYPE_GLOBAL: + case HeraldRuleTypeConfig::RULE_TYPE_PERSONAL: + return true; + case HeraldRuleTypeConfig::RULE_TYPE_OBJECT: + default: + return false; + } + } + + public function setQuestion(PonderQuestion $question) { + $this->question = $question; + return $this; + } + + public function getObject() { + return $this->question; + } + + public function getAdapterContentName() { + return pht('Ponder Questions'); + } + + public function getHeraldName() { + return 'Q'.$this->getObject()->getID(); + } + +} diff --git a/src/applications/ponder/query/PonderAnswerQuery.php b/src/applications/ponder/query/PonderAnswerQuery.php index be9c42c5ea..4e874aeb60 100644 --- a/src/applications/ponder/query/PonderAnswerQuery.php +++ b/src/applications/ponder/query/PonderAnswerQuery.php @@ -101,7 +101,6 @@ final class PonderAnswerQuery $edges[$answer->getPHID()][$etype], $viewer_phid, array()); - $answer->attachUserVote($viewer_phid, idx($user_edge, 'data', 0)); } } diff --git a/src/applications/ponder/query/PonderQuestionQuery.php b/src/applications/ponder/query/PonderQuestionQuery.php index 74d8230327..dd5db40a48 100644 --- a/src/applications/ponder/query/PonderQuestionQuery.php +++ b/src/applications/ponder/query/PonderQuestionQuery.php @@ -5,19 +5,13 @@ final class PonderQuestionQuery private $ids; private $phids; + private $status; private $authorPHIDs; private $answererPHIDs; private $needProjectPHIDs; - private $status = 'status-any'; - - const STATUS_ANY = 'status-any'; - const STATUS_OPEN = 'status-open'; - const STATUS_CLOSED = 'status-closed'; - private $needAnswers; - private $needViewerVotes; public function withIDs(array $ids) { $this->ids = $ids; @@ -34,7 +28,7 @@ final class PonderQuestionQuery return $this; } - public function withStatus($status) { + public function withStatuses($status) { $this->status = $status; return $this; } @@ -49,11 +43,6 @@ final class PonderQuestionQuery return $this; } - public function needViewerVotes($need_viewer_votes) { - $this->needViewerVotes = $need_viewer_votes; - return $this; - } - public function needProjectPHIDs($need_projects) { $this->needProjectPHIDs = $need_projects; return $this; @@ -84,24 +73,10 @@ final class PonderQuestionQuery } if ($this->status !== null) { - switch ($this->status) { - case self::STATUS_ANY: - break; - case self::STATUS_OPEN: - $where[] = qsprintf( - $conn, - 'q.status = %d', - PonderQuestionStatus::STATUS_OPEN); - break; - case self::STATUS_CLOSED: - $where[] = qsprintf( - $conn, - 'q.status = %d', - PonderQuestionStatus::STATUS_CLOSED); - break; - default: - throw new Exception(pht("Unknown status query '%s'!", $this->status)); - } + $where[] = qsprintf( + $conn, + 'q.status IN (%Ls)', + $this->status); } return $where; @@ -123,12 +98,9 @@ final class PonderQuestionQuery $aquery = id(new PonderAnswerQuery()) ->setViewer($this->getViewer()) ->setOrderVector(array('-id')) + ->needViewerVotes(true) ->withQuestionIDs(mpull($questions, 'getID')); - if ($this->needViewerVotes) { - $aquery->needViewerVotes($this->needViewerVotes); - } - $answers = $aquery->execute(); $answers = mgroup($answers, 'getQuestionID'); @@ -138,26 +110,6 @@ final class PonderQuestionQuery } } - if ($this->needViewerVotes) { - $viewer_phid = $this->getViewer()->getPHID(); - - $etype = PonderQuestionHasVotingUserEdgeType::EDGECONST; - $edges = id(new PhabricatorEdgeQuery()) - ->withSourcePHIDs($phids) - ->withDestinationPHIDs(array($viewer_phid)) - ->withEdgeTypes(array($etype)) - ->needEdgeData(true) - ->execute(); - foreach ($questions as $question) { - $user_edge = idx( - $edges[$question->getPHID()][$etype], - $viewer_phid, - array()); - - $question->attachUserVote($viewer_phid, idx($user_edge, 'data', 0)); - } - } - if ($this->needProjectPHIDs) { $edge_query = id(new PhabricatorEdgeQuery()) ->withSourcePHIDs($phids) diff --git a/src/applications/ponder/query/PonderQuestionSearchEngine.php b/src/applications/ponder/query/PonderQuestionSearchEngine.php index 1b475c7b58..b4cb7aef21 100644 --- a/src/applications/ponder/query/PonderQuestionSearchEngine.php +++ b/src/applications/ponder/query/PonderQuestionSearchEngine.php @@ -27,16 +27,8 @@ final class PonderQuestionSearchEngine $query->withAnswererPHIDs($map['answerers']); } - $status = $map['status']; - if ($status != null) { - switch ($status) { - case 0: - $query->withStatus(PonderQuestionQuery::STATUS_OPEN); - break; - case 1: - $query->withStatus(PonderQuestionQuery::STATUS_CLOSED); - break; - } + if ($map['statuses']) { + $query->withStatuses($map['statuses']); } return $query; @@ -52,9 +44,9 @@ final class PonderQuestionSearchEngine ->setKey('answerers') ->setAliases(array('answerers')) ->setLabel(pht('Answered By')), - id(new PhabricatorSearchSelectField()) + id(new PhabricatorSearchCheckboxesField()) ->setLabel(pht('Status')) - ->setKey('status') + ->setKey('statuses') ->setOptions(PonderQuestionStatus::getQuestionStatusMap()), ); } @@ -65,7 +57,9 @@ final class PonderQuestionSearchEngine protected function getBuiltinQueryNames() { $names = array( + 'recent' => pht('Recent Questions'), 'open' => pht('Open Questions'), + 'resolved' => pht('Resolved Questions'), 'all' => pht('All Questions'), ); @@ -85,7 +79,17 @@ final class PonderQuestionSearchEngine case 'all': return $query; case 'open': - return $query->setParameter('status', PonderQuestionQuery::STATUS_OPEN); + return $query->setParameter( + 'statuses', array(PonderQuestionStatus::STATUS_OPEN)); + case 'recent': + return $query->setParameter( + 'statuses', array( + PonderQuestionStatus::STATUS_OPEN, + PonderQuestionStatus::STATUS_CLOSED_RESOLVED, + )); + case 'resolved': + return $query->setParameter( + 'statuses', array(PonderQuestionStatus::STATUS_CLOSED_RESOLVED)); case 'authored': return $query->setParameter( 'authorPHIDs', diff --git a/src/applications/ponder/storage/PonderAnswer.php b/src/applications/ponder/storage/PonderAnswer.php index 10ada70c5e..2c8aff23da 100644 --- a/src/applications/ponder/storage/PonderAnswer.php +++ b/src/applications/ponder/storage/PonderAnswer.php @@ -8,7 +8,6 @@ final class PonderAnswer extends PonderDAO PhabricatorPolicyInterface, PhabricatorFlaggableInterface, PhabricatorSubscribableInterface, - PhabricatorTokenReceiverInterface, PhabricatorDestructibleInterface { const MARKUP_FIELD_CONTENT = 'markup:content'; @@ -17,16 +16,31 @@ final class PonderAnswer extends PonderDAO protected $questionID; protected $content; - protected $contentSource; protected $mailKey; - + protected $status; protected $voteCount; + private $vote; private $question = self::ATTACHABLE; private $comments; private $userVotes = array(); + public static function initializeNewAnswer(PhabricatorUser $actor) { + $app = id(new PhabricatorApplicationQuery()) + ->setViewer($actor) + ->withClasses(array('PhabricatorPonderApplication')) + ->executeOne(); + + return id(new PonderAnswer()) + ->setQuestionID(0) + ->setContent('') + ->setAuthorPHID($actor->getPHID()) + ->setVoteCount(0) + ->setStatus(PonderAnswerStatus::ANSWER_STATUS_VISIBLE); + + } + public function attachQuestion(PonderQuestion $question = null) { $this->question = $question; return $this; @@ -72,11 +86,8 @@ final class PonderAnswer extends PonderDAO self::CONFIG_COLUMN_SCHEMA => array( 'voteCount' => 'sint32', 'content' => 'text', + 'status' => 'text32', 'mailKey' => 'bytes20', - - // T6203/NULLABILITY - // This should always exist. - 'contentSource' => 'text?', ), self::CONFIG_KEY_SCHEMA => array( 'key_phid' => null, @@ -94,6 +105,9 @@ final class PonderAnswer extends PonderDAO 'authorPHID' => array( 'columns' => array('authorPHID'), ), + 'status' => array( + 'columns' => array('status'), + ), ), ) + parent::getConfiguration(); } @@ -102,15 +116,6 @@ final class PonderAnswer extends PonderDAO return PhabricatorPHID::generateNewPHID(PonderAnswerPHIDType::TYPECONST); } - public function setContentSource(PhabricatorContentSource $content_source) { - $this->contentSource = $content_source->serialize(); - return $this; - } - - public function getContentSource() { - return PhabricatorContentSource::newFromSerialized($this->contentSource); - } - public function getMarkupField() { return self::MARKUP_FIELD_CONTENT; } @@ -198,7 +203,9 @@ final class PonderAnswer extends PonderDAO case PhabricatorPolicyCapability::CAN_VIEW: return $this->getQuestion()->getPolicy($capability); case PhabricatorPolicyCapability::CAN_EDIT: - return PhabricatorPolicies::POLICY_NOONE; + $app = PhabricatorApplication::getByClass( + 'PhabricatorPonderApplication'); + return $app->getPolicy(PonderModerateCapability::CAPABILITY); } } @@ -224,22 +231,14 @@ final class PonderAnswer extends PonderDAO case PhabricatorPolicyCapability::CAN_VIEW: $out[] = pht( 'The user who asks a question can always view the answers.'); + $out[] = pht( + 'A moderator can always view the answers.'); break; } return $out; } -/* -( PhabricatorTokenReceiverInterface )---------------------------------- */ - - - public function getUsersToNotifyOfTokenGiven() { - return array( - $this->getAuthorPHID(), - ); - } - - /* -( PhabricatorSubscribableInterface )----------------------------------- */ diff --git a/src/applications/ponder/storage/PonderAnswerTransaction.php b/src/applications/ponder/storage/PonderAnswerTransaction.php index 08af2470ad..c13cb07b87 100644 --- a/src/applications/ponder/storage/PonderAnswerTransaction.php +++ b/src/applications/ponder/storage/PonderAnswerTransaction.php @@ -4,6 +4,8 @@ final class PonderAnswerTransaction extends PhabricatorApplicationTransaction { const TYPE_CONTENT = 'ponder.answer:content'; + const TYPE_STATUS = 'ponder.answer:status'; + const TYPE_QUESTION_ID = 'ponder.answer:question-id'; public function getApplicationName() { return 'ponder'; @@ -26,6 +28,7 @@ final class PonderAnswerTransaction switch ($this->getTransactionType()) { case self::TYPE_CONTENT: + case self::TYPE_STATUS: $phids[] = $this->getObjectPHID(); break; } @@ -45,16 +48,48 @@ final class PonderAnswerTransaction return $blocks; } + public function shouldHide() { + switch ($this->getTransactionType()) { + case self::TYPE_QUESTION_ID: + return true; + } + return parent::shouldHide(); + } + public function getTitle() { $author_phid = $this->getAuthorPHID(); $object_phid = $this->getObjectPHID(); + $old = $this->getOldValue(); + $new = $this->getNewValue(); + switch ($this->getTransactionType()) { case self::TYPE_CONTENT: - return pht( - '%s edited %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid)); + if ($old === '') { + return pht( + '%s added %s.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($object_phid)); + } else { + return pht( + '%s edited %s.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($object_phid)); + } + break; + case self::TYPE_STATUS: + if ($new == PonderAnswerStatus::ANSWER_STATUS_VISIBLE) { + return pht( + '%s marked %s as visible.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($object_phid)); + } else if ($new == PonderAnswerStatus::ANSWER_STATUS_HIDDEN) { + return pht( + '%s marked %s as hidden.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($object_phid)); + } + break; } return parent::getTitle(); @@ -64,12 +99,36 @@ final class PonderAnswerTransaction $author_phid = $this->getAuthorPHID(); $object_phid = $this->getObjectPHID(); + $old = $this->getOldValue(); + $new = $this->getNewValue(); + switch ($this->getTransactionType()) { case self::TYPE_CONTENT: - return pht( - '%s updated %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid)); + if ($old === '') { + return pht( + '%s added %s.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($object_phid)); + } else { + return pht( + '%s updated %s.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($object_phid)); + } + break; + case self::TYPE_STATUS: + if ($new == PonderAnswerStatus::ANSWER_STATUS_VISIBLE) { + return pht( + '%s marked %s as visible.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($object_phid)); + } else if ($new == PonderAnswerStatus::ANSWER_STATUS_HIDDEN) { + return pht( + '%s marked %s as hidden.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($object_phid)); + } + break; } return parent::getTitleForFeed(); diff --git a/src/applications/ponder/storage/PonderQuestion.php b/src/applications/ponder/storage/PonderQuestion.php index f2d81c4838..bc71690387 100644 --- a/src/applications/ponder/storage/PonderQuestion.php +++ b/src/applications/ponder/storage/PonderQuestion.php @@ -4,7 +4,6 @@ final class PonderQuestion extends PonderDAO implements PhabricatorApplicationTransactionInterface, PhabricatorMarkupInterface, - PonderVotableInterface, PhabricatorSubscribableInterface, PhabricatorFlaggableInterface, PhabricatorPolicyInterface, @@ -23,16 +22,12 @@ final class PonderQuestion extends PonderDAO protected $content; protected $contentSource; protected $viewPolicy; - protected $editPolicy; protected $spacePHID; - protected $voteCount; protected $answerCount; - protected $heat; protected $mailKey; private $answers; - private $vote; private $comments; private $projectPHIDs = self::ATTACHABLE; @@ -44,18 +39,13 @@ final class PonderQuestion extends PonderDAO ->executeOne(); $view_policy = $app->getPolicy( - PonderQuestionDefaultViewCapability::CAPABILITY); - $edit_policy = $app->getPolicy( - PonderQuestionDefaultEditCapability::CAPABILITY); + PonderDefaultViewCapability::CAPABILITY); return id(new PonderQuestion()) ->setAuthorPHID($actor->getPHID()) ->setViewPolicy($view_policy) - ->setEditPolicy($edit_policy) ->setStatus(PonderQuestionStatus::STATUS_OPEN) - ->setVoteCount(0) ->setAnswerCount(0) - ->setHeat(0.0) ->setSpacePHID($actor->getDefaultSpacePHID()); } @@ -64,10 +54,8 @@ final class PonderQuestion extends PonderDAO self::CONFIG_AUX_PHID => true, self::CONFIG_COLUMN_SCHEMA => array( 'title' => 'text255', - 'voteCount' => 'sint32', - 'status' => 'uint32', + 'status' => 'text32', 'content' => 'text', - 'heat' => 'double', 'answerCount' => 'uint32', 'mailKey' => 'bytes20', @@ -84,9 +72,6 @@ final class PonderQuestion extends PonderDAO 'authorPHID' => array( 'columns' => array('authorPHID'), ), - 'heat' => array( - 'columns' => array('heat'), - ), 'status' => array( 'columns' => array('status'), ), @@ -107,49 +92,6 @@ final class PonderQuestion extends PonderDAO return PhabricatorContentSource::newFromSerialized($this->contentSource); } - public function attachVotes($user_phid) { - $qa_phids = mpull($this->answers, 'getPHID') + array($this->getPHID()); - - $edges = id(new PhabricatorEdgeQuery()) - ->withSourcePHIDs(array($user_phid)) - ->withDestinationPHIDs($qa_phids) - ->withEdgeTypes( - array( - PonderVotingUserHasQuestionEdgeType::EDGECONST, - PonderVotingUserHasAnswerEdgeType::EDGECONST, - )) - ->needEdgeData(true) - ->execute(); - - $question_edge = - $edges[$user_phid][PonderVotingUserHasQuestionEdgeType::EDGECONST]; - $answer_edges = - $edges[$user_phid][PonderVotingUserHasAnswerEdgeType::EDGECONST]; - $edges = null; - - $this->setUserVote(idx($question_edge, $this->getPHID())); - foreach ($this->answers as $answer) { - $answer->setUserVote(idx($answer_edges, $answer->getPHID())); - } - } - - public function setUserVote($vote) { - $this->vote = $vote['data']; - if (!$this->vote) { - $this->vote = PonderVote::VOTE_NONE; - } - return $this; - } - - public function attachUserVote($user_phid, $vote) { - $this->vote = $vote; - return $this; - } - - public function getUserVote() { - return $this->vote; - } - public function setComments($comments) { $this->comments = $comments; return $this; @@ -233,15 +175,6 @@ final class PonderQuestion extends PonderDAO return (bool)$this->getID(); } - // votable interface - public function getUserVoteEdgeType() { - return PonderVotingUserHasQuestionEdgeType::EDGECONST; - } - - public function getVotablePHID() { - return $this->getPHID(); - } - public function save() { if (!$this->getMailKey()) { $this->setMailKey(Filesystem::readRandomCharacters(20)); @@ -275,17 +208,33 @@ final class PonderQuestion extends PonderDAO case PhabricatorPolicyCapability::CAN_VIEW: return $this->getViewPolicy(); case PhabricatorPolicyCapability::CAN_EDIT: - return $this->getEditPolicy(); + $app = PhabricatorApplication::getByClass( + 'PhabricatorPonderApplication'); + return $app->getPolicy(PonderModerateCapability::CAPABILITY); } } public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { + if ($capability == PhabricatorPolicyCapability::CAN_VIEW) { + if (PhabricatorPolicyFilter::hasCapability( + $viewer, $this, PhabricatorPolicyCapability::CAN_EDIT)) { + return true; + } + } return ($viewer->getPHID() == $this->getAuthorPHID()); } public function describeAutomaticCapability($capability) { - return pht('The user who asked a question can always view and edit it.'); + $out = array(); + $out[] = pht('The user who asked a question can always view and edit it.'); + switch ($capability) { + case PhabricatorPolicyCapability::CAN_VIEW: + $out[] = pht( + 'A moderator can always view the question.'); + break; + } + return $out; } diff --git a/src/applications/ponder/storage/PonderQuestionTransaction.php b/src/applications/ponder/storage/PonderQuestionTransaction.php index 0e4d07b150..3c2734ea71 100644 --- a/src/applications/ponder/storage/PonderQuestionTransaction.php +++ b/src/applications/ponder/storage/PonderQuestionTransaction.php @@ -8,6 +8,11 @@ final class PonderQuestionTransaction const TYPE_ANSWERS = 'ponder.question:answer'; const TYPE_STATUS = 'ponder.question:status'; + const MAILTAG_DETAILS = 'question:details'; + const MAILTAG_COMMENT = 'question:comment'; + const MAILTAG_ANSWERS = 'question:answer'; + const MAILTAG_OTHER = 'question:other'; + public function getApplicationName() { return 'ponder'; } @@ -87,9 +92,17 @@ final class PonderQuestionTransaction return pht( '%s reopened this question.', $this->renderHandleLink($author_phid)); - case PonderQuestionStatus::STATUS_CLOSED: + case PonderQuestionStatus::STATUS_CLOSED_RESOLVED: return pht( - '%s closed this question.', + '%s closed this question as resolved.', + $this->renderHandleLink($author_phid)); + case PonderQuestionStatus::STATUS_CLOSED_OBSOLETE: + return pht( + '%s closed this question as obsolete.', + $this->renderHandleLink($author_phid)); + case PonderQuestionStatus::STATUS_CLOSED_DUPLICATE: + return pht( + '%s closed this question as a duplicate.', $this->renderHandleLink($author_phid)); } } @@ -97,6 +110,28 @@ final class PonderQuestionTransaction return parent::getTitle(); } + public function getMailTags() { + $tags = parent::getMailTags(); + + switch ($this->getTransactionType()) { + case PhabricatorTransactions::TYPE_COMMENT: + $tags[] = self::MAILTAG_COMMENT; + break; + case self::TYPE_TITLE: + case self::TYPE_CONTENT: + case self::TYPE_STATUS: + $tags[] = self::MAILTAG_DETAILS; + break; + case self::TYPE_ANSWERS: + $tags[] = self::MAILTAG_ANSWERS; + break; + default: + $tags[] = self::MAILTAG_OTHER; + break; + } + return $tags; + } + public function getIcon() { $old = $this->getOldValue(); $new = $this->getNewValue(); @@ -106,12 +141,7 @@ final class PonderQuestionTransaction case self::TYPE_CONTENT: return 'fa-pencil'; case self::TYPE_STATUS: - switch ($new) { - case PonderQuestionStatus::STATUS_OPEN: - return 'fa-check-circle'; - case PonderQuestionStatus::STATUS_CLOSED: - return 'fa-minus-circle'; - } + return PonderQuestionStatus::getQuestionStatusIcon($new); case self::TYPE_ANSWERS: return 'fa-plus'; } @@ -130,12 +160,7 @@ final class PonderQuestionTransaction case self::TYPE_ANSWERS: return PhabricatorTransactions::COLOR_GREEN; case self::TYPE_STATUS: - switch ($new) { - case PonderQuestionStatus::STATUS_OPEN: - return PhabricatorTransactions::COLOR_GREEN; - case PonderQuestionStatus::STATUS_CLOSED: - return PhabricatorTransactions::COLOR_INDIGO; - } + return PonderQuestionStatus::getQuestionStatusTagColor($new); } } @@ -239,12 +264,22 @@ final class PonderQuestionTransaction switch ($new) { case PonderQuestionStatus::STATUS_OPEN: return pht( - '%s reopened %s', + '%s reopened %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); - case PonderQuestionStatus::STATUS_CLOSED: + case PonderQuestionStatus::STATUS_CLOSED_RESOLVED: return pht( - '%s closed %s', + '%s closed %s as resolved.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($object_phid)); + case PonderQuestionStatus::STATUS_CLOSED_DUPLICATE: + return pht( + '%s closed %s as duplicate.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($object_phid)); + case PonderQuestionStatus::STATUS_CLOSED_OBSOLETE: + return pht( + '%s closed %s as obsolete.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); } diff --git a/src/applications/ponder/view/PonderAddAnswerView.php b/src/applications/ponder/view/PonderAddAnswerView.php index 53e06074f7..5ffe99edc7 100644 --- a/src/applications/ponder/view/PonderAddAnswerView.php +++ b/src/applications/ponder/view/PonderAddAnswerView.php @@ -18,6 +18,28 @@ final class PonderAddAnswerView extends AphrontView { public function render() { $question = $this->question; + $viewer = $this->user; + + $authors = mpull($question->getAnswers(), null, 'getAuthorPHID'); + if (isset($authors[$viewer->getPHID()])) { + return id(new PHUIInfoView()) + ->setSeverity(PHUIInfoView::SEVERITY_NOTICE) + ->setTitle(pht('Already Answered')) + ->appendChild( + pht( + 'You have already answered this question. You can not answer '. + 'twice, but you can edit your existing answer.')); + } + + $info_panel = null; + if ($question->getStatus() != PonderQuestionStatus::STATUS_OPEN) { + $info_panel = id(new PHUIInfoView()) + ->setSeverity(PHUIInfoView::SEVERITY_WARNING) + ->appendChild( + pht( + 'This question has been marked as closed, + but you can still leave a new answer.')); + } $header = id(new PHUIHeaderView()) ->setHeader(pht('Add Answer')); @@ -39,8 +61,14 @@ final class PonderAddAnswerView extends AphrontView { id(new AphrontFormSubmitControl()) ->setValue(pht('Add Answer'))); - return id(new PHUIObjectBoxView()) + $box = id(new PHUIObjectBoxView()) ->setHeader($header) ->appendChild($form); + + if ($info_panel) { + $box->setInfoView($info_panel); + } + + return $box; } } diff --git a/src/applications/ponder/view/PonderAnswerView.php b/src/applications/ponder/view/PonderAnswerView.php new file mode 100644 index 0000000000..532bc9f2cd --- /dev/null +++ b/src/applications/ponder/view/PonderAnswerView.php @@ -0,0 +1,200 @@ +answer = $answer; + return $this; + } + + public function setTransactions($transactions) { + $this->transactions = $transactions; + return $this; + } + + public function setMarkupEngine(PhabricatorMarkupEngine $engine) { + $this->engine = $engine; + return $this; + } + + protected function getTagAttributes() { + return array( + 'class' => 'ponder-answer-view', + ); + } + + protected function getTagContent() { + require_celerity_resource('ponder-view-css'); + $answer = $this->answer; + $viewer = $this->getUser(); + $status = $answer->getStatus(); + $author_phid = $answer->getAuthorPHID(); + $actions = $this->buildAnswerActions(); + $id = $answer->getID(); + + if ($status == PonderAnswerStatus::ANSWER_STATUS_HIDDEN) { + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $answer, + PhabricatorPolicyCapability::CAN_EDIT); + + $message = array(); + $message[] = phutil_tag( + 'em', + array(), + pht('This answer has been hidden.')); + + if ($can_edit) { + $message[] = phutil_tag( + 'a', + array( + 'href' => "/ponder/answer/edit/{$id}/", + ), + pht('Edit Answer')); + } + $message = phutil_implode_html(' ', $message); + + return id(new PHUIInfoView()) + ->setSeverity(PHUIInfoView::SEVERITY_NODATA) + ->appendChild($message); + } + + $action_button = id(new PHUIButtonView()) + ->setTag('a') + ->setText(pht('Actions')) + ->setHref('#') + ->setIconFont('fa-bars') + ->setDropdownMenu($actions); + + $header = id(new PHUIHeaderView()) + ->setUser($viewer) + ->setEpoch($answer->getDateCreated()) + ->setHeader($viewer->renderHandle($author_phid)) + ->addActionLink($action_button); + + $content = phutil_tag( + 'div', + array( + 'class' => 'phabricator-remarkup mlt mlb msr msl', + ), + PhabricatorMarkupEngine::renderOneObject( + $answer, + $answer->getMarkupField(), + $viewer)); + + $anchor = id(new PhabricatorAnchorView()) + ->setAnchorName("A$id"); + + $content_id = celerity_generate_unique_node_id(); + $footer = id(new PonderFooterView()) + ->setContentID($content_id) + ->setCount(count($this->transactions)); + + $votes = $answer->getVoteCount(); + if ($votes) { + $icon = id(new PHUIIconView()) + ->setIconFont('fa-thumbs-up'); + $helpful = phutil_tag( + 'span', + array( + 'class' => 'ponder-footer-action', + ), + array($votes, $icon)); + $footer->addAction($helpful); + } + + $answer_view = id(new PHUIObjectBoxView()) + ->setHeader($header) + ->appendChild($anchor) + ->appendChild($content) + ->appendChild($footer); + + $transaction_view = id(new PhabricatorApplicationTransactionView()) + ->setUser($viewer) + ->setObjectPHID($answer->getPHID()) + ->setTransactions($this->transactions) + ->setMarkupEngine($this->engine); + + $comment_view = id(new PhabricatorApplicationTransactionCommentView()) + ->setUser($viewer) + ->setObjectPHID($answer->getPHID()) + ->setShowPreview(false) + ->setHeaderText(pht('Answer Comment')) + ->setAction("/ponder/answer/comment/{$id}/") + ->setSubmitButtonName(pht('Comment')); + + $hidden_view = phutil_tag( + 'div', + array( + 'id' => $content_id, + 'style' => 'display: none;', + ), + array( + $transaction_view, + $comment_view, + )); + + return array( + $answer_view, + $hidden_view, + ); + } + + private function buildAnswerActions() { + $viewer = $this->getUser(); + $answer = $this->answer; + $id = $answer->getID(); + + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $answer, + PhabricatorPolicyCapability::CAN_EDIT); + + $view = id(new PhabricatorActionListView()) + ->setUser($viewer) + ->setObject($answer) + ->setObjectURI('Q'.$answer->getQuestionID()); + + $user_marked = $answer->getUserVote(); + $can_vote = $viewer->isLoggedIn(); + + if ($user_marked) { + $helpful_uri = "/ponder/answer/helpful/remove/{$id}/"; + $helpful_icon = 'fa-times'; + $helpful_text = pht('Remove Helpful'); + } else { + $helpful_uri = "/ponder/answer/helpful/add/{$id}/"; + $helpful_icon = 'fa-thumbs-up'; + $helpful_text = pht('Mark as Helpful'); + } + + $view->addAction( + id(new PhabricatorActionView()) + ->setIcon($helpful_icon) + ->setName($helpful_text) + ->setHref($helpful_uri) + ->setRenderAsForm(true) + ->setDisabled(!$can_vote) + ->setWorkflow($can_vote)); + + $view->addAction( + id(new PhabricatorActionView()) + ->setIcon('fa-pencil') + ->setName(pht('Edit Answer')) + ->setHref("/ponder/answer/edit/{$id}/") + ->setDisabled(!$can_edit) + ->setWorkflow(!$can_edit)); + + $view->addAction( + id(new PhabricatorActionView()) + ->setIcon('fa-list') + ->setName(pht('View History')) + ->setHref("/ponder/answer/history/{$id}/")); + + return $view; + } +} diff --git a/src/applications/ponder/view/PonderFooterView.php b/src/applications/ponder/view/PonderFooterView.php new file mode 100644 index 0000000000..8f55b86aeb --- /dev/null +++ b/src/applications/ponder/view/PonderFooterView.php @@ -0,0 +1,80 @@ +contentID = $content_id; + return $this; + } + + public function setCount($count) { + $this->count = $count; + return $this; + } + + public function addAction($action) { + $this->actions[] = $action; + return $this; + } + + protected function getTagAttributes() { + return array( + 'class' => 'ponder-footer-view', + ); + } + + protected function getTagContent() { + require_celerity_resource('ponder-view-css'); + Javelin::initBehavior('phabricator-reveal-content'); + + $hide_action_id = celerity_generate_unique_node_id(); + $show_action_id = celerity_generate_unique_node_id(); + $content_id = $this->contentID; + + if ($this->count == 0) { + $text = pht('Add a Comment'); + } else { + $text = pht('Show %s Comments', new PhutilNumber($this->count)); + } + + $actions = array(); + $hide_action = javelin_tag( + 'a', + array( + 'sigil' => 'reveal-content', + 'class' => 'ponder-footer-action', + 'id' => $hide_action_id, + 'href' => '#', + 'meta' => array( + 'showIDs' => array($content_id, $show_action_id), + 'hideIDs' => array($hide_action_id), + ), + ), + $text); + + $show_action = javelin_tag( + 'a', + array( + 'sigil' => 'reveal-content', + 'style' => 'display: none;', + 'class' => 'ponder-footer-action', + 'id' => $show_action_id, + 'href' => '#', + 'meta' => array( + 'showIDs' => array($hide_action_id), + 'hideIDs' => array($content_id, $show_action_id), + ), + ), + pht('Hide Comments')); + + $actions[] = $hide_action; + $actions[] = $show_action; + + return array($actions, $this->actions); + } + +} diff --git a/src/applications/ponder/view/PonderVotableView.php b/src/applications/ponder/view/PonderVotableView.php deleted file mode 100644 index dbd6ba32a8..0000000000 --- a/src/applications/ponder/view/PonderVotableView.php +++ /dev/null @@ -1,92 +0,0 @@ -phid = $phid; - return $this; - } - - public function setURI($uri) { - $this->uri = $uri; - return $this; - } - - public function setCount($count) { - $this->count = $count; - return $this; - } - - public function setVote($vote) { - $this->vote = $vote; - return $this; - } - - public function render() { - require_celerity_resource('ponder-view-css'); - require_celerity_resource('javelin-behavior-ponder-votebox'); - - Javelin::initBehavior('ponder-votebox', array()); - - $uri = id(new PhutilURI($this->uri))->alter('phid', $this->phid); - - $up = javelin_tag( - 'a', - array( - 'href' => (string)$uri, - 'sigil' => 'upvote', - 'mustcapture' => true, - 'class' => ($this->vote > 0) ? 'ponder-vote-active' : null, - ), - "\xE2\x96\xB2"); - - $down = javelin_tag( - 'a', - array( - 'href' => (string)$uri, - 'sigil' => 'downvote', - 'mustcapture' => true, - 'class' => ($this->vote < 0) ? 'ponder-vote-active' : null, - ), - "\xE2\x96\xBC"); - - $count = javelin_tag( - 'div', - array( - 'class' => 'ponder-vote-count', - 'sigil' => 'ponder-vote-count', - ), - $this->count); - - return javelin_tag( - 'div', - array( - 'class' => 'ponder-votable', - 'sigil' => 'ponder-votable', - 'meta' => array( - 'count' => (int)$this->count, - 'vote' => (int)$this->vote, - ), - ), - array( - javelin_tag( - 'div', - array( - 'class' => 'ponder-votebox', - ), - array($up, $count, $down)), - phutil_tag( - 'div', - array( - 'class' => 'ponder-votebox-content', - ), - $this->renderChildren()), - )); - } - -} diff --git a/src/applications/project/controller/PhabricatorProjectArchiveController.php b/src/applications/project/controller/PhabricatorProjectArchiveController.php index fdd34a3ea6..d6470ca1eb 100644 --- a/src/applications/project/controller/PhabricatorProjectArchiveController.php +++ b/src/applications/project/controller/PhabricatorProjectArchiveController.php @@ -3,19 +3,13 @@ final class PhabricatorProjectArchiveController extends PhabricatorProjectController { - private $id; - - public function willProcessRequest(array $data) { - $this->id = $data['id']; - } - - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); + $id = $request->getURIData('id'); $project = id(new PhabricatorProjectQuery()) ->setViewer($viewer) - ->withIDs(array($this->id)) + ->withIDs(array($id)) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, diff --git a/src/applications/project/controller/PhabricatorProjectBoardImportController.php b/src/applications/project/controller/PhabricatorProjectBoardImportController.php index b11d6efe31..46877b9e14 100644 --- a/src/applications/project/controller/PhabricatorProjectBoardImportController.php +++ b/src/applications/project/controller/PhabricatorProjectBoardImportController.php @@ -3,15 +3,9 @@ final class PhabricatorProjectBoardImportController extends PhabricatorProjectBoardController { - private $projectID; - - public function willProcessRequest(array $data) { - $this->projectID = $data['projectID']; - } - - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); + $project_id = $request->getURIData('projectID'); $project = id(new PhabricatorProjectQuery()) ->setViewer($viewer) @@ -20,7 +14,7 @@ final class PhabricatorProjectBoardImportController PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )) - ->withIDs(array($this->projectID)) + ->withIDs(array($project_id)) ->executeOne(); if (!$project) { return new Aphront404Response(); diff --git a/src/applications/project/controller/PhabricatorProjectBoardReorderController.php b/src/applications/project/controller/PhabricatorProjectBoardReorderController.php index 3bd1233909..425c27b5f0 100644 --- a/src/applications/project/controller/PhabricatorProjectBoardReorderController.php +++ b/src/applications/project/controller/PhabricatorProjectBoardReorderController.php @@ -3,15 +3,9 @@ final class PhabricatorProjectBoardReorderController extends PhabricatorProjectBoardController { - private $projectID; - - public function willProcessRequest(array $data) { - $this->projectID = $data['projectID']; - } - - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); + $projectid = $request->getURIData('projectID'); $project = id(new PhabricatorProjectQuery()) ->setViewer($viewer) @@ -20,15 +14,13 @@ final class PhabricatorProjectBoardReorderController PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )) - ->withIDs(array($this->projectID)) + ->withIDs(array($projectid)) ->executeOne(); if (!$project) { return new Aphront404Response(); } $this->setProject($project); - - $project_id = $project->getID(); $board_uri = $this->getApplicationURI("board/{$project_id}/"); diff --git a/src/applications/project/controller/PhabricatorProjectColumnDetailController.php b/src/applications/project/controller/PhabricatorProjectColumnDetailController.php index 3a91b55cbd..c44fb5b87c 100644 --- a/src/applications/project/controller/PhabricatorProjectColumnDetailController.php +++ b/src/applications/project/controller/PhabricatorProjectColumnDetailController.php @@ -3,17 +3,10 @@ final class PhabricatorProjectColumnDetailController extends PhabricatorProjectBoardController { - private $id; - private $projectID; - - public function willProcessRequest(array $data) { - $this->projectID = $data['projectID']; - $this->id = idx($data, 'id'); - } - - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); + $id = $request->getURIData('id'); + $project_id = $request->getURIData('projectID'); $project = id(new PhabricatorProjectQuery()) ->setViewer($viewer) @@ -21,7 +14,7 @@ final class PhabricatorProjectColumnDetailController array( PhabricatorPolicyCapability::CAN_VIEW, )) - ->withIDs(array($this->projectID)) + ->withIDs(array($project_id)) ->needImages(true) ->executeOne(); @@ -32,7 +25,7 @@ final class PhabricatorProjectColumnDetailController $column = id(new PhabricatorProjectColumnQuery()) ->setViewer($viewer) - ->withIDs(array($this->id)) + ->withIDs(array($id)) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, diff --git a/src/applications/project/controller/PhabricatorProjectColumnEditController.php b/src/applications/project/controller/PhabricatorProjectColumnEditController.php index 28038f5d98..d59c3648fe 100644 --- a/src/applications/project/controller/PhabricatorProjectColumnEditController.php +++ b/src/applications/project/controller/PhabricatorProjectColumnEditController.php @@ -3,17 +3,10 @@ final class PhabricatorProjectColumnEditController extends PhabricatorProjectBoardController { - private $id; - private $projectID; - - public function willProcessRequest(array $data) { - $this->projectID = $data['projectID']; - $this->id = idx($data, 'id'); - } - - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); + $id = $request->getURIData('id'); + $project_id = $request->getURIData('projectID'); $project = id(new PhabricatorProjectQuery()) ->setViewer($viewer) @@ -22,7 +15,7 @@ final class PhabricatorProjectColumnEditController PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )) - ->withIDs(array($this->projectID)) + ->withIDs(array($project_id)) ->needImages(true) ->executeOne(); @@ -31,12 +24,12 @@ final class PhabricatorProjectColumnEditController } $this->setProject($project); - $is_new = ($this->id ? false : true); + $is_new = ($id ? false : true); if (!$is_new) { $column = id(new PhabricatorProjectColumnQuery()) ->setViewer($viewer) - ->withIDs(array($this->id)) + ->withIDs(array($id)) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, @@ -57,12 +50,12 @@ final class PhabricatorProjectColumnEditController $v_name = $column->getName(); $validation_exception = null; - $base_uri = '/board/'.$this->projectID.'/'; + $base_uri = '/board/'.$project_id.'/'; if ($is_new) { // we want to go back to the board $view_uri = $this->getApplicationURI($base_uri); } else { - $view_uri = $this->getApplicationURI($base_uri.'column/'.$this->id.'/'); + $view_uri = $this->getApplicationURI($base_uri.'column/'.$id.'/'); } if ($request->isFormPost()) { diff --git a/src/applications/project/controller/PhabricatorProjectColumnHideController.php b/src/applications/project/controller/PhabricatorProjectColumnHideController.php index 2e613ee7da..ae39e783bb 100644 --- a/src/applications/project/controller/PhabricatorProjectColumnHideController.php +++ b/src/applications/project/controller/PhabricatorProjectColumnHideController.php @@ -3,17 +3,11 @@ final class PhabricatorProjectColumnHideController extends PhabricatorProjectBoardController { - private $id; - private $projectID; + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); + $id = $request->getURIData('id'); + $project_id = $request->getURIData('projectID'); - public function willProcessRequest(array $data) { - $this->projectID = $data['projectID']; - $this->id = idx($data, 'id'); - } - - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); $project = id(new PhabricatorProjectQuery()) ->setViewer($viewer) ->requireCapabilities( @@ -21,7 +15,7 @@ final class PhabricatorProjectColumnHideController PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )) - ->withIDs(array($this->projectID)) + ->withIDs(array($project_id)) ->executeOne(); if (!$project) { @@ -31,7 +25,7 @@ final class PhabricatorProjectColumnHideController $column = id(new PhabricatorProjectColumnQuery()) ->setViewer($viewer) - ->withIDs(array($this->id)) + ->withIDs(array($id)) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, @@ -44,7 +38,7 @@ final class PhabricatorProjectColumnHideController $column_phid = $column->getPHID(); - $view_uri = $this->getApplicationURI('/board/'.$this->projectID.'/'); + $view_uri = $this->getApplicationURI('/board/'.$project_id.'/'); $view_uri = new PhutilURI($view_uri); foreach ($request->getPassthroughRequestData() as $key => $value) { $view_uri->setQueryParam($key, $value); @@ -66,9 +60,10 @@ final class PhabricatorProjectColumnHideController } $type_status = PhabricatorProjectColumnTransaction::TYPE_STATUS; - $xactions = array(id(new PhabricatorProjectColumnTransaction()) - ->setTransactionType($type_status) - ->setNewValue($new_status), + $xactions = array( + id(new PhabricatorProjectColumnTransaction()) + ->setTransactionType($type_status) + ->setNewValue($new_status), ); $editor = id(new PhabricatorProjectColumnTransactionEditor()) diff --git a/src/applications/project/controller/PhabricatorProjectEditDetailsController.php b/src/applications/project/controller/PhabricatorProjectEditDetailsController.php index af92c60aea..48016845a8 100644 --- a/src/applications/project/controller/PhabricatorProjectEditDetailsController.php +++ b/src/applications/project/controller/PhabricatorProjectEditDetailsController.php @@ -3,23 +3,17 @@ final class PhabricatorProjectEditDetailsController extends PhabricatorProjectController { - private $id; + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); + $id = $request->getURIData('id'); - public function willProcessRequest(array $data) { - $this->id = idx($data, 'id'); - } - - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); - - if ($this->id) { + if ($id) { $id = $request->getURIData('id'); $is_new = false; $project = id(new PhabricatorProjectQuery()) ->setViewer($viewer) - ->withIDs(array($this->id)) + ->withIDs(array($id)) ->needSlugs(true) ->needImages(true) ->requireCapabilities( diff --git a/src/applications/project/controller/PhabricatorProjectEditIconController.php b/src/applications/project/controller/PhabricatorProjectEditIconController.php index c21080d595..fe7e28b4c2 100644 --- a/src/applications/project/controller/PhabricatorProjectEditIconController.php +++ b/src/applications/project/controller/PhabricatorProjectEditIconController.php @@ -3,20 +3,14 @@ final class PhabricatorProjectEditIconController extends PhabricatorProjectController { - private $id; + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); + $id = $request->getURIData('id'); - public function willProcessRequest(array $data) { - $this->id = idx($data, 'id'); - } - - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); - - if ($this->id) { + if ($id) { $project = id(new PhabricatorProjectQuery()) ->setViewer($viewer) - ->withIDs(array($this->id)) + ->withIDs(array($id)) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, diff --git a/src/applications/project/controller/PhabricatorProjectEditPictureController.php b/src/applications/project/controller/PhabricatorProjectEditPictureController.php index ca99159718..6f5128eceb 100644 --- a/src/applications/project/controller/PhabricatorProjectEditPictureController.php +++ b/src/applications/project/controller/PhabricatorProjectEditPictureController.php @@ -3,20 +3,13 @@ final class PhabricatorProjectEditPictureController extends PhabricatorProjectController { - private $id; - - public function willProcessRequest(array $data) { - $this->id = $data['id']; - } - - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); $id = $request->getURIData('id'); $project = id(new PhabricatorProjectQuery()) ->setViewer($viewer) - ->withIDs(array($this->id)) + ->withIDs(array($id)) ->needImages(true) ->requireCapabilities( array( diff --git a/src/applications/project/controller/PhabricatorProjectListController.php b/src/applications/project/controller/PhabricatorProjectListController.php index 1852f43bfc..ea56036c7b 100644 --- a/src/applications/project/controller/PhabricatorProjectListController.php +++ b/src/applications/project/controller/PhabricatorProjectListController.php @@ -3,19 +3,16 @@ final class PhabricatorProjectListController extends PhabricatorProjectController { - private $queryKey; - public function shouldAllowPublic() { return true; } - public function willProcessRequest(array $data) { - $this->queryKey = idx($data, 'queryKey'); - } + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); + $query_key = $request->getURIData('queryKey'); - public function processRequest() { $controller = id(new PhabricatorApplicationSearchController()) - ->setQueryKey($this->queryKey) + ->setQueryKey($query_key) ->setSearchEngine(new PhabricatorProjectSearchEngine()) ->setNavigation($this->buildSideNavView()); diff --git a/src/applications/project/controller/PhabricatorProjectMembersEditController.php b/src/applications/project/controller/PhabricatorProjectMembersEditController.php index d033a87394..c7a9144188 100644 --- a/src/applications/project/controller/PhabricatorProjectMembersEditController.php +++ b/src/applications/project/controller/PhabricatorProjectMembersEditController.php @@ -3,20 +3,13 @@ final class PhabricatorProjectMembersEditController extends PhabricatorProjectController { - private $id; - - public function willProcessRequest(array $data) { - $this->id = $data['id']; - } - - public function processRequest() { - $request = $this->getRequest(); - $user = $request->getUser(); + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); $id = $request->getURIData('id'); $project = id(new PhabricatorProjectQuery()) - ->setViewer($user) - ->withIDs(array($this->id)) + ->setViewer($viewer) + ->withIDs(array($id)) ->needMembers(true) ->needImages(true) ->requireCapabilities( @@ -53,7 +46,7 @@ final class PhabricatorProjectMembersEditController ->setNewValue($member_spec); $editor = id(new PhabricatorProjectTransactionEditor($project)) - ->setActor($user) + ->setActor($viewer) ->setContentSourceFromRequest($request) ->setContinueOnNoEffect(true) ->setContinueOnMissingFields(true) @@ -75,7 +68,7 @@ final class PhabricatorProjectMembersEditController } $can_edit = PhabricatorPolicyFilter::hasCapability( - $user, + $viewer, $project, PhabricatorPolicyCapability::CAN_EDIT); @@ -87,7 +80,7 @@ final class PhabricatorProjectMembersEditController $form = new AphrontFormView(); $form - ->setUser($user) + ->setUser($viewer) ->appendControl( id(new AphrontFormTokenizerControl()) ->setName('phids') diff --git a/src/applications/project/controller/PhabricatorProjectMembersRemoveController.php b/src/applications/project/controller/PhabricatorProjectMembersRemoveController.php index 270dd9a022..cad3f35c05 100644 --- a/src/applications/project/controller/PhabricatorProjectMembersRemoveController.php +++ b/src/applications/project/controller/PhabricatorProjectMembersRemoveController.php @@ -3,19 +3,13 @@ final class PhabricatorProjectMembersRemoveController extends PhabricatorProjectController { - private $id; - - public function willProcessRequest(array $data) { - $this->id = $data['id']; - } - - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); + $id = $request->getURIData('id'); $project = id(new PhabricatorProjectQuery()) ->setViewer($viewer) - ->withIDs(array($this->id)) + ->withIDs(array($id)) ->needMembers(true) ->requireCapabilities( array( diff --git a/src/applications/project/controller/PhabricatorProjectMoveController.php b/src/applications/project/controller/PhabricatorProjectMoveController.php index a22050ea77..1b2429917c 100644 --- a/src/applications/project/controller/PhabricatorProjectMoveController.php +++ b/src/applications/project/controller/PhabricatorProjectMoveController.php @@ -3,15 +3,9 @@ final class PhabricatorProjectMoveController extends PhabricatorProjectController { - private $id; - - public function willProcessRequest(array $data) { - $this->id = $data['id']; - } - - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); + $id = $request->getURIData('id'); $column_phid = $request->getStr('columnPHID'); $object_phid = $request->getStr('objectPHID'); @@ -26,7 +20,7 @@ final class PhabricatorProjectMoveController array( PhabricatorPolicyCapability::CAN_VIEW, )) - ->withIDs(array($this->id)) + ->withIDs(array($id)) ->executeOne(); if (!$project) { return new Aphront404Response(); diff --git a/src/applications/project/controller/PhabricatorProjectUpdateController.php b/src/applications/project/controller/PhabricatorProjectUpdateController.php index a484b2b670..cfdeb4fd09 100644 --- a/src/applications/project/controller/PhabricatorProjectUpdateController.php +++ b/src/applications/project/controller/PhabricatorProjectUpdateController.php @@ -3,24 +3,17 @@ final class PhabricatorProjectUpdateController extends PhabricatorProjectController { - private $id; - private $action; - - public function willProcessRequest(array $data) { - $this->id = $data['id']; - $this->action = $data['action']; - } - - public function processRequest() { - $request = $this->getRequest(); - $user = $request->getUser(); + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); + $id = $request->getURIData('id'); + $action = $request->getURIData('action'); $capabilities = array( PhabricatorPolicyCapability::CAN_VIEW, ); $process_action = false; - switch ($this->action) { + switch ($action) { case 'join': $capabilities[] = PhabricatorPolicyCapability::CAN_JOIN; $process_action = $request->isFormPost(); @@ -33,8 +26,8 @@ final class PhabricatorProjectUpdateController } $project = id(new PhabricatorProjectQuery()) - ->setViewer($user) - ->withIDs(array($this->id)) + ->setViewer($viewer) + ->withIDs(array($id)) ->needMembers(true) ->requireCapabilities($capabilities) ->executeOne(); @@ -47,7 +40,7 @@ final class PhabricatorProjectUpdateController if ($process_action) { $edge_action = null; - switch ($this->action) { + switch ($action) { case 'join': $edge_action = '+'; break; @@ -58,7 +51,7 @@ final class PhabricatorProjectUpdateController $type_member = PhabricatorProjectProjectHasMemberEdgeType::EDGECONST; $member_spec = array( - $edge_action => array($user->getPHID() => $user->getPHID()), + $edge_action => array($viewer->getPHID() => $viewer->getPHID()), ); $xactions = array(); @@ -68,7 +61,7 @@ final class PhabricatorProjectUpdateController ->setNewValue($member_spec); $editor = id(new PhabricatorProjectTransactionEditor($project)) - ->setActor($user) + ->setActor($viewer) ->setContentSourceFromRequest($request) ->setContinueOnNoEffect(true) ->setContinueOnMissingFields(true) @@ -78,10 +71,10 @@ final class PhabricatorProjectUpdateController } $dialog = null; - switch ($this->action) { + switch ($action) { case 'leave': $dialog = new AphrontDialogView(); - $dialog->setUser($user); + $dialog->setUser($viewer); if ($this->userCannotLeave($project)) { $dialog->setTitle(pht('You can not leave this project.')); $body = pht('The membership is locked for this project.'); @@ -107,12 +100,12 @@ final class PhabricatorProjectUpdateController * this logic to render a better form for users hitting this case. */ private function userCannotLeave(PhabricatorProject $project) { - $user = $this->getRequest()->getUser(); + $viewer = $this->getViewer(); return $project->getIsMembershipLocked() && !PhabricatorPolicyFilter::hasCapability( - $user, + $viewer, $project, PhabricatorPolicyCapability::CAN_EDIT); } diff --git a/src/applications/project/controller/PhabricatorProjectViewController.php b/src/applications/project/controller/PhabricatorProjectViewController.php index a974010bc0..45329285df 100644 --- a/src/applications/project/controller/PhabricatorProjectViewController.php +++ b/src/applications/project/controller/PhabricatorProjectViewController.php @@ -9,10 +9,10 @@ final class PhabricatorProjectViewController public function handleRequest(AphrontRequest $request) { $request = $this->getRequest(); - $user = $request->getUser(); + $viewer = $request->getViewer(); $query = id(new PhabricatorProjectQuery()) - ->setViewer($user) + ->setViewer($viewer) ->needMembers(true) ->needWatchers(true) ->needImages(true) @@ -31,7 +31,7 @@ final class PhabricatorProjectViewController $columns = id(new PhabricatorProjectColumnQuery()) - ->setViewer($user) + ->setViewer($viewer) ->withProjectPHIDs(array($project->getPHID())) ->execute(); if ($columns) { diff --git a/src/applications/project/controller/PhabricatorProjectWatchController.php b/src/applications/project/controller/PhabricatorProjectWatchController.php index b1c52e7870..53538f8393 100644 --- a/src/applications/project/controller/PhabricatorProjectWatchController.php +++ b/src/applications/project/controller/PhabricatorProjectWatchController.php @@ -3,21 +3,14 @@ final class PhabricatorProjectWatchController extends PhabricatorProjectController { - private $id; - private $action; - - public function willProcessRequest(array $data) { - $this->id = $data['id']; - $this->action = $data['action']; - } - - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); + $id = $request->getURIData('id'); + $action = $request->getURIData('action'); $project = id(new PhabricatorProjectQuery()) ->setViewer($viewer) - ->withIDs(array($this->id)) + ->withIDs(array($id)) ->needMembers(true) ->needWatchers(true) ->executeOne(); @@ -34,7 +27,7 @@ final class PhabricatorProjectWatchController if ($request->isDialogFormPost()) { $edge_action = null; - switch ($this->action) { + switch ($action) { case 'watch': $edge_action = '+'; $force_subscribe = true; @@ -67,7 +60,7 @@ final class PhabricatorProjectWatchController } $dialog = null; - switch ($this->action) { + switch ($action) { case 'watch': $title = pht('Watch Project?'); $body = pht( diff --git a/src/applications/repository/worker/PhabricatorRepositoryPushMailWorker.php b/src/applications/repository/worker/PhabricatorRepositoryPushMailWorker.php index 49f5fbe293..17226a1377 100644 --- a/src/applications/repository/worker/PhabricatorRepositoryPushMailWorker.php +++ b/src/applications/repository/worker/PhabricatorRepositoryPushMailWorker.php @@ -130,11 +130,6 @@ final class PhabricatorRepositoryPushMailWorker return $target->willSendMail($mail); } - public function renderForDisplay(PhabricatorUser $viewer) { - // This data has some sensitive stuff, so don't show it. - return null; - } - private function renderRefs(array $logs) { $ref_lines = array(); $ref_list = array(); diff --git a/src/applications/search/engine/PhabricatorElasticSearchEngine.php b/src/applications/search/engine/PhabricatorElasticSearchEngine.php index fa09e64a9c..e99160839f 100644 --- a/src/applications/search/engine/PhabricatorElasticSearchEngine.php +++ b/src/applications/search/engine/PhabricatorElasticSearchEngine.php @@ -325,7 +325,7 @@ final class PhabricatorElasticSearchEngine extends PhabricatorSearchEngine { foreach ($types as $type) { // Use the custom trigram analyzer for the corpus of text $data['mappings'][$type]['properties']['field']['properties']['corpus'] = - array( 'type' => 'string', 'analyzer' => 'custom_trigrams' ); + array('type' => 'string', 'analyzer' => 'custom_trigrams'); // Ensure we have dateCreated since the default query requires it $data['mappings'][$type]['properties']['dateCreated']['type'] = 'string'; diff --git a/src/applications/search/index/PhabricatorSearchIndexer.php b/src/applications/search/index/PhabricatorSearchIndexer.php index 137a030e2a..8a37b0f1e2 100644 --- a/src/applications/search/index/PhabricatorSearchIndexer.php +++ b/src/applications/search/index/PhabricatorSearchIndexer.php @@ -15,9 +15,9 @@ final class PhabricatorSearchIndexer extends Phobject { } public function indexDocumentByPHID($phid, $context) { - $indexers = id(new PhutilSymbolLoader()) + $indexers = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorSearchDocumentIndexer') - ->loadObjects(); + ->execute(); foreach ($indexers as $indexer) { if ($indexer->shouldIndexDocumentByPHID($phid)) { diff --git a/src/applications/search/management/PhabricatorSearchManagementIndexWorkflow.php b/src/applications/search/management/PhabricatorSearchManagementIndexWorkflow.php index f7c679782e..853f8c42c1 100644 --- a/src/applications/search/management/PhabricatorSearchManagementIndexWorkflow.php +++ b/src/applications/search/management/PhabricatorSearchManagementIndexWorkflow.php @@ -122,9 +122,9 @@ final class PhabricatorSearchManagementIndexWorkflow } private function loadPHIDsByTypes($type) { - $indexers = id(new PhutilSymbolLoader()) + $indexers = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorSearchDocumentIndexer') - ->loadObjects(); + ->execute(); $phids = array(); foreach ($indexers as $indexer) { diff --git a/src/applications/search/query/PhabricatorSearchApplicationSearchEngine.php b/src/applications/search/query/PhabricatorSearchApplicationSearchEngine.php index a6202b44fc..731edbfb04 100644 --- a/src/applications/search/query/PhabricatorSearchApplicationSearchEngine.php +++ b/src/applications/search/query/PhabricatorSearchApplicationSearchEngine.php @@ -204,9 +204,9 @@ final class PhabricatorSearchApplicationSearchEngine // TODO: This is inelegant and not very efficient, but gets us reasonable // results. It would be nice to do this more elegantly. - $indexers = id(new PhutilSymbolLoader()) + $indexers = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorSearchDocumentIndexer') - ->loadObjects(); + ->execute(); if ($viewer) { $types = PhabricatorPHIDType::getAllInstalledTypes($viewer); diff --git a/src/applications/settings/controller/PhabricatorSettingsMainController.php b/src/applications/settings/controller/PhabricatorSettingsMainController.php index 7a2799935c..728e2131ad 100644 --- a/src/applications/settings/controller/PhabricatorSettingsMainController.php +++ b/src/applications/settings/controller/PhabricatorSettingsMainController.php @@ -75,19 +75,11 @@ final class PhabricatorSettingsMainController } private function buildPanels() { - $panel_specs = id(new PhutilSymbolLoader()) + $panels = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorSettingsPanel') - ->setConcreteOnly(true) - ->selectAndLoadSymbols(); - - $panels = array(); - foreach ($panel_specs as $spec) { - $class = newv($spec['name'], array()); - $panels[] = $class->buildPanels(); - } - - $panels = array_mergev($panels); - $panels = mpull($panels, null, 'getPanelKey'); + ->setExpandMethod('buildPanels') + ->setUniqueMethod('getPanelKey') + ->execute(); $result = array(); foreach ($panels as $key => $panel) { diff --git a/src/applications/settings/panel/PhabricatorEmailPreferencesSettingsPanel.php b/src/applications/settings/panel/PhabricatorEmailPreferencesSettingsPanel.php index 13defb380e..c4142412c6 100644 --- a/src/applications/settings/panel/PhabricatorEmailPreferencesSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorEmailPreferencesSettingsPanel.php @@ -191,16 +191,12 @@ final class PhabricatorEmailPreferencesSettingsPanel } private function getAllEditorsWithTags(PhabricatorUser $user) { - $editors = id(new PhutilSymbolLoader()) + $editors = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorApplicationTransactionEditor') - ->loadObjects(); + ->setFilterMethod('getMailTagsMap') + ->execute(); foreach ($editors as $key => $editor) { - // Remove editors which do not support mail tags. - if (!$editor->getMailTagsMap()) { - unset($editors[$key]); - } - // Remove editors for applications which are not installed. $app = $editor->getEditorApplicationClass(); if ($app !== null) { diff --git a/src/applications/transactions/controller/PhabricatorApplicationTransactionShowOlderController.php b/src/applications/transactions/controller/PhabricatorApplicationTransactionShowOlderController.php index bd8a834d40..e5388184db 100644 --- a/src/applications/transactions/controller/PhabricatorApplicationTransactionShowOlderController.php +++ b/src/applications/transactions/controller/PhabricatorApplicationTransactionShowOlderController.php @@ -23,9 +23,9 @@ final class PhabricatorApplicationTransactionShowOlderController } $template = $object->getApplicationTransactionTemplate(); - $queries = id(new PhutilSymbolLoader()) + $queries = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorApplicationTransactionQuery') - ->loadObjects(); + ->execute(); $object_query = null; foreach ($queries as $query) { diff --git a/src/applications/transactions/phid/PhabricatorApplicationTransactionTransactionPHIDType.php b/src/applications/transactions/phid/PhabricatorApplicationTransactionTransactionPHIDType.php index bd5291efa8..ccb5d271ab 100644 --- a/src/applications/transactions/phid/PhabricatorApplicationTransactionTransactionPHIDType.php +++ b/src/applications/transactions/phid/PhabricatorApplicationTransactionTransactionPHIDType.php @@ -28,9 +28,9 @@ final class PhabricatorApplicationTransactionTransactionPHIDType static $queries; if ($queries === null) { - $objects = id(new PhutilSymbolLoader()) + $objects = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorApplicationTransactionQuery') - ->loadObjects(); + ->execute(); $queries = array(); foreach ($objects as $object) { diff --git a/src/applications/transactions/worker/PhabricatorApplicationTransactionPublishWorker.php b/src/applications/transactions/worker/PhabricatorApplicationTransactionPublishWorker.php index ce1166f23c..486b4da2de 100644 --- a/src/applications/transactions/worker/PhabricatorApplicationTransactionPublishWorker.php +++ b/src/applications/transactions/worker/PhabricatorApplicationTransactionPublishWorker.php @@ -112,9 +112,9 @@ final class PhabricatorApplicationTransactionPublishWorker * the transactions. */ private function buildTransactionQuery($type) { - $queries = id(new PhutilSymbolLoader()) + $queries = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorApplicationTransactionQuery') - ->loadObjects(); + ->execute(); foreach ($queries as $query) { $query_type = $query diff --git a/src/applications/typeahead/controller/PhabricatorTypeaheadFunctionHelpController.php b/src/applications/typeahead/controller/PhabricatorTypeaheadFunctionHelpController.php index e2285a061b..0770bc6d3d 100644 --- a/src/applications/typeahead/controller/PhabricatorTypeaheadFunctionHelpController.php +++ b/src/applications/typeahead/controller/PhabricatorTypeaheadFunctionHelpController.php @@ -11,9 +11,10 @@ final class PhabricatorTypeaheadFunctionHelpController $viewer = $this->getViewer(); $class = $request->getURIData('class'); - $sources = id(new PhutilSymbolLoader()) + $sources = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorTypeaheadDatasource') - ->loadObjects(); + ->execute(); + if (!isset($sources[$class])) { return new Aphront404Response(); } diff --git a/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php b/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php index f0f274b894..9ac5b4bdb2 100644 --- a/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php +++ b/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php @@ -29,9 +29,10 @@ final class PhabricatorTypeaheadModularDatasourceController // This makes form submission easier in the debug view. $class = nonempty($request->getURIData('class'), $request->getStr('class')); - $sources = id(new PhutilSymbolLoader()) + $sources = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorTypeaheadDatasource') - ->loadObjects(); + ->execute(); + if (isset($sources[$class])) { $source = $sources[$class]; $source->setParameters($request->getRequestData()); diff --git a/src/applications/uiexample/controller/PhabricatorUIExampleRenderController.php b/src/applications/uiexample/controller/PhabricatorUIExampleRenderController.php index 32c60de1c5..8da553104f 100644 --- a/src/applications/uiexample/controller/PhabricatorUIExampleRenderController.php +++ b/src/applications/uiexample/controller/PhabricatorUIExampleRenderController.php @@ -9,10 +9,10 @@ final class PhabricatorUIExampleRenderController extends PhabricatorController { public function handleRequest(AphrontRequest $request) { $id = $request->getURIData('class'); - $classes = id(new PhutilSymbolLoader()) + $classes = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorUIExample') - ->loadObjects(); - $classes = msort($classes, 'getName'); + ->setSortMethod('getName') + ->execute(); $nav = new AphrontSideNavFilterView(); $nav->setBaseURI(new PhutilURI($this->getApplicationURI('view/'))); diff --git a/src/applications/uiexample/examples/PHUIButtonExample.php b/src/applications/uiexample/examples/PHUIButtonExample.php index 216a4a4406..06cf369ba2 100644 --- a/src/applications/uiexample/examples/PHUIButtonExample.php +++ b/src/applications/uiexample/examples/PHUIButtonExample.php @@ -68,7 +68,8 @@ final class PHUIButtonExample extends PhabricatorUIExample { // PHUIButtonView - $colors = array(null, + $colors = array( + null, PHUIButtonView::GREEN, PHUIButtonView::GREY, PHUIButtonView::DISABLED, diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php index a75239bde5..76a1de9989 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php @@ -22,10 +22,10 @@ abstract class PhabricatorStandardCustomField PhabricatorCustomField $template, array $config) { - $types = id(new PhutilSymbolLoader()) + $types = id(new PhutilClassMapQuery()) ->setAncestorClass(__CLASS__) - ->loadObjects(); - $types = mpull($types, null, 'getFieldType'); + ->setUniqueMethod('getFieldType') + ->execute(); $fields = array(); foreach ($config as $key => $value) { diff --git a/src/infrastructure/daemon/workers/PhabricatorTriggerDaemon.php b/src/infrastructure/daemon/workers/PhabricatorTriggerDaemon.php index a8e169b759..9c40aba315 100644 --- a/src/infrastructure/daemon/workers/PhabricatorTriggerDaemon.php +++ b/src/infrastructure/daemon/workers/PhabricatorTriggerDaemon.php @@ -385,9 +385,9 @@ final class PhabricatorTriggerDaemon * @task garbage */ private function loadGarbageCollectors() { - return id(new PhutilSymbolLoader()) + return id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorGarbageCollector') - ->loadObjects(); + ->execute(); } diff --git a/src/infrastructure/daemon/workers/PhabricatorWorker.php b/src/infrastructure/daemon/workers/PhabricatorWorker.php index 29f3e0ae39..629e688b90 100644 --- a/src/infrastructure/daemon/workers/PhabricatorWorker.php +++ b/src/infrastructure/daemon/workers/PhabricatorWorker.php @@ -210,8 +210,7 @@ abstract class PhabricatorWorker extends Phobject { } public function renderForDisplay(PhabricatorUser $viewer) { - $data = PhutilReadableSerializer::printableValue($this->data); - return phutil_tag('pre', array(), $data); + return null; } /** diff --git a/src/infrastructure/env/PhabricatorEnv.php b/src/infrastructure/env/PhabricatorEnv.php index d5c4c34461..b576e2f83b 100644 --- a/src/infrastructure/env/PhabricatorEnv.php +++ b/src/infrastructure/env/PhabricatorEnv.php @@ -200,10 +200,11 @@ final class PhabricatorEnv extends Phobject { $default_source->loadExternalOptions(); // If this install has site config sources, load them now. - $site_sources = id(new PhutilSymbolLoader()) + $site_sources = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorConfigSiteSource') - ->loadObjects(); - $site_sources = msort($site_sources, 'getPriority'); + ->setSortMethod('getPriority') + ->execute(); + foreach ($site_sources as $site_source) { $stack->pushSource($site_source); } diff --git a/src/infrastructure/events/PhabricatorEventEngine.php b/src/infrastructure/events/PhabricatorEventEngine.php index 9206be89bf..dd2cc1e4a2 100644 --- a/src/infrastructure/events/PhabricatorEventEngine.php +++ b/src/infrastructure/events/PhabricatorEventEngine.php @@ -9,9 +9,9 @@ final class PhabricatorEventEngine extends Phobject { // be able to run `bin/config` in order to remove an invalid listener. // Load automatic listeners. - $listeners = id(new PhutilSymbolLoader()) + $listeners = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorAutoEventListener') - ->loadObjects(); + ->execute(); // Load configured listeners. $config_listeners = PhabricatorEnv::getEnvConfig('events.listeners'); diff --git a/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php b/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php index 98d062766b..76f2bab8ba 100644 --- a/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php +++ b/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php @@ -613,6 +613,14 @@ final class PhabricatorUSEnglishTranslation ), ), + '%s edited dependencie(s), added %s: %s; removed %s: %s.' => array( + '%s edited dependencies, added: %3$s; removed: %5$s.', + ), + + '%s edited dependencie(s) for %s, added %s: %s; removed %s: %s.' => array( + '%s edited dependencies for %s, added: %3$s; removed: %5$s.', + ), + '%s added %s dependent revision(s): %s.' => array( array( '%s added a dependent revision: %3$s.', diff --git a/src/infrastructure/markup/PhabricatorMarkupEngine.php b/src/infrastructure/markup/PhabricatorMarkupEngine.php index f00c8a1ef5..933a21b163 100644 --- a/src/infrastructure/markup/PhabricatorMarkupEngine.php +++ b/src/infrastructure/markup/PhabricatorMarkupEngine.php @@ -622,15 +622,15 @@ final class PhabricatorMarkupEngine extends Phobject { } private static function loadCustomInlineRules() { - return id(new PhutilSymbolLoader()) + return id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorRemarkupCustomInlineRule') - ->loadObjects(); + ->execute(); } private static function loadCustomBlockRules() { - return id(new PhutilSymbolLoader()) + return id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorRemarkupCustomBlockRule') - ->loadObjects(); + ->execute(); } } diff --git a/src/infrastructure/sms/worker/PhabricatorSMSWorker.php b/src/infrastructure/sms/worker/PhabricatorSMSWorker.php index a999fe5e82..7b8f851eaa 100644 --- a/src/infrastructure/sms/worker/PhabricatorSMSWorker.php +++ b/src/infrastructure/sms/worker/PhabricatorSMSWorker.php @@ -1,11 +1,4 @@ assertEqual( @@ -38,7 +47,9 @@ final class LiskChunkTestCase extends PhabricatorTestCase { $fragments = array( 'xxxxxxxxxx', 'yyyyyyyyyy', - 'a', 'b', 'c', + 'a', + 'b', + 'c', 'zzzzzzzzzz', ); diff --git a/src/infrastructure/storage/patch/PhabricatorSQLPatchList.php b/src/infrastructure/storage/patch/PhabricatorSQLPatchList.php index e0be6add1a..5373762889 100644 --- a/src/infrastructure/storage/patch/PhabricatorSQLPatchList.php +++ b/src/infrastructure/storage/patch/PhabricatorSQLPatchList.php @@ -37,31 +37,15 @@ abstract class PhabricatorSQLPatchList extends Phobject { } final public static function buildAllPatches() { - $patch_lists = id(new PhutilSymbolLoader()) + $patch_lists = id(new PhutilClassMapQuery()) ->setAncestorClass(__CLASS__) - ->setConcreteOnly(true) - ->selectAndLoadSymbols(); + ->setUniqueMethod('getNamespace') + ->execute(); $specs = array(); $seen_namespaces = array(); - foreach ($patch_lists as $patch_class) { - $patch_class = $patch_class['name']; - $patch_list = newv($patch_class, array()); - - $namespace = $patch_list->getNamespace(); - if (isset($seen_namespaces[$namespace])) { - $prior = $seen_namespaces[$namespace]; - throw new Exception( - pht( - "%s '%s' has the same namespace, '%s', as another patch list ". - "class, '%s'. Each patch list MUST have a unique namespace.", - __CLASS__, - $patch_class, - $namespace, - $prior)); - } - + foreach ($patch_lists as $patch_list) { $last_key = null; foreach ($patch_list->getPatches() as $key => $patch) { if (!is_array($patch)) { @@ -69,7 +53,7 @@ abstract class PhabricatorSQLPatchList extends Phobject { pht( "%s '%s' has a patch '%s' which is not an array.", __CLASS__, - $patch_class, + get_class($patch_list), $key)); } @@ -88,7 +72,7 @@ abstract class PhabricatorSQLPatchList extends Phobject { "%s '%s' has a patch, '%s', with an unknown property, '%s'.". "Patches must have only valid keys: %s.", __CLASS__, - $patch_class, + get_class($patch_list), $key, $pkey, implode(', ', array_keys($valid)))); @@ -101,7 +85,7 @@ abstract class PhabricatorSQLPatchList extends Phobject { "%s '%s' has a patch with a numeric key, '%s'. ". "Patches must use string keys.", __CLASS__, - $patch_class, + get_class($patch_list), $key)); } @@ -111,10 +95,11 @@ abstract class PhabricatorSQLPatchList extends Phobject { "%s '%s' has a patch with a colon in the key name, '%s'. ". "Patch keys may not contain colons.", __CLASS__, - $patch_class, + get_class($patch_list), $key)); } + $namespace = $patch_list->getNamespace(); $full_key = "{$namespace}:{$key}"; if (isset($specs[$full_key])) { @@ -123,7 +108,7 @@ abstract class PhabricatorSQLPatchList extends Phobject { "%s '%s' has a patch '%s' which duplicates an ". "existing patch key.", __CLASS__, - $patch_class, + get_class($patch_list), $key)); } @@ -152,7 +137,7 @@ abstract class PhabricatorSQLPatchList extends Phobject { "determined implicitly. The first patch in a patch list must ". "list the patch or patches it depends on explicitly.", $full_key, - $patch_class)); + get_class($patch_list))); } else { $patch['after'] = array($last_key); } diff --git a/src/infrastructure/util/__tests__/PhabricatorHashTestCase.php b/src/infrastructure/util/__tests__/PhabricatorHashTestCase.php index c6839dae09..b89515701d 100644 --- a/src/infrastructure/util/__tests__/PhabricatorHashTestCase.php +++ b/src/infrastructure/util/__tests__/PhabricatorHashTestCase.php @@ -19,9 +19,23 @@ final class PhabricatorHashTestCase extends PhabricatorTestCase { // Test that the encoding produces 6 bits of entropy per byte. $entropy = array( - 'dog', 'cat', 'rat', 'bat', 'dig', 'fig', 'cot', - 'cut', 'fog', 'rig', 'rug', 'dug', 'mat', 'pat', - 'eat', 'tar', 'pot', + 'dog', + 'cat', + 'rat', + 'bat', + 'dig', + 'fig', + 'cot', + 'cut', + 'fog', + 'rig', + 'rug', + 'dug', + 'mat', + 'pat', + 'eat', + 'tar', + 'pot', ); $seen = array(); diff --git a/src/view/AphrontDialogView.php b/src/view/AphrontDialogView.php index 19210c312d..ca866b11a8 100644 --- a/src/view/AphrontDialogView.php +++ b/src/view/AphrontDialogView.php @@ -290,10 +290,11 @@ final class AphrontDialogView extends AphrontView { } if (!$this->renderAsForm) { - $buttons = array(phabricator_form( - $this->user, - $form_attributes, - array_merge($hidden_inputs, $buttons)), + $buttons = array( + phabricator_form( + $this->user, + $form_attributes, + array_merge($hidden_inputs, $buttons)), ); } diff --git a/src/view/__tests__/PhabricatorUnitsTestCase.php b/src/view/__tests__/PhabricatorUnitsTestCase.php index bd0f535008..aae9598ddf 100644 --- a/src/view/__tests__/PhabricatorUnitsTestCase.php +++ b/src/view/__tests__/PhabricatorUnitsTestCase.php @@ -55,7 +55,7 @@ final class PhabricatorUnitsTestCase extends PhabricatorTestCase { public function testDetailedDurationFormatting() { $expected_zero = 'now'; - $tests = array ( + $tests = array( 12095939 => '19 w, 6 d', -12095939 => '19 w, 6 d ago', diff --git a/src/view/phui/PHUITwoColumnView.php b/src/view/phui/PHUITwoColumnView.php new file mode 100644 index 0000000000..a7af97da52 --- /dev/null +++ b/src/view/phui/PHUITwoColumnView.php @@ -0,0 +1,48 @@ +mainColumn = $main; + return $this; + } + + public function setSideColumn($side) { + $this->sideColumn = $side; + return $this; + } + + protected function getTagAttributes() { + return array( + 'class' => 'phui-two-column-view grouped', + ); + } + + protected function getTagContent() { + require_celerity_resource('phui-two-column-view-css'); + + $main = phutil_tag( + 'div', + array( + 'class' => 'phui-main-column', + ), + $this->mainColumn); + + $side = phutil_tag( + 'div', + array( + 'class' => 'phui-side-column', + ), + $this->sideColumn); + + return phutil_tag_div( + 'phui-two-column-row', + array( + $main, + $side, + )); + } +} diff --git a/support/aphlict/server/aphlict_launcher.php b/support/aphlict/server/aphlict_launcher.php index 8a550265bf..f6173fbd6d 100755 --- a/support/aphlict/server/aphlict_launcher.php +++ b/support/aphlict/server/aphlict_launcher.php @@ -16,8 +16,8 @@ EOSYNOPSIS ); $args->parseStandardArguments(); -$workflows = id(new PhutilSymbolLoader()) +$workflows = id(new PhutilClassMapQuery()) ->setAncestorClass('PhabricatorAphlictManagementWorkflow') - ->loadObjects(); + ->execute(); $workflows[] = new PhutilHelpArgumentWorkflow(); $args->parseWorkflows($workflows); diff --git a/webroot/rsrc/css/application/ponder/ponder-view.css b/webroot/rsrc/css/application/ponder/ponder-view.css index 237c3e7676..867009c4bb 100644 --- a/webroot/rsrc/css/application/ponder/ponder-view.css +++ b/webroot/rsrc/css/application/ponder/ponder-view.css @@ -2,45 +2,6 @@ * @provides ponder-view-css */ -.ponder-votable { - float: right; - margin: 4px 0 4px 24px; -} - -.ponder-votebox { - border-radius: 4px; - background: #f3f3f3; - border: 1px solid {$blueborder}; - text-align: center; - width: 24px; -} - -.ponder-votebox a { - font-size: 20px; - line-height: 24px; - display: block; - - text-decoration: none; - color: #aaaaaa; - font-weight: normal; -} - -.ponder-votebox a.ponder-vote-active { - color: {$blue}; -} - -.ponder-votebox a:hover { - color: #ffffff; - background: {$blue}; -} - -.ponder-vote-count { - color: {$darkbluetext}; - font-size: {$biggerfontsize}; - line-height: 20px; - font-weight: bold; -} - .ponder-show-comments { text-align: center; padding: 8px; @@ -53,7 +14,43 @@ border-right: 1px solid {$lightblueborder}; } -.device-desktop .ponder-comments-view { - width: 90%; - margin: 0 auto; +.ponder-question-view .phui-property-list-properties-wrap { + width: 66%; +} + +.ponder-question-view .phui-property-list-actions { + width: 30%; +} + +.ponder-answer-view { + margin-top: 16px; +} + +.ponder-answer-view .phui-header-subheader { + display: inline; + margin-left: 12px; +} + +.ponder-footer-view { + margin: 0 4px -4px; +} + +.ponder-footer-view .ponder-footer-action { + padding: 4px 8px; + margin-right: 8px; + color: {$bluetext}; + display: inline-block; + background-color: rgba(71, 87, 120, 0.06); + font-size: {$smallerfontsize}; +} + +.ponder-footer-view .ponder-footer-action .phui-icon-view { + color: {$bluetext}; + margin-left: 4px; +} + +.ponder-footer-view a:hover { + text-decoration: none; + color: {$darkbluetext}; + background-color: rgba(71, 87, 120, 0.10); } diff --git a/webroot/rsrc/css/phui/phui-two-column-view.css b/webroot/rsrc/css/phui/phui-two-column-view.css new file mode 100644 index 0000000000..4701955b49 --- /dev/null +++ b/webroot/rsrc/css/phui/phui-two-column-view.css @@ -0,0 +1,33 @@ +/** + * @provides phui-two-column-view-css + */ + +.phui-two-column-view { + display: table; + width: 100%; +} + +.phui-two-column-row { + display: table-row; +} + +.device-desktop .phui-two-column-view .phui-main-column { + display: table-cell; + vertical-align: top; +} + +.device-desktop .phui-two-column-view .phui-side-column { + width: 320px; + display: table-cell; + vertical-align: top; +} + +.device-desktop .phui-two-column-view + .phui-main-column .phui-object-box:first-child { + margin: 0 16px 0 16px; +} + +.device-desktop .phui-two-column-view + .phui-side-column .phui-object-box:first-child { + margin: 0 16px 16px 0; +} diff --git a/webroot/rsrc/js/application/ponder/behavior-votebox.js b/webroot/rsrc/js/application/ponder/behavior-votebox.js deleted file mode 100644 index 47e750bcb7..0000000000 --- a/webroot/rsrc/js/application/ponder/behavior-votebox.js +++ /dev/null @@ -1,57 +0,0 @@ -/** - * @provides javelin-behavior-ponder-votebox - * @requires javelin-behavior - * javelin-dom - * javelin-util - * javelin-stratcom - * javelin-request - */ - -JX.behavior('ponder-votebox', function() { - - function handle_vote(e, vote) { - e.kill(); - - var root = e.getNode('ponder-votable'); - var data = e.getNodeData('ponder-votable'); - - if (data.vote != vote) { - // Remove the user's current vote, if they have one. - data.count -= data.vote; - data.vote = vote; - data.count += vote; - } else { - // User is undoing their vote. - data.vote = 0; - data.count -= vote; - } - - var upv = JX.DOM.find(root, 'a', 'upvote'); - JX.DOM.alterClass(upv, 'ponder-vote-active', (data.vote > 0)); - - var downv = JX.DOM.find(root, 'a', 'downvote'); - JX.DOM.alterClass(downv, 'ponder-vote-active', (data.vote < 0)); - - JX.DOM.setContent( - JX.DOM.find(root, 'div', 'ponder-vote-count'), - data.count); - - new JX.Request(e.getTarget().href, JX.bag) - .setData({vote: data.vote}) - .send(); - } - - JX.Stratcom.listen( - 'click', - 'downvote', - function(e) { - handle_vote(e, -1); - }); - - JX.Stratcom.listen( - 'click', - 'upvote', - function(e) { - handle_vote(e, 1); - }); -});