From 3d1783b2dad4de02e17bfdcfe2ded7f86d772355 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sat, 8 Aug 2015 09:46:21 -0700 Subject: [PATCH 01/39] Add application search for Paste status Summary: Fixes T9076. This adds the ability to search for active, archived, or all Pastes. Test Plan: Search for active, archived, all Pastes. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Maniphest Tasks: T9076 Differential Revision: https://secure.phabricator.com/D13809 --- .../paste/query/PhabricatorPasteQuery.php | 14 +++++++++++++ .../query/PhabricatorPasteSearchEngine.php | 21 +++++++++++++++++++ 2 files changed, 35 insertions(+) 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); From dc687dbd92a54425974ad9476112b32c2c142360 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sat, 8 Aug 2015 10:22:18 -0700 Subject: [PATCH 02/39] Add basic Herald support to Ponder Summary: Ref T6919, Just a basic herald adapter (new questions) for Ponder Test Plan: Created a Personal Rule, got subscribed to new question, saw transcript. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Maniphest Tasks: T6919 Differential Revision: https://secure.phabricator.com/D13828 --- src/__phutil_library_map__.php | 2 + .../HeraldTestConsoleController.php | 3 + .../ponder/editor/PonderQuestionEditor.php | 14 +++++ .../herald/HeraldPonderQuestionAdapter.php | 62 +++++++++++++++++++ 4 files changed, 81 insertions(+) create mode 100644 src/applications/ponder/herald/HeraldPonderQuestionAdapter.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 0f00bd7430..43fc9b3b94 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1063,6 +1063,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', @@ -4789,6 +4790,7 @@ phutil_register_library_map(array( 'HeraldNotifyActionGroup' => 'HeraldActionGroup', 'HeraldObjectTranscript' => 'Phobject', 'HeraldPholioMockAdapter' => 'HeraldAdapter', + 'HeraldPonderQuestionAdapter' => 'HeraldAdapter', 'HeraldPreCommitAdapter' => 'HeraldAdapter', 'HeraldPreCommitContentAdapter' => 'HeraldPreCommitAdapter', 'HeraldPreCommitRefAdapter' => 'HeraldPreCommitAdapter', 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/ponder/editor/PonderQuestionEditor.php b/src/applications/ponder/editor/PonderQuestionEditor.php index af984f4b4d..d9f77fad94 100644 --- a/src/applications/ponder/editor/PonderQuestionEditor.php +++ b/src/applications/ponder/editor/PonderQuestionEditor.php @@ -252,4 +252,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(); + } + +} From d2ef273ecd0a128bdec038c39bb538cb028e39ab Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sat, 8 Aug 2015 10:23:33 -0700 Subject: [PATCH 03/39] Add additional statuses to Ponder Summary: Ref T9096. This is a first cut at adding additional statuses, happy to add or subtract as needed... maybe even configurable? Also, the dialog doesn't seem to fire, I'll keep debugging. Test Plan: Close and Reopen many questions. Test applicationSearch params by seeing resolved questions, all questions. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Maniphest Tasks: T9096 Differential Revision: https://secure.phabricator.com/D13826 --- .../autopatches/20150806.ponder.status.1.sql | 2 + .../autopatches/20150806.ponder.status.2.sql | 2 + .../autopatches/20150806.ponder.status.3.sql | 2 + .../PhabricatorPonderApplication.php | 2 +- .../ponder/constants/PonderQuestionStatus.php | 55 ++++++++++++++++--- .../PonderQuestionEditController.php | 29 +++++++--- .../PonderQuestionStatusController.php | 54 +++++++++++------- .../PonderQuestionViewController.php | 13 +++-- .../ponder/query/PonderQuestionQuery.php | 31 ++--------- .../query/PonderQuestionSearchEngine.php | 23 ++++---- .../ponder/storage/PonderQuestion.php | 2 +- .../storage/PonderQuestionTransaction.php | 26 ++++----- 12 files changed, 147 insertions(+), 94 deletions(-) create mode 100644 resources/sql/autopatches/20150806.ponder.status.1.sql create mode 100644 resources/sql/autopatches/20150806.ponder.status.2.sql create mode 100644 resources/sql/autopatches/20150806.ponder.status.3.sql 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..89371cc505 --- /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..3a8f5c719d --- /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/src/applications/ponder/application/PhabricatorPonderApplication.php b/src/applications/ponder/application/PhabricatorPonderApplication.php index 11e90dc274..6ba5e3ca4f 100644 --- a/src/applications/ponder/application/PhabricatorPonderApplication.php +++ b/src/applications/ponder/application/PhabricatorPonderApplication.php @@ -71,7 +71,7 @@ final class PhabricatorPonderApplication extends PhabricatorApplication { => 'PonderQuestionHistoryController', 'preview/' => 'PhabricatorMarkupPreviewController', - 'question/(?Popen|close)/(?P[1-9]\d*)/' + 'question/status/(?P[1-9]\d*)/' => 'PonderQuestionStatusController', 'vote/' => 'PonderVoteSaveController', ), diff --git a/src/applications/ponder/constants/PonderQuestionStatus.php b/src/applications/ponder/constants/PonderQuestionStatus.php index 2d26b9cb66..6c612ec4ae 100644 --- a/src/applications/ponder/constants/PonderQuestionStatus.php +++ b/src/applications/ponder/constants/PonderQuestionStatus.php @@ -2,20 +2,40 @@ 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 getQuestionStatusDescription($status) { + $map = array( + self::STATUS_OPEN => + pht('This question is open for answers.'), + self::STATUS_CLOSED_RESOLVED => + pht('This question has been 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 +43,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 +54,27 @@ 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/controller/PonderQuestionEditController.php b/src/applications/ponder/controller/PonderQuestionEditController.php index cf8b5f9de1..d1a8d48f59 100644 --- a/src/applications/ponder/controller/PonderQuestionEditController.php +++ b/src/applications/ponder/controller/PonderQuestionEditController.php @@ -33,6 +33,8 @@ final class PonderQuestionEditController extends PonderController { $v_view = $question->getViewPolicy(); $v_edit = $question->getEditPolicy(); $v_space = $question->getSpacePHID(); + $v_status = $question->getStatus(); + $errors = array(); $e_title = true; @@ -43,6 +45,7 @@ final class PonderQuestionEditController extends PonderController { $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,6 +68,10 @@ final class PonderQuestionEditController extends PonderController { ->setTransactionType(PonderQuestionTransaction::TYPE_CONTENT) ->setNewValue($v_content); + $xactions[] = id(clone $template) + ->setTransactionType(PonderQuestionTransaction::TYPE_STATUS) + ->setNewValue($v_status); + $xactions[] = id(clone $template) ->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY) ->setNewValue($v_view); @@ -130,7 +137,13 @@ final class PonderQuestionEditController extends PonderController { ->setPolicyObject($question) ->setPolicies($policies) ->setValue($v_edit) - ->setCapability(PhabricatorPolicyCapability::CAN_EDIT)); + ->setCapability(PhabricatorPolicyCapability::CAN_EDIT)) + ->appendChild( + id(new AphrontFormSelectControl()) + ->setLabel(pht('Status')) + ->setName('status') + ->setValue($v_status) + ->setOptions(PonderQuestionStatus::getQuestionStatusMap())); $form->appendControl( id(new AphrontFormTokenizerControl()) @@ -149,21 +162,23 @@ final class PonderQuestionEditController extends PonderController { ->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 +186,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..c5349c5eab 100644 --- a/src/applications/ponder/controller/PonderQuestionViewController.php +++ b/src/applications/ponder/controller/PonderQuestionViewController.php @@ -45,7 +45,11 @@ 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); @@ -109,21 +113,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()) diff --git a/src/applications/ponder/query/PonderQuestionQuery.php b/src/applications/ponder/query/PonderQuestionQuery.php index 74d8230327..1ed3344382 100644 --- a/src/applications/ponder/query/PonderQuestionQuery.php +++ b/src/applications/ponder/query/PonderQuestionQuery.php @@ -5,17 +5,12 @@ 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; @@ -34,7 +29,7 @@ final class PonderQuestionQuery return $this; } - public function withStatus($status) { + public function withStatuses($status) { $this->status = $status; return $this; } @@ -84,24 +79,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; diff --git a/src/applications/ponder/query/PonderQuestionSearchEngine.php b/src/applications/ponder/query/PonderQuestionSearchEngine.php index 1b475c7b58..b444ff8be9 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()), ); } @@ -66,6 +58,7 @@ final class PonderQuestionSearchEngine protected function getBuiltinQueryNames() { $names = array( 'open' => pht('Open Questions'), + 'resolved' => pht('Resolved Questions'), 'all' => pht('All Questions'), ); @@ -85,7 +78,11 @@ 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 'resolved': + return $query->setParameter( + 'statuses', array(PonderQuestionStatus::STATUS_CLOSED_RESOLVED)); case 'authored': return $query->setParameter( 'authorPHIDs', diff --git a/src/applications/ponder/storage/PonderQuestion.php b/src/applications/ponder/storage/PonderQuestion.php index f2d81c4838..0e1ea0226b 100644 --- a/src/applications/ponder/storage/PonderQuestion.php +++ b/src/applications/ponder/storage/PonderQuestion.php @@ -65,7 +65,7 @@ final class PonderQuestion extends PonderDAO self::CONFIG_COLUMN_SCHEMA => array( 'title' => 'text255', 'voteCount' => 'sint32', - 'status' => 'uint32', + 'status' => 'text32', 'content' => 'text', 'heat' => 'double', 'answerCount' => 'uint32', diff --git a/src/applications/ponder/storage/PonderQuestionTransaction.php b/src/applications/ponder/storage/PonderQuestionTransaction.php index 0e4d07b150..38f2b5e45b 100644 --- a/src/applications/ponder/storage/PonderQuestionTransaction.php +++ b/src/applications/ponder/storage/PonderQuestionTransaction.php @@ -87,9 +87,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)); } } @@ -106,12 +114,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 +133,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); } } From 44f18cfb12d08e0e8260067eec9b28c7932099fc Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sat, 8 Aug 2015 10:34:55 -0700 Subject: [PATCH 04/39] Update Project for handleRequest Summary: Updates Projects to use handleRequest Test Plan: New Project, Edit Project, Archive, Watch, Create Workboards, Import Workboards, Edit Column, New Column, Change Icon, Add/Remove Members Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D13829 --- .../PhabricatorProjectArchiveController.php | 14 +++----- ...habricatorProjectBoardImportController.php | 14 +++----- ...abricatorProjectBoardReorderController.php | 16 +++------ ...abricatorProjectColumnDetailController.php | 19 ++++------ ...PhabricatorProjectColumnEditController.php | 25 +++++-------- ...PhabricatorProjectColumnHideController.php | 20 ++++------- ...habricatorProjectEditDetailsController.php | 16 +++------ .../PhabricatorProjectEditIconController.php | 16 +++------ ...habricatorProjectEditPictureController.php | 13 ++----- .../PhabricatorProjectListController.php | 11 +++--- ...habricatorProjectMembersEditController.php | 21 ++++------- ...bricatorProjectMembersRemoveController.php | 14 +++----- .../PhabricatorProjectMoveController.php | 14 +++----- .../PhabricatorProjectUpdateController.php | 35 ++++++++----------- .../PhabricatorProjectViewController.php | 6 ++-- .../PhabricatorProjectWatchController.php | 21 ++++------- 16 files changed, 90 insertions(+), 185 deletions(-) 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..167cbfbd2e 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); 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( From 736bda7081c947595ba9519ff1af5fd70b9170a0 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sat, 8 Aug 2015 11:07:42 -0700 Subject: [PATCH 05/39] Fix SQL issue with Ponder migration Summary: This fails to apply on my second sandbox with incorrect DOUBLE value. Reran SQL, works as expected. Test Plan: Rerun new SQL on ponder_question table Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D13830 --- resources/sql/autopatches/20150806.ponder.status.2.sql | 2 +- resources/sql/autopatches/20150806.ponder.status.3.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/sql/autopatches/20150806.ponder.status.2.sql b/resources/sql/autopatches/20150806.ponder.status.2.sql index 89371cc505..0827c5b106 100644 --- a/resources/sql/autopatches/20150806.ponder.status.2.sql +++ b/resources/sql/autopatches/20150806.ponder.status.2.sql @@ -1,2 +1,2 @@ UPDATE {$NAMESPACE}_ponder.ponder_question - SET status = 'open' WHERE status = 0; + 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 index 3a8f5c719d..fdd71a5b20 100644 --- a/resources/sql/autopatches/20150806.ponder.status.3.sql +++ b/resources/sql/autopatches/20150806.ponder.status.3.sql @@ -1,2 +1,2 @@ UPDATE {$NAMESPACE}_ponder.ponder_question - SET status = 'resolved' WHERE status = 1; + SET status = 'resolved' WHERE status = '1'; From 22de31c56d6f6fd501eaa76fdd0cd8793bb497ab Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sat, 8 Aug 2015 11:17:10 -0700 Subject: [PATCH 06/39] Fix Ponder feed story transaction Summary: These transactions were missed, add in all closed states. Test Plan: Closed a Question, see title in Feed. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D13831 --- .../ponder/storage/PonderQuestionTransaction.php | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/applications/ponder/storage/PonderQuestionTransaction.php b/src/applications/ponder/storage/PonderQuestionTransaction.php index 38f2b5e45b..80cb9f83f0 100644 --- a/src/applications/ponder/storage/PonderQuestionTransaction.php +++ b/src/applications/ponder/storage/PonderQuestionTransaction.php @@ -237,12 +237,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)); } From f98f5a081e41c7b84ad5e43432a78dffcbb9f6a8 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sat, 8 Aug 2015 12:20:01 -0700 Subject: [PATCH 07/39] Add a default moderation policy to Ponder Summary: This allows installs to essentially set a "moderator" for Ponder, who can clean up answers. Fixes T9098 Test Plan: Edit an answer I don't own. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Maniphest Tasks: T9098 Differential Revision: https://secure.phabricator.com/D13818 --- .../autopatches/20150806.ponder.answer.1.sql | 2 + .../20150806.ponder.editpolicy.2.sql | 2 + src/__phutil_library_map__.php | 8 ++-- .../PhabricatorPonderApplication.php | 5 +- ...ty.php => PonderDefaultViewCapability.php} | 6 +-- .../capability/PonderModerateCapability.php | 12 +++++ .../PonderQuestionDefaultEditCapability.php | 12 ----- .../controller/PonderAnswerSaveController.php | 40 ++++++++++------ .../PonderQuestionEditController.php | 13 ----- .../ponder/editor/PonderAnswerEditor.php | 8 ++++ .../ponder/storage/PonderAnswer.php | 34 ++++++++------ .../storage/PonderAnswerTransaction.php | 47 +++++++++++++++---- .../ponder/storage/PonderQuestion.php | 26 +++++++--- 13 files changed, 137 insertions(+), 78 deletions(-) create mode 100644 resources/sql/autopatches/20150806.ponder.answer.1.sql create mode 100644 resources/sql/autopatches/20150806.ponder.editpolicy.2.sql rename src/applications/ponder/capability/{PonderQuestionDefaultViewCapability.php => PonderDefaultViewCapability.php} (53%) create mode 100644 src/applications/ponder/capability/PonderModerateCapability.php delete mode 100644 src/applications/ponder/capability/PonderQuestionDefaultEditCapability.php 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/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 43fc9b3b94..d52f761126 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3402,11 +3402,11 @@ phutil_register_library_map(array( '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', + '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', @@ -7591,7 +7591,9 @@ phutil_register_library_map(array( 'PonderConstants' => 'Phobject', 'PonderController' => 'PhabricatorController', 'PonderDAO' => 'PhabricatorLiskDAO', + 'PonderDefaultViewCapability' => 'PhabricatorPolicyCapability', 'PonderEditor' => 'PhabricatorApplicationTransactionEditor', + 'PonderModerateCapability' => 'PhabricatorPolicyCapability', 'PonderQuestion' => array( 'PonderDAO', 'PhabricatorApplicationTransactionInterface', @@ -7606,8 +7608,6 @@ phutil_register_library_map(array( 'PhabricatorSpacesInterface', ), 'PonderQuestionCommentController' => 'PonderController', - 'PonderQuestionDefaultEditCapability' => 'PhabricatorPolicyCapability', - 'PonderQuestionDefaultViewCapability' => 'PhabricatorPolicyCapability', 'PonderQuestionEditController' => 'PonderController', 'PonderQuestionEditor' => 'PonderEditor', 'PonderQuestionHasVotingUserEdgeType' => 'PhabricatorEdgeType', diff --git a/src/applications/ponder/application/PhabricatorPonderApplication.php b/src/applications/ponder/application/PhabricatorPonderApplication.php index 6ba5e3ca4f..fe1073b85a 100644 --- a/src/applications/ponder/application/PhabricatorPonderApplication.php +++ b/src/applications/ponder/application/PhabricatorPonderApplication.php @@ -93,11 +93,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 @@ 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/PonderQuestionEditController.php b/src/applications/ponder/controller/PonderQuestionEditController.php index d1a8d48f59..293bbe3c79 100644 --- a/src/applications/ponder/controller/PonderQuestionEditController.php +++ b/src/applications/ponder/controller/PonderQuestionEditController.php @@ -31,7 +31,6 @@ 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(); @@ -43,7 +42,6 @@ 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'); @@ -76,10 +74,6 @@ final class PonderQuestionEditController extends PonderController { ->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); @@ -131,13 +125,6 @@ final class PonderQuestionEditController extends PonderController { ->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)) ->appendChild( id(new AphrontFormSelectControl()) ->setLabel(pht('Status')) diff --git a/src/applications/ponder/editor/PonderAnswerEditor.php b/src/applications/ponder/editor/PonderAnswerEditor.php index 69ee10f270..9eea95c511 100644 --- a/src/applications/ponder/editor/PonderAnswerEditor.php +++ b/src/applications/ponder/editor/PonderAnswerEditor.php @@ -10,7 +10,9 @@ final class PonderAnswerEditor extends PonderEditor { $types = parent::getTransactionTypes(); $types[] = PhabricatorTransactions::TYPE_COMMENT; + $types[] = PonderAnswerTransaction::TYPE_CONTENT; + $types[] = PonderAnswerTransaction::TYPE_QUESTION_ID; return $types; } @@ -22,6 +24,8 @@ final class PonderAnswerEditor extends PonderEditor { switch ($xaction->getTransactionType()) { case PonderAnswerTransaction::TYPE_CONTENT: return $object->getContent(); + case PonderAnswerTransaction::TYPE_QUESTION_ID: + return $object->getQuestionID(); } } @@ -31,6 +35,7 @@ final class PonderAnswerEditor extends PonderEditor { switch ($xaction->getTransactionType()) { case PonderAnswerTransaction::TYPE_CONTENT: + case PonderAnswerTransaction::TYPE_QUESTION_ID: return $xaction->getNewValue(); } } @@ -43,6 +48,9 @@ final class PonderAnswerEditor extends PonderEditor { case PonderAnswerTransaction::TYPE_CONTENT: $object->setContent($xaction->getNewValue()); break; + case PonderAnswerTransaction::TYPE_QUESTION_ID: + $object->setQuestionID($xaction->getNewValue()); + break; } } diff --git a/src/applications/ponder/storage/PonderAnswer.php b/src/applications/ponder/storage/PonderAnswer.php index 10ada70c5e..937ae75411 100644 --- a/src/applications/ponder/storage/PonderAnswer.php +++ b/src/applications/ponder/storage/PonderAnswer.php @@ -17,7 +17,6 @@ final class PonderAnswer extends PonderDAO protected $questionID; protected $content; - protected $contentSource; protected $mailKey; protected $voteCount; @@ -27,6 +26,20 @@ final class PonderAnswer extends PonderDAO 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); + + } + public function attachQuestion(PonderQuestion $question = null) { $this->question = $question; return $this; @@ -73,10 +86,6 @@ final class PonderAnswer extends PonderDAO 'voteCount' => 'sint32', 'content' => 'text', 'mailKey' => 'bytes20', - - // T6203/NULLABILITY - // This should always exist. - 'contentSource' => 'text?', ), self::CONFIG_KEY_SCHEMA => array( 'key_phid' => null, @@ -102,15 +111,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 +198,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,6 +226,8 @@ 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; diff --git a/src/applications/ponder/storage/PonderAnswerTransaction.php b/src/applications/ponder/storage/PonderAnswerTransaction.php index 08af2470ad..07b9768c82 100644 --- a/src/applications/ponder/storage/PonderAnswerTransaction.php +++ b/src/applications/ponder/storage/PonderAnswerTransaction.php @@ -4,6 +4,7 @@ final class PonderAnswerTransaction extends PhabricatorApplicationTransaction { const TYPE_CONTENT = 'ponder.answer:content'; + const TYPE_QUESTION_ID = 'ponder.answer:question-id'; public function getApplicationName() { return 'ponder'; @@ -45,16 +46,35 @@ 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; } return parent::getTitle(); @@ -64,12 +84,23 @@ 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; } return parent::getTitleForFeed(); diff --git a/src/applications/ponder/storage/PonderQuestion.php b/src/applications/ponder/storage/PonderQuestion.php index 0e1ea0226b..14fafcecbf 100644 --- a/src/applications/ponder/storage/PonderQuestion.php +++ b/src/applications/ponder/storage/PonderQuestion.php @@ -23,7 +23,6 @@ final class PonderQuestion extends PonderDAO protected $content; protected $contentSource; protected $viewPolicy; - protected $editPolicy; protected $spacePHID; protected $voteCount; @@ -44,14 +43,11 @@ 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) @@ -275,17 +271,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; } From 0c3f74663c91ad881002987ea1d83b370e42be45 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sat, 8 Aug 2015 14:04:26 -0700 Subject: [PATCH 08/39] Remove Ponder voting UI Summary: Ref T6920, This just removes the old voting UI from Ponder. Test Plan: Visit a Question, no voting UI Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Maniphest Tasks: T6920 Differential Revision: https://secure.phabricator.com/D13827 --- resources/celerity/map.php | 13 +-- src/__phutil_library_map__.php | 2 - .../PonderQuestionViewController.php | 14 --- .../ponder/view/PonderVotableView.php | 92 ------------------- .../css/application/ponder/ponder-view.css | 39 -------- .../js/application/ponder/behavior-votebox.js | 57 ------------ 6 files changed, 2 insertions(+), 215 deletions(-) delete mode 100644 src/applications/ponder/view/PonderVotableView.php delete mode 100644 webroot/rsrc/js/application/ponder/behavior-votebox.js diff --git a/resources/celerity/map.php b/resources/celerity/map.php index da014bccde..10a54b7e8e 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' => '4e557c89', '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', @@ -400,7 +400,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 +631,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', @@ -811,7 +809,7 @@ return array( 'policy-css' => '957ea14c', 'policy-edit-css' => '815c66f7', 'policy-transaction-detail-css' => '82100a43', - 'ponder-view-css' => 'fcd6b398', + 'ponder-view-css' => '4e557c89', 'project-icon-css' => '4e3eaa5a', 'raphael-core' => '51ee6b43', 'raphael-g' => '40dde778', @@ -1142,13 +1140,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/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index d52f761126..36675ef9f5 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3428,7 +3428,6 @@ 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', @@ -7628,7 +7627,6 @@ phutil_register_library_map(array( 'PonderSchemaSpec' => 'PhabricatorConfigSchemaSpec', 'PonderSearchIndexer' => 'PhabricatorSearchDocumentIndexer', 'PonderTransactionFeedStory' => 'PhabricatorApplicationTransactionFeedStory', - 'PonderVotableView' => 'AphrontView', 'PonderVote' => 'PonderConstants', 'PonderVoteEditor' => 'PhabricatorEditor', 'PonderVoteSaveController' => 'PonderController', diff --git a/src/applications/ponder/controller/PonderQuestionViewController.php b/src/applications/ponder/controller/PonderQuestionViewController.php index c5349c5eab..cad39b4135 100644 --- a/src/applications/ponder/controller/PonderQuestionViewController.php +++ b/src/applications/ponder/controller/PonderQuestionViewController.php @@ -155,16 +155,9 @@ 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( @@ -334,16 +327,9 @@ final class PonderQuestionViewController extends PonderController { $view->invokeWillRenderEvent(); - $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( 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/webroot/rsrc/css/application/ponder/ponder-view.css b/webroot/rsrc/css/application/ponder/ponder-view.css index 237c3e7676..4cf9509281 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; 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); - }); -}); From 7e7e38e9c00981d0514c95f741984ffb0c64ad67 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sat, 8 Aug 2015 20:29:37 -0700 Subject: [PATCH 09/39] Remove VotableInterface from PonderQuestion Summary: Ref T6920, This removes the PonderVotableInterface from PonderQuestion and assocaited code. Also... never used? Test Plan: Visit Ponder, See List, New Question, Add Answer. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Maniphest Tasks: T6920 Differential Revision: https://secure.phabricator.com/D13833 --- .../autopatches/20150808.ponder.vote.1.sql | 2 + .../autopatches/20150808.ponder.vote.2.sql | 2 + src/__phutil_library_map__.php | 5 - .../PonderQuestionViewController.php | 3 - .../PonderQuestionHasVotingUserEdgeType.php | 105 ------------------ .../PonderVotingUserHasQuestionEdgeType.php | 105 ------------------ .../ponder/query/PonderQuestionQuery.php | 30 ----- .../ponder/storage/PonderQuestion.php | 63 ----------- 8 files changed, 4 insertions(+), 311 deletions(-) create mode 100644 resources/sql/autopatches/20150808.ponder.vote.1.sql create mode 100644 resources/sql/autopatches/20150808.ponder.vote.2.sql delete mode 100644 src/applications/ponder/edge/PonderQuestionHasVotingUserEdgeType.php delete mode 100644 src/applications/ponder/edge/PonderVotingUserHasQuestionEdgeType.php 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/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 36675ef9f5..c3a7d893b7 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3409,7 +3409,6 @@ phutil_register_library_map(array( 'PonderQuestionCommentController' => 'applications/ponder/controller/PonderQuestionCommentController.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', @@ -3432,7 +3431,6 @@ phutil_register_library_map(array( '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', @@ -7597,7 +7595,6 @@ phutil_register_library_map(array( 'PonderDAO', 'PhabricatorApplicationTransactionInterface', 'PhabricatorMarkupInterface', - 'PonderVotableInterface', 'PhabricatorSubscribableInterface', 'PhabricatorFlaggableInterface', 'PhabricatorPolicyInterface', @@ -7609,7 +7606,6 @@ phutil_register_library_map(array( 'PonderQuestionCommentController' => 'PonderController', 'PonderQuestionEditController' => 'PonderController', 'PonderQuestionEditor' => 'PonderEditor', - 'PonderQuestionHasVotingUserEdgeType' => 'PhabricatorEdgeType', 'PonderQuestionHistoryController' => 'PonderController', 'PonderQuestionListController' => 'PonderController', 'PonderQuestionMailReceiver' => 'PhabricatorObjectMailReceiver', @@ -7631,7 +7627,6 @@ phutil_register_library_map(array( 'PonderVoteEditor' => 'PhabricatorEditor', 'PonderVoteSaveController' => 'PonderController', 'PonderVotingUserHasAnswerEdgeType' => 'PhabricatorEdgeType', - 'PonderVotingUserHasQuestionEdgeType' => 'PhabricatorEdgeType', 'ProjectAddProjectsEmailCommand' => 'MetaMTAEmailTransactionCommand', 'ProjectBoardTaskCard' => 'Phobject', 'ProjectCanLockProjectsCapability' => 'PhabricatorPolicyCapability', diff --git a/src/applications/ponder/controller/PonderQuestionViewController.php b/src/applications/ponder/controller/PonderQuestionViewController.php index cad39b4135..30c56a0cc4 100644 --- a/src/applications/ponder/controller/PonderQuestionViewController.php +++ b/src/applications/ponder/controller/PonderQuestionViewController.php @@ -10,14 +10,11 @@ final class PonderQuestionViewController extends PonderController { ->setViewer($viewer) ->withIDs(array($id)) ->needAnswers(true) - ->needViewerVotes(true) ->executeOne(); if (!$question) { return new Aphront404Response(); } - $question->attachVotes($viewer->getPHID()); - $question_xactions = $this->buildQuestionTransactions($question); $answers = $this->buildAnswers($question->getAnswers()); 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 @@ -ids = $ids; @@ -44,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; @@ -106,10 +100,6 @@ final class PonderQuestionQuery ->setOrderVector(array('-id')) ->withQuestionIDs(mpull($questions, 'getID')); - if ($this->needViewerVotes) { - $aquery->needViewerVotes($this->needViewerVotes); - } - $answers = $aquery->execute(); $answers = mgroup($answers, 'getQuestionID'); @@ -119,26 +109,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/storage/PonderQuestion.php b/src/applications/ponder/storage/PonderQuestion.php index 14fafcecbf..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, @@ -25,13 +24,10 @@ final class PonderQuestion extends PonderDAO protected $viewPolicy; protected $spacePHID; - protected $voteCount; protected $answerCount; - protected $heat; protected $mailKey; private $answers; - private $vote; private $comments; private $projectPHIDs = self::ATTACHABLE; @@ -49,9 +45,7 @@ final class PonderQuestion extends PonderDAO ->setAuthorPHID($actor->getPHID()) ->setViewPolicy($view_policy) ->setStatus(PonderQuestionStatus::STATUS_OPEN) - ->setVoteCount(0) ->setAnswerCount(0) - ->setHeat(0.0) ->setSpacePHID($actor->getDefaultSpacePHID()); } @@ -60,10 +54,8 @@ final class PonderQuestion extends PonderDAO self::CONFIG_AUX_PHID => true, self::CONFIG_COLUMN_SCHEMA => array( 'title' => 'text255', - 'voteCount' => 'sint32', 'status' => 'text32', 'content' => 'text', - 'heat' => 'double', 'answerCount' => 'uint32', 'mailKey' => 'bytes20', @@ -80,9 +72,6 @@ final class PonderQuestion extends PonderDAO 'authorPHID' => array( 'columns' => array('authorPHID'), ), - 'heat' => array( - 'columns' => array('heat'), - ), 'status' => array( 'columns' => array('status'), ), @@ -103,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; @@ -229,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)); From 384cae49424cd9a25b9816f18979a464638d4eb9 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Mon, 10 Aug 2015 09:07:40 -0700 Subject: [PATCH 10/39] Update Daemons for handleRequest Summary: Updates the Daemon application calls Test Plan: Click on various things in the Daemon app Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D13835 --- ...PhabricatorDaemonLogEventViewController.php | 12 +++--------- .../PhabricatorDaemonLogListController.php | 5 ++--- .../PhabricatorDaemonLogViewController.php | 18 ++++++------------ .../PhabricatorWorkerTaskDetailController.php | 16 +++++----------- 4 files changed, 16 insertions(+), 35 deletions(-) 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..a0c8608935 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); } From b92e6d3582f68ff99f06276fffb6bea23682f403 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Mon, 10 Aug 2015 09:07:51 -0700 Subject: [PATCH 11/39] Update Dashboards for handleRequest Summary: Updates Dashboards app Test Plan: Click on Manage Dashboard, page load a.ok Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D13836 --- .../PhabricatorDashboardManageController.php | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) 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) { From 803e5dc7e268e222aaad31bf392b57ff4333cea8 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Mon, 10 Aug 2015 09:08:04 -0700 Subject: [PATCH 12/39] Update Doorkeeper for handleRequest Summary: Updates Doorkeeper Test Plan: Peer Review Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D13837 --- .../doorkeeper/controller/DoorkeeperTagsController.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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 { From 97e30c1d06cb2c235acb87b0846b44d8c07d4125 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Mon, 10 Aug 2015 09:08:21 -0700 Subject: [PATCH 13/39] Update Celerity for handleRequest Summary: Updates Celerity controllers Test Plan: View Phabricator, change to high contrast, change to larger fonts. Everything seems ok? Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D13838 --- .../CelerityPhabricatorResourceController.php | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) 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 { From 0e7efceb5123da21084ab24881dc58875e6a6b7b Mon Sep 17 00:00:00 2001 From: Chad Little Date: Mon, 10 Aug 2015 09:08:33 -0700 Subject: [PATCH 14/39] Update Meta for handleRequest Summary: Updates Applications application for handleRequest Test Plan: Install, Uninstall an application Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D13839 --- ...abricatorApplicationUninstallController.php | 18 +++++++----------- .../PhabricatorApplicationsListController.php | 10 ++-------- 2 files changed, 9 insertions(+), 19 deletions(-) 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()); From 2752419160a11c62380b8f1ff661bb342a4e46a0 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Mon, 10 Aug 2015 09:09:07 -0700 Subject: [PATCH 15/39] Add Mark as Helpful to PonderAnswer Summary: Ref T6920, this adds a basic controller for marking an answer as helpful and removes the negative voting. Any current positive vote is kept as helpful. New UI is needed here, but there is a separate task for redesigning Ponder overall. Test Plan: Mark an answer as helpful, see count go up, remove helpful, see count go down. Test endpoint manually. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Maniphest Tasks: T6920 Differential Revision: https://secure.phabricator.com/D13834 --- src/__phutil_library_map__.php | 4 +- .../PhabricatorPonderApplication.php | 3 +- .../ponder/constants/PonderVote.php | 1 - .../PonderHelpfulSaveController.php | 60 +++++++++++++++++++ .../PonderQuestionViewController.php | 26 ++++++++ .../controller/PonderVoteSaveController.php | 32 ---------- .../ponder/query/PonderAnswerQuery.php | 1 - .../ponder/query/PonderQuestionQuery.php | 1 + 8 files changed, 91 insertions(+), 37 deletions(-) create mode 100644 src/applications/ponder/controller/PonderHelpfulSaveController.php delete mode 100644 src/applications/ponder/controller/PonderVoteSaveController.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index c3a7d893b7..acda1020a9 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3404,6 +3404,7 @@ phutil_register_library_map(array( 'PonderDAO' => 'applications/ponder/storage/PonderDAO.php', 'PonderDefaultViewCapability' => 'applications/ponder/capability/PonderDefaultViewCapability.php', 'PonderEditor' => 'applications/ponder/editor/PonderEditor.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', @@ -3429,7 +3430,6 @@ phutil_register_library_map(array( 'PonderVotableInterface' => 'applications/ponder/storage/PonderVotableInterface.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', 'ProjectAddProjectsEmailCommand' => 'applications/project/command/ProjectAddProjectsEmailCommand.php', 'ProjectBoardTaskCard' => 'applications/project/view/ProjectBoardTaskCard.php', @@ -7590,6 +7590,7 @@ phutil_register_library_map(array( 'PonderDAO' => 'PhabricatorLiskDAO', 'PonderDefaultViewCapability' => 'PhabricatorPolicyCapability', 'PonderEditor' => 'PhabricatorApplicationTransactionEditor', + 'PonderHelpfulSaveController' => 'PonderController', 'PonderModerateCapability' => 'PhabricatorPolicyCapability', 'PonderQuestion' => array( 'PonderDAO', @@ -7625,7 +7626,6 @@ phutil_register_library_map(array( 'PonderTransactionFeedStory' => 'PhabricatorApplicationTransactionFeedStory', 'PonderVote' => 'PonderConstants', 'PonderVoteEditor' => 'PhabricatorEditor', - 'PonderVoteSaveController' => 'PonderController', 'PonderVotingUserHasAnswerEdgeType' => 'PhabricatorEdgeType', 'ProjectAddProjectsEmailCommand' => 'MetaMTAEmailTransactionCommand', 'ProjectBoardTaskCard' => 'Phobject', diff --git a/src/applications/ponder/application/PhabricatorPonderApplication.php b/src/applications/ponder/application/PhabricatorPonderApplication.php index fe1073b85a..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/' @@ -73,7 +75,6 @@ final class PhabricatorPonderApplication extends PhabricatorApplication { => 'PhabricatorMarkupPreviewController', 'question/status/(?P[1-9]\d*)/' => 'PonderQuestionStatusController', - 'vote/' => 'PonderVoteSaveController', ), ); } 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/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/PonderQuestionViewController.php b/src/applications/ponder/controller/PonderQuestionViewController.php index 30c56a0cc4..1fc9c05a0f 100644 --- a/src/applications/ponder/controller/PonderQuestionViewController.php +++ b/src/applications/ponder/controller/PonderQuestionViewController.php @@ -291,6 +291,28 @@ final class PonderQuestionViewController extends PonderController { ->setObject($answer) ->setObjectURI($request->getRequestURI()); + $user_marked = $answer->getUserVote(); + $can_vote = $viewer->isLoggedIn(); + + if ($user_marked) { + $helpful_uri = "/answer/helpful/remove/{$id}/"; + $helpful_icon = 'fa-times'; + $helpful_text = pht('Remove Helpful'); + } else { + $helpful_uri = "/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($this->getApplicationURI($helpful_uri)) + ->setRenderAsForm(true) + ->setDisabled(!$can_vote) + ->setWorkflow($can_vote)); + $view->addAction( id(new PhabricatorActionView()) ->setIcon('fa-pencil') @@ -322,6 +344,10 @@ final class PonderQuestionViewController extends PonderController { pht('Created'), phabricator_datetime($answer->getDateCreated(), $viewer)); + $view->addProperty( + pht('Helpfuls'), + $answer->getVoteCount()); + $view->invokeWillRenderEvent(); $view->addSectionHeader(pht('Answer')); 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/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 b8eae2da81..dd5db40a48 100644 --- a/src/applications/ponder/query/PonderQuestionQuery.php +++ b/src/applications/ponder/query/PonderQuestionQuery.php @@ -98,6 +98,7 @@ final class PonderQuestionQuery $aquery = id(new PonderAnswerQuery()) ->setViewer($this->getViewer()) ->setOrderVector(array('-id')) + ->needViewerVotes(true) ->withQuestionIDs(mpull($questions, 'getID')); $answers = $aquery->execute(); From a1499ceb8f619124fdf6bcbe5c5042eadb941fb6 Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 10 Aug 2015 14:14:52 -0700 Subject: [PATCH 16/39] Don't let autoplans be run manually Summary: Ref T8096. Like stop/start, autoplans are pushed into the system from outside (normally by `arc`) so it doesn't make any sense to run them manually. Test Plan: - Tried to run an autoplan from web UI, got an error. - Ran a normal plan from web UI. - Tried to run an autoplan from CLI, got an error. - Ran a noraml plan from CLI. Reviewers: chad Reviewed By: chad Maniphest Tasks: T8096 Differential Revision: https://secure.phabricator.com/D13844 --- .../controller/HarbormasterPlanRunController.php | 10 +++++++++- .../controller/HarbormasterPlanViewController.php | 4 +++- .../management/HarbormasterManagementBuildWorkflow.php | 5 +++++ .../storage/configuration/HarbormasterBuildPlan.php | 9 +++++++++ 4 files changed, 26 insertions(+), 2 deletions(-) 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/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) { From a1431e53cc0110c8076328d452d17c48ef3b050a Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 10 Aug 2015 14:15:19 -0700 Subject: [PATCH 17/39] Link to Harbormaster build targets from the Daemon worker page Summary: Fixes T7370. Two changes: - Make the default to show nothing, instead of showing all the data. This is a better default because the data is sometimes sensitive. Workers should have to opt in to revealing it. - For TargetWorkers, link to the target (technically the build, for now, since there's no dedicated target detail page). Test Plan: {F698325} Reviewers: chad Reviewed By: chad Maniphest Tasks: T7370 Differential Revision: https://secure.phabricator.com/D13845 --- .../PhabricatorWorkerTaskDetailController.php | 6 ++--- .../phid/HarbormasterBuildPHIDType.php | 11 ++++---- .../phid/HarbormasterBuildTargetPHIDType.php | 12 ++++++++- .../worker/HarbormasterBuildWorker.php | 27 +++++++++++++++---- .../worker/HarbormasterTargetWorker.php | 10 +++++++ .../PhabricatorRepositoryPushMailWorker.php | 5 ---- .../daemon/workers/PhabricatorWorker.php | 3 +-- .../sms/worker/PhabricatorSMSWorker.php | 5 ---- 8 files changed, 52 insertions(+), 27 deletions(-) diff --git a/src/applications/daemon/controller/PhabricatorWorkerTaskDetailController.php b/src/applications/daemon/controller/PhabricatorWorkerTaskDetailController.php index a0c8608935..ad8f673fd7 100644 --- a/src/applications/daemon/controller/PhabricatorWorkerTaskDetailController.php +++ b/src/applications/daemon/controller/PhabricatorWorkerTaskDetailController.php @@ -149,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/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/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/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/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/sms/worker/PhabricatorSMSWorker.php b/src/infrastructure/sms/worker/PhabricatorSMSWorker.php index a999fe5e82..145a371e7d 100644 --- a/src/infrastructure/sms/worker/PhabricatorSMSWorker.php +++ b/src/infrastructure/sms/worker/PhabricatorSMSWorker.php @@ -3,9 +3,4 @@ abstract class PhabricatorSMSWorker extends PhabricatorWorker { - public function renderForDisplay(PhabricatorUser $viewer) { - // This data has some sensitive stuff, so don't show it. - return null; - } - } From c2c8b00c5c6bfb35bfcd718931384b5411fd82c4 Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 10 Aug 2015 14:15:43 -0700 Subject: [PATCH 18/39] Always show build target tabs, even if they have no data Summary: Ref T8096. Hit this on IRC: > epriestley: what's in the Messages tab? > jdoe: what Messages tab??! > epriestley: ughhhh Test Plan: Clicked "Messages" tab, saw helpful "no messages" message. Reviewers: chad Reviewed By: chad Maniphest Tasks: T8096 Differential Revision: https://secure.phabricator.com/D13846 --- .../HarbormasterBuildViewController.php | 39 +++++++------------ 1 file changed, 14 insertions(+), 25 deletions(-) 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) { From 2b13da88ceb9c984eacb31103ee1476d1663f0c7 Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 10 Aug 2015 14:16:14 -0700 Subject: [PATCH 19/39] Read synthetic inline comments from Harbormaster Summary: Ref T8096. These are still reading out of the old diff property, but should read from Harbormaster instead. Test Plan: {F698346} Reviewers: chad Reviewed By: chad Maniphest Tasks: T8096 Differential Revision: https://secure.phabricator.com/D13847 --- .../DifferentialChangesetViewController.php | 78 ++++++++++++++----- 1 file changed, 59 insertions(+), 19 deletions(-) diff --git a/src/applications/differential/controller/DifferentialChangesetViewController.php b/src/applications/differential/controller/DifferentialChangesetViewController.php index 2373d3ef2c..28a6e26b2c 100644 --- a/src/applications/differential/controller/DifferentialChangesetViewController.php +++ b/src/applications/differential/controller/DifferentialChangesetViewController.php @@ -203,6 +203,29 @@ final class DifferentialChangesetViewController extends DifferentialController { $inlines = array(); } + 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)); + } + } + if ($left_new) { $inlines = array_merge( $inlines, @@ -356,30 +379,47 @@ 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(); + + $buildable = $diff->getBuildable(); + if (!$buildable) { return array(); } - $lint = $lint->getData(); + + $target_phids = array(); + foreach ($buildable->getBuilds() as $build) { + foreach ($build->getBuildTargets() as $target) { + $target_phids[] = $target->getPHID(); + } + } + + if (!$target_phids) { + return array(); + } + + $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; From 60a529b9d1926a3e8198eeffa3466c8b17ca7861 Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 10 Aug 2015 14:16:25 -0700 Subject: [PATCH 20/39] Remove some obsolete lint and unit support Summary: Ref T8096. Long ago, support for "postponed" lint and unit tests got hacked in. `arc` would publish a bunch of ghost results, and then something else would fill the results in later. This was always a hack. It is not nearly as powerful or flexible as having a real build system, and is obsolete with Harbormaster, which supports these operations in a more reasonable and straightforward way. This was used (only? almost only?) at Facebook. - Remove `differential.finishpostponedlinters`. This only served to update postponed linters. - Remove lint magic in `differential.setdiffproperty`. This magic only made sense in the context of postponed linters. - Remove `differential.updateunitresults`. The only made sense for postponed unit tests. And one minor change: when a diff contains >100 affected files, we hide the content by default, but show content for files with inline comments. Previously, we'd do this for lint inlines, too. I don't tink this is too useful, and it's much simpler to just remove it. We could add it back at some point, but I think large changes often trigger a lot of lint and no one actually cares. The behavior for actual human inlines is retained. Test Plan: `grep` Reviewers: chad Reviewed By: chad Maniphest Tasks: T8096 Differential Revision: https://secure.phabricator.com/D13848 --- src/__phutil_library_map__.php | 4 - ...FinishPostponedLintersConduitAPIMethod.php | 118 -------------- ...rentialSetDiffPropertyConduitAPIMethod.php | 60 ------- ...ntialUpdateUnitResultsConduitAPIMethod.php | 154 ------------------ .../DifferentialRevisionViewController.php | 10 -- 5 files changed, 346 deletions(-) delete mode 100644 src/applications/differential/conduit/DifferentialFinishPostponedLintersConduitAPIMethod.php delete mode 100644 src/applications/differential/conduit/DifferentialUpdateUnitResultsConduitAPIMethod.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index acda1020a9..633efef26c 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', @@ -3999,7 +3997,6 @@ phutil_register_library_map(array( 'DifferentialFieldParseException' => 'Exception', 'DifferentialFieldValidationException' => 'Exception', 'DifferentialFindConduitAPIMethod' => 'DifferentialConduitAPIMethod', - 'DifferentialFinishPostponedLintersConduitAPIMethod' => 'DifferentialConduitAPIMethod', 'DifferentialGetAllDiffsConduitAPIMethod' => 'DifferentialConduitAPIMethod', 'DifferentialGetCommitMessageConduitAPIMethod' => 'DifferentialConduitAPIMethod', 'DifferentialGetCommitPathsConduitAPIMethod' => 'DifferentialConduitAPIMethod', @@ -4136,7 +4133,6 @@ phutil_register_library_map(array( 'DifferentialUnitStatus' => 'Phobject', 'DifferentialUnitTestResult' => 'Phobject', 'DifferentialUpdateRevisionConduitAPIMethod' => 'DifferentialConduitAPIMethod', - 'DifferentialUpdateUnitResultsConduitAPIMethod' => 'DifferentialConduitAPIMethod', 'DifferentialViewPolicyField' => 'DifferentialCoreCustomField', 'DiffusionAuditorDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 'DiffusionAuditorsAddAuditorsHeraldAction' => 'DiffusionAuditorsHeraldAction', 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/DifferentialRevisionViewController.php b/src/applications/differential/controller/DifferentialRevisionViewController.php index 94d9db0716..6c695b8ce4 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; From efa8855e030876fdc2ee6274caa8c0e526d1a460 Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 10 Aug 2015 14:16:36 -0700 Subject: [PATCH 21/39] Read Differential changeset coverage information from Harbormaster Summary: Ref T8096. This information has moved into Harbormaster. Test Plan: Pushed some test results in; see right margin: {F698394} Reviewers: chad Reviewed By: chad Maniphest Tasks: T8096 Differential Revision: https://secure.phabricator.com/D13850 --- .../DifferentialChangesetViewController.php | 106 ++++++++---------- .../DifferentialHarbormasterField.php | 19 +--- .../differential/storage/DifferentialDiff.php | 16 +++ 3 files changed, 70 insertions(+), 71 deletions(-) diff --git a/src/applications/differential/controller/DifferentialChangesetViewController.php b/src/applications/differential/controller/DifferentialChangesetViewController.php index 28a6e26b2c..5efb8561af 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'); @@ -203,29 +206,6 @@ final class DifferentialChangesetViewController extends DifferentialController { $inlines = array(); } - 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)); - } - } - if ($left_new) { $inlines = array_merge( $inlines, @@ -381,18 +361,7 @@ final class DifferentialChangesetViewController extends DifferentialController { private function buildLintInlineComments($changeset) { $diff = $changeset->getDiff(); - $buildable = $diff->getBuildable(); - if (!$buildable) { - return array(); - } - - $target_phids = array(); - foreach ($buildable->getBuilds() as $build) { - foreach ($build->getBuildTargets() as $target) { - $target_phids[] = $target->getPHID(); - } - } - + $target_phids = $diff->getBuildTargetPHIDs(); if (!$target_phids) { return array(); } @@ -425,4 +394,27 @@ final class DifferentialChangesetViewController extends DifferentialController { 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/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/storage/DifferentialDiff.php b/src/applications/differential/storage/DifferentialDiff.php index 4a0e63d5a7..e30a401306 100644 --- a/src/applications/differential/storage/DifferentialDiff.php +++ b/src/applications/differential/storage/DifferentialDiff.php @@ -331,6 +331,22 @@ 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; + } /* -( PhabricatorPolicyInterface )----------------------------------------- */ From b4e038d2af4940f229296bc6061d7a7ea140b8df Mon Sep 17 00:00:00 2001 From: Chad Little Date: Mon, 10 Aug 2015 14:55:43 -0700 Subject: [PATCH 22/39] Add similiar questions sidebar to Ponder Summary: Ref T9099. Testing out a two column layout in Ponder, with the main idea being creating a more browsable, discoverable product. I'd like the side column though to be a little smarter and provide project based searching. Ideally, if I'm reading Resolved Maniphest questions, other Resolved Maniphest questions are likely interesting. Another scenario is if I'm answering questions, in which case browsing more Open questions would also be interesting. Ponder "Main Column" still needs to be redesigned. Test Plan: Browse open questions, resolved questions. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Maniphest Tasks: T9099 Differential Revision: https://secure.phabricator.com/D13849 --- resources/celerity/map.php | 2 + src/__phutil_library_map__.php | 2 + .../ponder/constants/PonderQuestionStatus.php | 10 +++ .../PonderQuestionViewController.php | 66 +++++++++++++++---- src/view/phui/PHUITwoColumnView.php | 48 ++++++++++++++ .../rsrc/css/phui/phui-two-column-view.css | 33 ++++++++++ 6 files changed, 149 insertions(+), 12 deletions(-) create mode 100644 src/view/phui/PHUITwoColumnView.php create mode 100644 webroot/rsrc/css/phui/phui-two-column-view.css diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 10a54b7e8e..5c3fcbbeeb 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -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', @@ -801,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', diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 633efef26c..9e76723b92 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1406,6 +1406,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', @@ -5183,6 +5184,7 @@ phutil_register_library_map(array( 'PHUITimelineEventView' => 'AphrontView', 'PHUITimelineExample' => 'PhabricatorUIExample', 'PHUITimelineView' => 'AphrontView', + 'PHUITwoColumnView' => 'AphrontTagView', 'PHUITypeaheadExample' => 'PhabricatorUIExample', 'PHUIWorkboardView' => 'AphrontTagView', 'PHUIWorkpanelView' => 'AphrontTagView', diff --git a/src/applications/ponder/constants/PonderQuestionStatus.php b/src/applications/ponder/constants/PonderQuestionStatus.php index 6c612ec4ae..f37a208bcc 100644 --- a/src/applications/ponder/constants/PonderQuestionStatus.php +++ b/src/applications/ponder/constants/PonderQuestionStatus.php @@ -26,6 +26,16 @@ final class PonderQuestionStatus extends PonderConstants { 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 => diff --git a/src/applications/ponder/controller/PonderQuestionViewController.php b/src/applications/ponder/controller/PonderQuestionViewController.php index 1fc9c05a0f..10236d775e 100644 --- a/src/applications/ponder/controller/PonderQuestionViewController.php +++ b/src/applications/ponder/controller/PonderQuestionViewController.php @@ -10,6 +10,7 @@ final class PonderQuestionViewController extends PonderController { ->setViewer($viewer) ->withIDs(array($id)) ->needAnswers(true) + ->needProjectPHIDs(true) ->executeOne(); if (!$question) { return new Aphront404Response(); @@ -51,6 +52,7 @@ final class PonderQuestionViewController extends PonderController { $actions = $this->buildActionListView($question); $properties = $this->buildPropertyListView($question, $actions); + $sidebar = $this->buildSidebar($question); $object_box = id(new PHUIObjectBoxView()) ->setHeader($header) @@ -59,21 +61,19 @@ final class PonderQuestionViewController extends PonderController { $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, + $question_xactions, + $answers, + $answer_add_panel, + )) + ->setSideColumn($sidebar) + ->addClass('ponder-question-view'); return $this->buildApplicationPage( array( + $crumbs, $ponder_view, ), array( @@ -410,4 +410,46 @@ final class PonderQuestionViewController extends PonderController { return array($show, $hide); } + private function buildSidebar(PonderQuestion $question) { + $viewer = $this->getViewer(); + $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) + ->setNoDataString(pht('No similar questions found.')); + + 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); + + $item->addAttribute( + pht('%d Answer(s)', $question->getAnswerCount())); + + $list->addItem($item); + } + + $box = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Similar Questions')) + ->setObjectList($list); + + return $box; + } + } 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/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; +} From bcdb7e9a2c697b5890a789a28006678b06c22d2c Mon Sep 17 00:00:00 2001 From: Joshua Spence Date: Tue, 11 Aug 2015 07:04:16 +1000 Subject: [PATCH 23/39] Use the configuration driven unit test engine Summary: Ref T5568. Use the very-new, very-beta `ArcanistConfigurationDrivenUnitTestEngine` unit test engine. Test Plan: `arc unit` Reviewers: epriestley, #blessed_reviewers Reviewed By: epriestley, #blessed_reviewers Subscribers: Korvin Maniphest Tasks: T5568 Differential Revision: https://secure.phabricator.com/D13853 --- .arcconfig | 1 - .arcunit | 8 ++++++++ 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 .arcunit 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$)" + } + } +} From 5485fb8aa9e7f2652c2a15accd7da855d4179c02 Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 10 Aug 2015 15:24:15 -0700 Subject: [PATCH 24/39] Read modern coverage information for tables of contents Summary: Ref T8096. This modernizes the last thing which was reading the old datasource. Also fix a bug where it didn't work. Test Plan: {F698405} Reviewers: chad Reviewed By: chad Maniphest Tasks: T8096 Differential Revision: https://secure.phabricator.com/D13852 --- .../DifferentialChangesetViewController.php | 10 +++++--- .../DifferentialDiffViewController.php | 2 +- .../DifferentialRevisionViewController.php | 2 +- .../differential/storage/DifferentialDiff.php | 25 +++++++++++++++++++ .../DifferentialDiffTableOfContentsView.php | 25 +++---------------- 5 files changed, 38 insertions(+), 26 deletions(-) diff --git a/src/applications/differential/controller/DifferentialChangesetViewController.php b/src/applications/differential/controller/DifferentialChangesetViewController.php index 5efb8561af..a73875c8f8 100644 --- a/src/applications/differential/controller/DifferentialChangesetViewController.php +++ b/src/applications/differential/controller/DifferentialChangesetViewController.php @@ -251,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()); } diff --git a/src/applications/differential/controller/DifferentialDiffViewController.php b/src/applications/differential/controller/DifferentialDiffViewController.php index d095465604..9756634a81 100644 --- a/src/applications/differential/controller/DifferentialDiffViewController.php +++ b/src/applications/differential/controller/DifferentialDiffViewController.php @@ -108,7 +108,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 6c695b8ce4..085e888fe5 100644 --- a/src/applications/differential/controller/DifferentialRevisionViewController.php +++ b/src/applications/differential/controller/DifferentialRevisionViewController.php @@ -353,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/storage/DifferentialDiff.php b/src/applications/differential/storage/DifferentialDiff.php index e30a401306..e48ed5630d 100644 --- a/src/applications/differential/storage/DifferentialDiff.php +++ b/src/applications/differential/storage/DifferentialDiff.php @@ -348,6 +348,31 @@ final class DifferentialDiff 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 { From 2cf9ded87892cef3312ac0e0d5b87d30afdc9d31 Mon Sep 17 00:00:00 2001 From: Joshua Spence Date: Tue, 11 Aug 2015 22:36:15 +1000 Subject: [PATCH 25/39] Various linter fixes Summary: Self explanatory. Test Plan: `arc lint` Reviewers: epriestley, #blessed_reviewers Reviewed By: epriestley, #blessed_reviewers Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D13863 --- .../AphrontApplicationConfiguration.php | 15 +++++++------- .../__tests__/ConpherenceTestCase.php | 14 +++++++------ ...DifferentialCreateDiffConduitAPIMethod.php | 7 ++++--- ...ferentialCreateRawDiffConduitAPIMethod.php | 7 ++++--- .../parser/DifferentialHunkParser.php | 6 +++--- .../PhabricatorHomeMainController.php | 2 +- .../PassphraseCredentialRevealController.php | 7 ++++--- ...PhabricatorProjectColumnHideController.php | 7 ++++--- .../engine/PhabricatorElasticSearchEngine.php | 2 +- .../uiexample/examples/PHUIButtonExample.php | 3 ++- .../sms/worker/PhabricatorSMSWorker.php | 4 +--- .../lisk/__tests__/LiskChunkTestCase.php | 19 ++++++++++++++---- .../__tests__/PhabricatorHashTestCase.php | 20 ++++++++++++++++--- src/view/AphrontDialogView.php | 9 +++++---- .../__tests__/PhabricatorUnitsTestCase.php | 2 +- 15 files changed, 78 insertions(+), 46 deletions(-) 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/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/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/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/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/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/project/controller/PhabricatorProjectColumnHideController.php b/src/applications/project/controller/PhabricatorProjectColumnHideController.php index 167cbfbd2e..ae39e783bb 100644 --- a/src/applications/project/controller/PhabricatorProjectColumnHideController.php +++ b/src/applications/project/controller/PhabricatorProjectColumnHideController.php @@ -60,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/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/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/sms/worker/PhabricatorSMSWorker.php b/src/infrastructure/sms/worker/PhabricatorSMSWorker.php index 145a371e7d..7b8f851eaa 100644 --- a/src/infrastructure/sms/worker/PhabricatorSMSWorker.php +++ b/src/infrastructure/sms/worker/PhabricatorSMSWorker.php @@ -1,6 +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/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', From 53ffaaa889ec0cdc6f2a570b80aefd9deb76cb28 Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 11 Aug 2015 06:39:05 -0700 Subject: [PATCH 26/39] Load buildables in DiffViewController Summary: Fixes T9128. I missed this when converting other controllers to look in Harbormaster for lint/unit data. Test Plan: Copy/pasted a diff successfully. Reviewers: chad Reviewed By: chad Maniphest Tasks: T9128 Differential Revision: https://secure.phabricator.com/D13865 --- .../controller/DifferentialDiffViewController.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/applications/differential/controller/DifferentialDiffViewController.php b/src/applications/differential/controller/DifferentialDiffViewController.php index 9756634a81..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')); From 306af6fb2892b2841f7e26f9c6b76e78a4df8686 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Tue, 11 Aug 2015 10:42:40 -0700 Subject: [PATCH 27/39] Update Ponder Answer layout Summary: Ref T9099, A step forward for the main Ponder UI. Mostly moving stuff into View classes and reducing clutter. Took a pass at keeping comments and helpfuls, but unclear what the 'final' UI will be (I'm just designing as I use the product). Test Plan: Review a number of questions and answers. {F702495} Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Maniphest Tasks: T9099 Differential Revision: https://secure.phabricator.com/D13872 --- resources/celerity/map.php | 4 +- src/__phutil_library_map__.php | 5 +- .../PonderQuestionViewController.php | 194 ++++-------------- .../ponder/storage/PonderAnswer.php | 11 - .../ponder/view/PonderAnswerView.php | 172 ++++++++++++++++ .../ponder/view/PonderFooterView.php | 79 +++++++ .../css/application/ponder/ponder-view.css | 42 +++- 7 files changed, 332 insertions(+), 175 deletions(-) create mode 100644 src/applications/ponder/view/PonderAnswerView.php create mode 100644 src/applications/ponder/view/PonderFooterView.php diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 5c3fcbbeeb..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' => '4e557c89', + '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', @@ -811,7 +811,7 @@ return array( 'policy-css' => '957ea14c', 'policy-edit-css' => '815c66f7', 'policy-transaction-detail-css' => '82100a43', - 'ponder-view-css' => '4e557c89', + 'ponder-view-css' => '6a399881', 'project-icon-css' => '4e3eaa5a', 'raphael-core' => '51ee6b43', 'raphael-g' => '40dde778', diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 9e76723b92..268e934669 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3398,11 +3398,13 @@ phutil_register_library_map(array( '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', @@ -7569,7 +7571,6 @@ phutil_register_library_map(array( 'PhabricatorPolicyInterface', 'PhabricatorFlaggableInterface', 'PhabricatorSubscribableInterface', - 'PhabricatorTokenReceiverInterface', 'PhabricatorDestructibleInterface', ), 'PonderAnswerCommentController' => 'PonderController', @@ -7583,11 +7584,13 @@ phutil_register_library_map(array( '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( diff --git a/src/applications/ponder/controller/PonderQuestionViewController.php b/src/applications/ponder/controller/PonderQuestionViewController.php index 10236d775e..a974716540 100644 --- a/src/applications/ponder/controller/PonderQuestionViewController.php +++ b/src/applications/ponder/controller/PonderQuestionViewController.php @@ -16,7 +16,6 @@ final class PonderQuestionViewController extends PonderController { return new Aphront404Response(); } - $question_xactions = $this->buildQuestionTransactions($question); $answers = $this->buildAnswers($question->getAnswers()); $authors = mpull($question->getAnswers(), null, 'getAuthorPHID'); @@ -54,9 +53,40 @@ final class PonderQuestionViewController extends PonderController { $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); @@ -64,7 +94,7 @@ final class PonderQuestionViewController extends PonderController { $ponder_view = id(new PHUITwoColumnView()) ->setMainColumn(array( $object_box, - $question_xactions, + $comment_view, $answers, $answer_add_panel, )) @@ -170,32 +200,6 @@ final class PonderQuestionViewController extends PonderController { 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. @@ -206,8 +210,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)) @@ -227,143 +229,19 @@ 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()); - - $user_marked = $answer->getUserVote(); - $can_vote = $viewer->isLoggedIn(); - - if ($user_marked) { - $helpful_uri = "/answer/helpful/remove/{$id}/"; - $helpful_icon = 'fa-times'; - $helpful_text = pht('Remove Helpful'); - } else { - $helpful_uri = "/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($this->getApplicationURI($helpful_uri)) - ->setRenderAsForm(true) - ->setDisabled(!$can_vote) - ->setWorkflow($can_vote)); - - $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) { - - $viewer = $this->getViewer(); - $view = id(new PHUIPropertyListView()) - ->setUser($viewer) - ->setObject($answer) - ->setActionList($actions); - - $view->addProperty( - pht('Created'), - phabricator_datetime($answer->getDateCreated(), $viewer)); - - $view->addProperty( - pht('Helpfuls'), - $answer->getVoteCount()); - - $view->invokeWillRenderEvent(); - - $view->addSectionHeader(pht('Answer')); - $view->addTextContent( - array( - phutil_tag( - 'div', - array( - 'class' => 'phabricator-remarkup', - ), - PhabricatorMarkupEngine::renderOneObject( - $answer, - $answer->getMarkupField(), - $viewer)), - )); - return $view; } diff --git a/src/applications/ponder/storage/PonderAnswer.php b/src/applications/ponder/storage/PonderAnswer.php index 937ae75411..42ba5f3816 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'; @@ -234,16 +233,6 @@ final class PonderAnswer extends PonderDAO } -/* -( PhabricatorTokenReceiverInterface )---------------------------------- */ - - - public function getUsersToNotifyOfTokenGiven() { - return array( - $this->getAuthorPHID(), - ); - } - - /* -( PhabricatorSubscribableInterface )----------------------------------- */ diff --git a/src/applications/ponder/view/PonderAnswerView.php b/src/applications/ponder/view/PonderAnswerView.php new file mode 100644 index 0000000000..435a613828 --- /dev/null +++ b/src/applications/ponder/view/PonderAnswerView.php @@ -0,0 +1,172 @@ +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(); + $author_phid = $answer->getAuthorPHID(); + $actions = $this->buildAnswerActions(); + + $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)); + + $id = $answer->getID(); + $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..7e553b3f4b --- /dev/null +++ b/src/applications/ponder/view/PonderFooterView.php @@ -0,0 +1,79 @@ +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() { + 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/webroot/rsrc/css/application/ponder/ponder-view.css b/webroot/rsrc/css/application/ponder/ponder-view.css index 4cf9509281..867009c4bb 100644 --- a/webroot/rsrc/css/application/ponder/ponder-view.css +++ b/webroot/rsrc/css/application/ponder/ponder-view.css @@ -14,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); } From f18537ce0b1bfd36dcb022bace9f26aa1744bfb9 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Wed, 12 Aug 2015 07:56:10 -0700 Subject: [PATCH 28/39] Change Ponder default query to 'Recent' Summary: In Ponder, I think the right default query is "good" questions. That being recently asked, and either open or resolved. For browsing this seems correct. For moderators, I presume they use saved queries accordingly. This is similar to stack overflow and quora. Test Plan: Review queries in my dev box, all seems to run correctly Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D13877 --- .../ponder/query/PonderQuestionSearchEngine.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/applications/ponder/query/PonderQuestionSearchEngine.php b/src/applications/ponder/query/PonderQuestionSearchEngine.php index b444ff8be9..b4cb7aef21 100644 --- a/src/applications/ponder/query/PonderQuestionSearchEngine.php +++ b/src/applications/ponder/query/PonderQuestionSearchEngine.php @@ -57,6 +57,7 @@ final class PonderQuestionSearchEngine protected function getBuiltinQueryNames() { $names = array( + 'recent' => pht('Recent Questions'), 'open' => pht('Open Questions'), 'resolved' => pht('Resolved Questions'), 'all' => pht('All Questions'), @@ -80,6 +81,12 @@ final class PonderQuestionSearchEngine case '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)); From 9a7beadd22f0356e56cdd86d5c7e5bcf525b6e6d Mon Sep 17 00:00:00 2001 From: Chad Little Date: Wed, 12 Aug 2015 07:56:31 -0700 Subject: [PATCH 29/39] Clean up some Ponder language Summary: Ref T9099. - Remove unused function - Fix celerity resource include - Add a note when an answer's been closed - Clean up architecture a little - Add a better notice when you've already answered. Test Plan: Test with and without answers, with answering, with being closed. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Maniphest Tasks: T9099 Differential Revision: https://secure.phabricator.com/D13875 --- .../ponder/constants/PonderQuestionStatus.php | 2 +- .../PonderQuestionViewController.php | 62 ++----------------- .../ponder/view/PonderAddAnswerView.php | 30 ++++++++- .../ponder/view/PonderFooterView.php | 1 + 4 files changed, 35 insertions(+), 60 deletions(-) diff --git a/src/applications/ponder/constants/PonderQuestionStatus.php b/src/applications/ponder/constants/PonderQuestionStatus.php index f37a208bcc..c401d52234 100644 --- a/src/applications/ponder/constants/PonderQuestionStatus.php +++ b/src/applications/ponder/constants/PonderQuestionStatus.php @@ -41,7 +41,7 @@ final class PonderQuestionStatus extends PonderConstants { self::STATUS_OPEN => pht('This question is open for answers.'), self::STATUS_CLOSED_RESOLVED => - pht('This question has been 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 => diff --git a/src/applications/ponder/controller/PonderQuestionViewController.php b/src/applications/ponder/controller/PonderQuestionViewController.php index a974716540..2f0ae5099f 100644 --- a/src/applications/ponder/controller/PonderQuestionViewController.php +++ b/src/applications/ponder/controller/PonderQuestionViewController.php @@ -18,21 +18,10 @@ final class PonderQuestionViewController extends PonderController { $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()); @@ -245,49 +234,6 @@ final class PonderQuestionViewController extends PonderController { return $view; } - private function wrapComments($n, $stuff) { - if ($n == 0) { - $text = pht('Add a Comment'); - } else { - $text = pht('Show %s Comments', new PhutilNumber($n)); - } - - $show_id = celerity_generate_unique_node_id(); - $hide_id = celerity_generate_unique_node_id(); - - 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); - } - private function buildSidebar(PonderQuestion $question) { $viewer = $this->getViewer(); $status = $question->getStatus(); 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/PonderFooterView.php b/src/applications/ponder/view/PonderFooterView.php index 7e553b3f4b..8f55b86aeb 100644 --- a/src/applications/ponder/view/PonderFooterView.php +++ b/src/applications/ponder/view/PonderFooterView.php @@ -28,6 +28,7 @@ final class PonderFooterView extends AphrontTagView { } protected function getTagContent() { + require_celerity_resource('ponder-view-css'); Javelin::initBehavior('phabricator-reveal-content'); $hide_action_id = celerity_generate_unique_node_id(); From 8c06d89070b9dccba74d0c37e7a1f0492bd381d3 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 12 Aug 2015 12:27:31 -0700 Subject: [PATCH 30/39] Flesh out web UI for mail a bit to prepare for Herald outbound rules Summary: Ref T9141. Ref T5791. Ref T7013. Major changes here is: - Currently, we don't store the headers we actually sent, or the reasons we actually did or did not deliver a mail. - Start storing these (as `headers.sent` and `actors.sent`). - Show them in the web UI. - Show them in `bin/mail show-outbound` (previously, we sort of re-computed them in a hacky way). - Take them into account in `bin/mail volume`. Then some minor changes: - Show mail bodies. - Show more mail information. - Start renaming "MetaMTA" to "Mail", at least in the web UI. Test Plan: {F707501} {F707502} {F707503} {F707504} {F707505} Reviewers: chad Reviewed By: chad Maniphest Tasks: T5791, T7013, T9141 Differential Revision: https://secure.phabricator.com/D13878 --- .../PhabricatorMetaMTAApplication.php | 12 +- .../PhabricatorMetaMTAMailViewController.php | 236 +++++++++++++++--- ...atorMailManagementShowOutboundWorkflow.php | 73 +++--- ...habricatorMailManagementVolumeWorkflow.php | 35 ++- .../metamta/query/PhabricatorMetaMTAActor.php | 35 +++ .../storage/PhabricatorMetaMTAMail.php | 119 +++++---- 6 files changed, 390 insertions(+), 120 deletions(-) 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/controller/PhabricatorMetaMTAMailViewController.php b/src/applications/metamta/controller/PhabricatorMetaMTAMailViewController.php index 938e6f656c..fd9d5a221e 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,51 @@ final class PhabricatorMetaMTAMailViewController } else { $title = $mail->getSubject(); } + $header = id(new PHUIHeaderView()) ->setHeader($title) - ->setUser($this->getRequest()->getUser()) + ->setUser($viewer) ->setPolicyObject($mail); + switch ($mail->getStatus()) { + case PhabricatorMetaMTAMail::STATUS_QUEUE: + $icon = 'fa-clock-o'; + $color = 'blue'; + $name = pht('Queued'); + break; + case PhabricatorMetaMTAMail::STATUS_SENT: + $icon = 'fa-envelope'; + $color = 'green'; + $name = pht('Sent'); + break; + case PhabricatorMetaMTAMail::STATUS_FAIL: + $icon = 'fa-envelope'; + $color = 'red'; + $name = pht('Delivery Failed'); + break; + case PhabricatorMetaMTAMail::STATUS_VOID: + $icon = 'fa-envelope'; + $color = 'black'; + $name = pht('Voided'); + break; + default: + $icon = 'fa-question-circle'; + $color = 'yellow'; + $name = pht('Unknown'); + break; + } + + $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 +76,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 +110,167 @@ 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); + + $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/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/storage/PhabricatorMetaMTAMail.php b/src/applications/metamta/storage/PhabricatorMetaMTAMail.php index ee09fa730f..38aae37e02 100644 --- a/src/applications/metamta/storage/PhabricatorMetaMTAMail.php +++ b/src/applications/metamta/storage/PhabricatorMetaMTAMail.php @@ -436,6 +436,8 @@ final class PhabricatorMetaMTAMail } try { + $headers = $this->generateHeaders(); + $params = $this->parameters; $actors = $this->loadAllActors(); @@ -535,16 +537,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 +585,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 +595,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 +607,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,6 +642,25 @@ 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->setMessage( @@ -692,24 +693,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:". @@ -1052,6 +1035,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 )----------------------------------------- */ From 3db9e4b4e5a20759777e66fa1069650fa4d759b4 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Thu, 13 Aug 2015 13:00:47 -0700 Subject: [PATCH 31/39] Add mailtags to Ponder Summary: Ref T3578 Adds mailtags to Ponder, answer stuff not quite ready, but that's another diff. Test Plan: set preferences to notify, second account updates a question, get notification on first. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Maniphest Tasks: T3578 Differential Revision: https://secure.phabricator.com/D13886 --- .../ponder/editor/PonderQuestionEditor.php | 13 +++++++++ .../storage/PonderQuestionTransaction.php | 27 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/src/applications/ponder/editor/PonderQuestionEditor.php b/src/applications/ponder/editor/PonderQuestionEditor.php index d9f77fad94..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); diff --git a/src/applications/ponder/storage/PonderQuestionTransaction.php b/src/applications/ponder/storage/PonderQuestionTransaction.php index 80cb9f83f0..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'; } @@ -105,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(); From 7938d9a5296007ee935ba1307f5d985a409ea223 Mon Sep 17 00:00:00 2001 From: Joshua Spence Date: Fri, 14 Aug 2015 07:38:32 +1000 Subject: [PATCH 32/39] Add some missing translation strings Summary: Self explanatory. Test Plan: N/A Reviewers: epriestley, #blessed_reviewers Reviewed By: epriestley, #blessed_reviewers Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D13880 --- .../translation/PhabricatorUSEnglishTranslation.php | 8 ++++++++ 1 file changed, 8 insertions(+) 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.', From 368f3591142ed923bcf2a8bf1a48b746794f2f02 Mon Sep 17 00:00:00 2001 From: Joshua Spence Date: Fri, 14 Aug 2015 07:49:00 +1000 Subject: [PATCH 33/39] Use PhutilClassMapQuery instead of PhutilSymbolLoader Summary: Use `PhutilClassMaQuery` instead of `PhutilSymbolLoader`, mostly for consistency. Depends on D13588. Test Plan: Poked around a bunch of pages. Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: epriestley, Korvin Differential Revision: https://secure.phabricator.com/D13589 --- scripts/almanac/manage_almanac.php | 4 +- scripts/cache/manage_cache.php | 4 +- scripts/daemon/manage_daemons.php | 4 +- scripts/diviner/diviner.php | 4 +- scripts/drydock/drydock_control.php | 4 +- scripts/fact/manage_facts.php | 4 +- scripts/files/manage_files.php | 4 +- scripts/lipsum/manage_lipsum.php | 4 +- scripts/mail/manage_mail.php | 4 +- scripts/repository/manage_repositories.php | 4 +- scripts/search/manage_search.php | 4 +- scripts/setup/manage_audit.php | 4 +- scripts/setup/manage_auth.php | 4 +- scripts/setup/manage_celerity.php | 4 +- scripts/setup/manage_config.php | 4 +- scripts/setup/manage_feed.php | 4 +- scripts/setup/manage_harbormaster.php | 4 +- scripts/setup/manage_hunks.php | 4 +- scripts/setup/manage_i18n.php | 4 +- scripts/setup/manage_phortune.php | 4 +- scripts/setup/manage_policy.php | 4 +- scripts/setup/manage_remove.php | 4 +- scripts/setup/manage_trigger.php | 4 +- scripts/setup/manage_worker.php | 4 +- scripts/sms/manage_sms.php | 4 +- scripts/sql/manage_storage.php | 4 +- scripts/ssh/ssh-exec.php | 9 ++--- src/__tests__/PhabricatorConduitTestCase.php | 4 +- .../conduit/method/ConduitAPIMethod.php | 32 ++-------------- .../method/ConduitQueryConduitAPIMethod.php | 5 +-- .../query/PhabricatorConduitMethodQuery.php | 12 ++---- .../PhabricatorConfigCoreSchemaSpec.php | 4 +- .../schema/PhabricatorConfigSchemaQuery.php | 4 +- .../console/core/DarkConsoleCore.php | 12 ++---- .../console/plugin/DarkConsolePlugin.php | 4 -- .../PhabricatorDaemonManagementWorkflow.php | 3 +- ...boardPanelSearchApplicationCustomField.php | 8 ++-- ...orDashboardPanelSearchQueryCustomField.php | 7 ++-- ...erentialLandingActionMenuEventListener.php | 5 ++- .../controller/DiffusionSymbolController.php | 5 ++- .../workflow/DivinerGenerateWorkflow.php | 5 +-- .../engine/DoorkeeperImportEngine.php | 8 ++-- .../option/PhabricatorAsanaConfigOptions.php | 4 +- .../worker/DoorkeeperFeedWorker.php | 4 +- .../feed/worker/FeedPublisherWorker.php | 1 + .../files/storage/PhabricatorFile.php | 13 +------ .../query/PhabricatorFlagSearchEngine.php | 6 ++- .../storage/build/HarbormasterBuild.php | 4 +- .../PhabricatorLipsumGenerateWorkflow.php | 4 +- ...catorMailManagementReceiveTestWorkflow.php | 8 ++-- .../PhabricatorMetaMTAReceivedMail.php | 8 ++-- .../__tests__/PhabricatorPolicyTestCase.php | 4 +- .../policy/config/PolicyLockOptionType.php | 6 +-- .../PhabricatorPolicyEditController.php | 4 +- .../policy/query/PhabricatorPolicyQuery.php | 4 +- .../search/index/PhabricatorSearchIndexer.php | 4 +- ...abricatorSearchManagementIndexWorkflow.php | 4 +- ...abricatorSearchApplicationSearchEngine.php | 4 +- .../PhabricatorSettingsMainController.php | 16 ++------ ...abricatorEmailPreferencesSettingsPanel.php | 10 ++--- ...licationTransactionShowOlderController.php | 4 +- ...licationTransactionTransactionPHIDType.php | 4 +- ...torApplicationTransactionPublishWorker.php | 4 +- ...ricatorTypeaheadFunctionHelpController.php | 5 ++- ...orTypeaheadModularDatasourceController.php | 5 ++- .../PhabricatorUIExampleRenderController.php | 6 +-- .../PhabricatorStandardCustomField.php | 6 +-- .../workers/PhabricatorTriggerDaemon.php | 4 +- src/infrastructure/env/PhabricatorEnv.php | 7 ++-- .../events/PhabricatorEventEngine.php | 4 +- .../markup/PhabricatorMarkupEngine.php | 8 ++-- .../storage/patch/PhabricatorSQLPatchList.php | 37 ++++++------------- support/aphlict/server/aphlict_launcher.php | 4 +- 73 files changed, 180 insertions(+), 257 deletions(-) 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..a9448cbbb3 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); 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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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); From d22930c0fd6dae2a2d847a8dd3b6ecb15e1cddc0 Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 13 Aug 2015 18:04:08 -0700 Subject: [PATCH 34/39] Fix variable name in ssh-exec.php Summary: Fixes T9176. This was renamed in D13589 but a use site was missed. Test Plan: Before: ``` epriestley@orbital ~/dev/phabricator $ SSH_ORIGINAL_COMMAND=conduit sudo -E ./bin/ssh-exec --phabricator-ssh-user admin phabricator-ssh-exec: Invalid command. ``` After: ``` epriestley@orbital ~/dev/phabricator $ SSH_ORIGINAL_COMMAND=conduit sudo -E ./bin/ssh-exec --phabricator-ssh-user admin phabricator-ssh-exec: No Conduit method provided. ``` Reviewers: chad, joshuaspence Reviewed By: chad, joshuaspence Maniphest Tasks: T9176 Differential Revision: https://secure.phabricator.com/D13893 --- scripts/ssh/ssh-exec.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ssh/ssh-exec.php b/scripts/ssh/ssh-exec.php index a9448cbbb3..45dacddebd 100755 --- a/scripts/ssh/ssh-exec.php +++ b/scripts/ssh/ssh-exec.php @@ -230,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.')); } From 489c7ce048d40dbdc57f088419916032251548d0 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Thu, 13 Aug 2015 18:23:03 -0700 Subject: [PATCH 35/39] Remove unused mailtag in badges Summary: grep for MAILTAG_NAME in badges, not used. Test Plan: view edit preferences, grep Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D13887 --- src/applications/badges/storage/PhabricatorBadgesTransaction.php | 1 - 1 file changed, 1 deletion(-) 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'; From 9e306710b3595b11b149562a8aa9b3dab3b6ebd7 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Thu, 13 Aug 2015 18:23:33 -0700 Subject: [PATCH 36/39] Clean up misc Ponder details Summary: Ref T3578, Changes "Ask Away" to just "Submit", Changes Description to Details, check for is_new when offering closed state on question edit. Test Plan: New Question, Edit Question Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Maniphest Tasks: T3578 Differential Revision: https://secure.phabricator.com/D13891 --- .../PonderQuestionEditController.php | 22 ++++++++++++------- .../PonderQuestionViewController.php | 5 ++++- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/applications/ponder/controller/PonderQuestionEditController.php b/src/applications/ponder/controller/PonderQuestionEditController.php index 293bbe3c79..71b9615cca 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(); } @@ -124,13 +126,17 @@ final class PonderQuestionEditController extends PonderController { ->setSpacePHID($v_space) ->setPolicies($policies) ->setValue($v_view) - ->setCapability(PhabricatorPolicyCapability::CAN_VIEW)) - ->appendChild( - id(new AphrontFormSelectControl()) - ->setLabel(pht('Status')) - ->setName('status') - ->setValue($v_status) - ->setOptions(PonderQuestionStatus::getQuestionStatusMap())); + ->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,7 +148,7 @@ 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')) diff --git a/src/applications/ponder/controller/PonderQuestionViewController.php b/src/applications/ponder/controller/PonderQuestionViewController.php index 2f0ae5099f..a1e526648d 100644 --- a/src/applications/ponder/controller/PonderQuestionViewController.php +++ b/src/applications/ponder/controller/PonderQuestionViewController.php @@ -171,7 +171,10 @@ final class PonderQuestionViewController extends PonderController { $view->invokeWillRenderEvent(); - $view->addSectionHeader(pht('Question')); + $view->addSectionHeader( + pht('Details'), + PHUIPropertyListView::ICON_SUMMARY); + $view->addTextContent( array( phutil_tag( From 3b987a93ce15592b68c19cb50dce1f3718844ec8 Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 14 Aug 2015 04:31:42 -0700 Subject: [PATCH 37/39] Consolidate outbound mail status in a new class Summary: Ref T5791. This collects outbound mail status in one place and makes the list view a little spiffier. Test Plan: Looked at list and detail views. Grepped for changed classes/constants. Reviewers: chad Reviewed By: chad Maniphest Tasks: T5791 Differential Revision: https://secure.phabricator.com/D13884 --- src/__phutil_library_map__.php | 6 +-- .../metamta/PhabricatorMetaMTAWorker.php | 2 +- .../metamta/constants/MetaMTAConstants.php | 3 -- .../constants/MetaMTAReceivedMailStatus.php | 2 +- .../PhabricatorMailOutboundStatus.php | 44 +++++++++++++++++++ .../PhabricatorMetaMTAMailViewController.php | 34 +++----------- ...atorMailManagementListOutboundWorkflow.php | 2 +- ...habricatorMailManagementResendWorkflow.php | 2 +- .../PhabricatorMetaMTAMailSearchEngine.php | 13 ++++-- .../storage/PhabricatorMetaMTAMail.php | 32 ++++---------- .../PhabricatorMetaMTAMailTestCase.php | 6 +-- 11 files changed, 78 insertions(+), 68 deletions(-) delete mode 100644 src/applications/metamta/constants/MetaMTAConstants.php create mode 100644 src/applications/metamta/constants/PhabricatorMailOutboundStatus.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 268e934669..ef35408192 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1248,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', @@ -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', @@ -5012,12 +5012,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', @@ -6182,6 +6181,7 @@ phutil_register_library_map(array( 'PhabricatorMailManagementShowOutboundWorkflow' => 'PhabricatorMailManagementWorkflow', 'PhabricatorMailManagementVolumeWorkflow' => 'PhabricatorMailManagementWorkflow', 'PhabricatorMailManagementWorkflow' => 'PhabricatorManagementWorkflow', + 'PhabricatorMailOutboundStatus' => 'Phobject', 'PhabricatorMailReceiver' => 'Phobject', 'PhabricatorMailReceiverTestCase' => 'PhabricatorTestCase', 'PhabricatorMailReplyHandler' => 'Phobject', 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/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 fd9d5a221e..faaa08e472 100644 --- a/src/applications/metamta/controller/PhabricatorMetaMTAMailViewController.php +++ b/src/applications/metamta/controller/PhabricatorMetaMTAMailViewController.php @@ -25,34 +25,10 @@ final class PhabricatorMetaMTAMailViewController ->setUser($viewer) ->setPolicyObject($mail); - switch ($mail->getStatus()) { - case PhabricatorMetaMTAMail::STATUS_QUEUE: - $icon = 'fa-clock-o'; - $color = 'blue'; - $name = pht('Queued'); - break; - case PhabricatorMetaMTAMail::STATUS_SENT: - $icon = 'fa-envelope'; - $color = 'green'; - $name = pht('Sent'); - break; - case PhabricatorMetaMTAMail::STATUS_FAIL: - $icon = 'fa-envelope'; - $color = 'red'; - $name = pht('Delivery Failed'); - break; - case PhabricatorMetaMTAMail::STATUS_VOID: - $icon = 'fa-envelope'; - $color = 'black'; - $name = pht('Voided'); - break; - default: - $icon = 'fa-question-circle'; - $color = 'yellow'; - $name = pht('Unknown'); - break; - } - + $status = $mail->getStatus(); + $name = PhabricatorMailOutboundStatus::getStatusName($status); + $icon = PhabricatorMailOutboundStatus::getStatusIcon($status); + $color = PhabricatorMailOutboundStatus::getStatusColor($status); $header->setStatus($icon, $color, $name); $crumbs = $this->buildApplicationCrumbs() @@ -249,6 +225,8 @@ final class PhabricatorMetaMTAMailViewController $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')); 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/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/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 38aae37e02..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,7 +425,7 @@ 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!')); } } @@ -662,7 +657,7 @@ final class PhabricatorMetaMTAMail $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, '. @@ -673,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 '. @@ -684,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` '. @@ -716,7 +711,7 @@ final class PhabricatorMetaMTAMail } } catch (Exception $ex) { $this - ->setStatus(self::STATUS_FAIL) + ->setStatus(PhabricatorMailOutboundStatus::STATUS_FAIL) ->setMessage($ex->getMessage()) ->save(); @@ -732,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(); @@ -752,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' 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()); } From 603c91e08a1f651968a3c07cf323aaac6ced7a1c Mon Sep 17 00:00:00 2001 From: Chad Little Date: Fri, 14 Aug 2015 09:25:02 -0700 Subject: [PATCH 38/39] Add ability to hide answers in Ponder Summary: Ref T9173, adds basic hide support for answers. Answer authors and Moderators can hide answers, unhide them. Test Plan: Hide answer, log into other account, see hidden message. Mark as visible, see answer again. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Maniphest Tasks: T9173 Differential Revision: https://secure.phabricator.com/D13894 --- .../autopatches/20150812.ponder.answer.1.sql | 2 ++ .../autopatches/20150812.ponder.answer.2.sql | 2 ++ src/__phutil_library_map__.php | 2 ++ .../ponder/constants/PonderAnswerStatus.php | 24 +++++++++++++++ .../ponder/constants/PonderQuestionStatus.php | 18 +++++++++++ .../controller/PonderAnswerEditController.php | 14 ++++++++- .../ponder/editor/PonderAnswerEditor.php | 6 ++++ .../ponder/storage/PonderAnswer.php | 10 +++++-- .../storage/PonderAnswerTransaction.php | 28 +++++++++++++++++ .../ponder/view/PonderAnswerView.php | 30 ++++++++++++++++++- 10 files changed, 132 insertions(+), 4 deletions(-) create mode 100644 resources/sql/autopatches/20150812.ponder.answer.1.sql create mode 100644 resources/sql/autopatches/20150812.ponder.answer.2.sql create mode 100644 src/applications/ponder/constants/PonderAnswerStatus.php 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/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index ef35408192..3bb3ad8c5b 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3395,6 +3395,7 @@ 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', @@ -7581,6 +7582,7 @@ phutil_register_library_map(array( 'PonderAnswerPHIDType' => 'PhabricatorPHIDType', 'PonderAnswerQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PonderAnswerSaveController' => 'PonderController', + 'PonderAnswerStatus' => 'PonderConstants', 'PonderAnswerTransaction' => 'PhabricatorApplicationTransaction', 'PonderAnswerTransactionComment' => 'PhabricatorApplicationTransactionComment', 'PonderAnswerTransactionQuery' => 'PhabricatorApplicationTransactionQuery', diff --git a/src/applications/ponder/constants/PonderAnswerStatus.php b/src/applications/ponder/constants/PonderAnswerStatus.php new file mode 100644 index 0000000000..f0f30f30a7 --- /dev/null +++ b/src/applications/ponder/constants/PonderAnswerStatus.php @@ -0,0 +1,24 @@ + 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 c401d52234..917539f81b 100644 --- a/src/applications/ponder/constants/PonderQuestionStatus.php +++ b/src/applications/ponder/constants/PonderQuestionStatus.php @@ -7,6 +7,9 @@ final class PonderQuestionStatus extends PonderConstants { const STATUS_CLOSED_OBSOLETE = 'obsolete'; const STATUS_CLOSED_DUPLICATE = 'duplicate'; + const ANSWER_STATUS_VISIBLE = 'visible'; + const ANSWER_STATUS_HIDDEN = 'hidden'; + public static function getQuestionStatusMap() { return array( self::STATUS_OPEN => pht('Open'), @@ -86,5 +89,20 @@ final class PonderQuestionStatus extends PonderConstants { ); } + public static function getAnswerStatusMap() { + return array( + self::ANSWER_STATUS_VISIBLE => 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/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/editor/PonderAnswerEditor.php b/src/applications/ponder/editor/PonderAnswerEditor.php index 9eea95c511..b9e3c42869 100644 --- a/src/applications/ponder/editor/PonderAnswerEditor.php +++ b/src/applications/ponder/editor/PonderAnswerEditor.php @@ -12,6 +12,7 @@ final class PonderAnswerEditor extends PonderEditor { $types[] = PhabricatorTransactions::TYPE_COMMENT; $types[] = PonderAnswerTransaction::TYPE_CONTENT; + $types[] = PonderAnswerTransaction::TYPE_STATUS; $types[] = PonderAnswerTransaction::TYPE_QUESTION_ID; return $types; @@ -23,6 +24,7 @@ final class PonderAnswerEditor extends PonderEditor { switch ($xaction->getTransactionType()) { case PonderAnswerTransaction::TYPE_CONTENT: + case PonderAnswerTransaction::TYPE_STATUS: return $object->getContent(); case PonderAnswerTransaction::TYPE_QUESTION_ID: return $object->getQuestionID(); @@ -35,6 +37,7 @@ final class PonderAnswerEditor extends PonderEditor { switch ($xaction->getTransactionType()) { case PonderAnswerTransaction::TYPE_CONTENT: + case PonderAnswerTransaction::TYPE_STATUS: case PonderAnswerTransaction::TYPE_QUESTION_ID: return $xaction->getNewValue(); } @@ -48,6 +51,9 @@ 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/storage/PonderAnswer.php b/src/applications/ponder/storage/PonderAnswer.php index 42ba5f3816..2c8aff23da 100644 --- a/src/applications/ponder/storage/PonderAnswer.php +++ b/src/applications/ponder/storage/PonderAnswer.php @@ -17,8 +17,9 @@ final class PonderAnswer extends PonderDAO protected $content; protected $mailKey; - + protected $status; protected $voteCount; + private $vote; private $question = self::ATTACHABLE; private $comments; @@ -35,7 +36,8 @@ final class PonderAnswer extends PonderDAO ->setQuestionID(0) ->setContent('') ->setAuthorPHID($actor->getPHID()) - ->setVoteCount(0); + ->setVoteCount(0) + ->setStatus(PonderAnswerStatus::ANSWER_STATUS_VISIBLE); } @@ -84,6 +86,7 @@ final class PonderAnswer extends PonderDAO self::CONFIG_COLUMN_SCHEMA => array( 'voteCount' => 'sint32', 'content' => 'text', + 'status' => 'text32', 'mailKey' => 'bytes20', ), self::CONFIG_KEY_SCHEMA => array( @@ -102,6 +105,9 @@ final class PonderAnswer extends PonderDAO 'authorPHID' => array( 'columns' => array('authorPHID'), ), + 'status' => array( + 'columns' => array('status'), + ), ), ) + parent::getConfiguration(); } diff --git a/src/applications/ponder/storage/PonderAnswerTransaction.php b/src/applications/ponder/storage/PonderAnswerTransaction.php index 07b9768c82..c13cb07b87 100644 --- a/src/applications/ponder/storage/PonderAnswerTransaction.php +++ b/src/applications/ponder/storage/PonderAnswerTransaction.php @@ -4,6 +4,7 @@ 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() { @@ -27,6 +28,7 @@ final class PonderAnswerTransaction switch ($this->getTransactionType()) { case self::TYPE_CONTENT: + case self::TYPE_STATUS: $phids[] = $this->getObjectPHID(); break; } @@ -75,6 +77,19 @@ final class PonderAnswerTransaction $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(); @@ -101,6 +116,19 @@ final class PonderAnswerTransaction $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/view/PonderAnswerView.php b/src/applications/ponder/view/PonderAnswerView.php index 435a613828..532bc9f2cd 100644 --- a/src/applications/ponder/view/PonderAnswerView.php +++ b/src/applications/ponder/view/PonderAnswerView.php @@ -31,8 +31,37 @@ final class PonderAnswerView extends AphrontTagView { 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') @@ -57,7 +86,6 @@ final class PonderAnswerView extends AphrontTagView { $answer->getMarkupField(), $viewer)); - $id = $answer->getID(); $anchor = id(new PhabricatorAnchorView()) ->setAnchorName("A$id"); From 3a6c3cc5ca9ea6fbb997246fdf46748491a7f0d4 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Fri, 14 Aug 2015 09:51:52 -0700 Subject: [PATCH 39/39] Fix new Question in Ponder Summary: I derped here and broke new questions, also remove old Answer constants Test Plan: Ask a new question, see it save Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D13899 --- .../ponder/constants/PonderQuestionStatus.php | 19 ------------ .../PonderQuestionEditController.php | 8 +++-- .../PonderQuestionViewController.php | 31 ++++++++++--------- 3 files changed, 22 insertions(+), 36 deletions(-) diff --git a/src/applications/ponder/constants/PonderQuestionStatus.php b/src/applications/ponder/constants/PonderQuestionStatus.php index 917539f81b..61a446353e 100644 --- a/src/applications/ponder/constants/PonderQuestionStatus.php +++ b/src/applications/ponder/constants/PonderQuestionStatus.php @@ -7,9 +7,6 @@ final class PonderQuestionStatus extends PonderConstants { const STATUS_CLOSED_OBSOLETE = 'obsolete'; const STATUS_CLOSED_DUPLICATE = 'duplicate'; - const ANSWER_STATUS_VISIBLE = 'visible'; - const ANSWER_STATUS_HIDDEN = 'hidden'; - public static function getQuestionStatusMap() { return array( self::STATUS_OPEN => pht('Open'), @@ -89,20 +86,4 @@ final class PonderQuestionStatus extends PonderConstants { ); } - public static function getAnswerStatusMap() { - return array( - self::ANSWER_STATUS_VISIBLE => 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/controller/PonderQuestionEditController.php b/src/applications/ponder/controller/PonderQuestionEditController.php index 71b9615cca..41123d6049 100644 --- a/src/applications/ponder/controller/PonderQuestionEditController.php +++ b/src/applications/ponder/controller/PonderQuestionEditController.php @@ -68,9 +68,11 @@ final class PonderQuestionEditController extends PonderController { ->setTransactionType(PonderQuestionTransaction::TYPE_CONTENT) ->setNewValue($v_content); - $xactions[] = id(clone $template) - ->setTransactionType(PonderQuestionTransaction::TYPE_STATUS) - ->setNewValue($v_status); + if (!$is_new) { + $xactions[] = id(clone $template) + ->setTransactionType(PonderQuestionTransaction::TYPE_STATUS) + ->setNewValue($v_status); + } $xactions[] = id(clone $template) ->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY) diff --git a/src/applications/ponder/controller/PonderQuestionViewController.php b/src/applications/ponder/controller/PonderQuestionViewController.php index a1e526648d..06f924510f 100644 --- a/src/applications/ponder/controller/PonderQuestionViewController.php +++ b/src/applications/ponder/controller/PonderQuestionViewController.php @@ -171,23 +171,26 @@ final class PonderQuestionViewController extends PonderController { $view->invokeWillRenderEvent(); - $view->addSectionHeader( + $details = PhabricatorMarkupEngine::renderOneObject( + $question, + $question->getMarkupField(), + $viewer); + + if ($details) { + $view->addSectionHeader( pht('Details'), PHUIPropertyListView::ICON_SUMMARY); - $view->addTextContent( - array( - phutil_tag( - 'div', - array( - 'class' => 'phabricator-remarkup', - ), - PhabricatorMarkupEngine::renderOneObject( - $question, - $question->getMarkupField(), - $viewer)), - )); - + $view->addTextContent( + array( + phutil_tag( + 'div', + array( + 'class' => 'phabricator-remarkup', + ), + $details), + )); + } return $view; }