From e3daf598fb58a5be9a13b45ff1b730e9670f0d47 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sat, 2 Apr 2016 14:24:40 +0000 Subject: [PATCH 01/84] Moderize Phragment Summary: Swap over to modern components, `newPage` and `handleRequest`. Test Plan: `arc lint` :( Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin, hach-que Differential Revision: https://secure.phabricator.com/D15571 --- .../controller/PhragmentBrowseController.php | 37 ++++++++-------- .../controller/PhragmentCreateController.php | 35 +++++++-------- .../controller/PhragmentHistoryController.php | 37 ++++++++-------- .../controller/PhragmentPatchController.php | 27 +++++------- .../controller/PhragmentPolicyController.php | 35 +++++++-------- .../controller/PhragmentRevertController.php | 26 ++++------- .../PhragmentSnapshotCreateController.php | 35 +++++++-------- .../PhragmentSnapshotDeleteController.php | 19 +++----- .../PhragmentSnapshotPromoteController.php | 43 ++++++++----------- .../PhragmentSnapshotViewController.php | 36 +++++++--------- .../controller/PhragmentUpdateController.php | 35 +++++++-------- .../controller/PhragmentVersionController.php | 40 ++++++++--------- .../controller/PhragmentZIPController.php | 37 ++++++++-------- 13 files changed, 191 insertions(+), 251 deletions(-) diff --git a/src/applications/phragment/controller/PhragmentBrowseController.php b/src/applications/phragment/controller/PhragmentBrowseController.php index 7b0a16cc52..d511835ce9 100644 --- a/src/applications/phragment/controller/PhragmentBrowseController.php +++ b/src/applications/phragment/controller/PhragmentBrowseController.php @@ -2,21 +2,15 @@ final class PhragmentBrowseController extends PhragmentController { - private $dblob; - public function shouldAllowPublic() { return true; } - public function willProcessRequest(array $data) { - $this->dblob = idx($data, 'dblob', ''); - } + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); + $dblob = $request->getURIData('dblob'); - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); - - $parents = $this->loadParentFragments($this->dblob); + $parents = $this->loadParentFragments($dblob); if ($parents === null) { return new Aphront404Response(); } @@ -83,16 +77,19 @@ final class PhragmentBrowseController extends PhragmentController { $list->addItem($item); } - return $this->buildApplicationPage( - array( - $crumbs, - $this->renderConfigurationWarningIfRequired(), - $current_box, - $list, - ), - array( - 'title' => pht('Browse Fragments'), - )); + $title = pht('Browse Fragments'); + + $view = array( + $this->renderConfigurationWarningIfRequired(), + $current_box, + $list, + ); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); + } } diff --git a/src/applications/phragment/controller/PhragmentCreateController.php b/src/applications/phragment/controller/PhragmentCreateController.php index 2a5b54538c..946d7cc332 100644 --- a/src/applications/phragment/controller/PhragmentCreateController.php +++ b/src/applications/phragment/controller/PhragmentCreateController.php @@ -2,18 +2,12 @@ final class PhragmentCreateController extends PhragmentController { - private $dblob; - - public function willProcessRequest(array $data) { - $this->dblob = idx($data, 'dblob', ''); - } - - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); + $dblob = $request->getURIData('dblob'); $parent = null; - $parents = $this->loadParentFragments($this->dblob); + $parents = $this->loadParentFragments($dblob); if ($parents === null) { return new Aphront404Response(); } @@ -124,15 +118,18 @@ final class PhragmentCreateController extends PhragmentController { $box->setInfoView($error_view); } - return $this->buildApplicationPage( - array( - $crumbs, - $this->renderConfigurationWarningIfRequired(), - $box, - ), - array( - 'title' => pht('Create Fragment'), - )); + $title = pht('Create Fragments'); + + $view = array( + $this->renderConfigurationWarningIfRequired(), + $box, + ); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); + } } diff --git a/src/applications/phragment/controller/PhragmentHistoryController.php b/src/applications/phragment/controller/PhragmentHistoryController.php index 72e0000b80..4e16deb43d 100644 --- a/src/applications/phragment/controller/PhragmentHistoryController.php +++ b/src/applications/phragment/controller/PhragmentHistoryController.php @@ -2,21 +2,15 @@ final class PhragmentHistoryController extends PhragmentController { - private $dblob; - public function shouldAllowPublic() { return true; } - public function willProcessRequest(array $data) { - $this->dblob = idx($data, 'dblob', ''); - } + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); + $dblob = $request->getURIData('dblob'); - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); - - $parents = $this->loadParentFragments($this->dblob); + $parents = $this->loadParentFragments($dblob); if ($parents === null) { return new Aphront404Response(); } @@ -97,16 +91,19 @@ final class PhragmentHistoryController extends PhragmentController { $first = false; } - return $this->buildApplicationPage( - array( - $crumbs, - $this->renderConfigurationWarningIfRequired(), - $current_box, - $list, - ), - array( - 'title' => pht('Fragment History'), - )); + $title = pht('Fragment History'); + + $view = array( + $this->renderConfigurationWarningIfRequired(), + $current_box, + $list, + ); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); + } } diff --git a/src/applications/phragment/controller/PhragmentPatchController.php b/src/applications/phragment/controller/PhragmentPatchController.php index 5a04403898..5ad4b6dfec 100644 --- a/src/applications/phragment/controller/PhragmentPatchController.php +++ b/src/applications/phragment/controller/PhragmentPatchController.php @@ -2,28 +2,21 @@ final class PhragmentPatchController extends PhragmentController { - private $aid; - private $bid; - public function shouldAllowPublic() { return true; } - public function willProcessRequest(array $data) { - $this->aid = idx($data, 'aid', 0); - $this->bid = idx($data, 'bid', 0); - } - - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); + $aid = $request->getURIData('aid'); + $bid = $request->getURIData('bid'); // If "aid" is "x", then it means the user wants to generate // a patch of an empty file to the version specified by "bid". - $ids = array($this->aid, $this->bid); - if ($this->aid === 'x') { - $ids = array($this->bid); + $ids = array($aid, $bid); + if ($aid === 'x') { + $ids = array($bid); } $versions = id(new PhragmentFragmentVersionQuery()) @@ -32,14 +25,14 @@ final class PhragmentPatchController extends PhragmentController { ->execute(); $version_a = null; - if ($this->aid !== 'x') { - $version_a = idx($versions, $this->aid, null); + if ($aid !== 'x') { + $version_a = idx($versions, $aid, null); if ($version_a === null) { return new Aphront404Response(); } } - $version_b = idx($versions, $this->bid, null); + $version_b = idx($versions, $bid, null); if ($version_b === null) { return new Aphront404Response(); } diff --git a/src/applications/phragment/controller/PhragmentPolicyController.php b/src/applications/phragment/controller/PhragmentPolicyController.php index 700c3edb5b..edcde80990 100644 --- a/src/applications/phragment/controller/PhragmentPolicyController.php +++ b/src/applications/phragment/controller/PhragmentPolicyController.php @@ -2,17 +2,11 @@ final class PhragmentPolicyController extends PhragmentController { - private $dblob; + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); + $dblob = $request->getURIData('dblob'); - public function willProcessRequest(array $data) { - $this->dblob = idx($data, 'dblob', ''); - } - - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); - - $parents = $this->loadParentFragments($this->dblob); + $parents = $this->loadParentFragments($dblob); if ($parents === null) { return new Aphront404Response(); } @@ -95,15 +89,18 @@ final class PhragmentPolicyController extends PhragmentController { ->setValidationException(null) ->setForm($form); - return $this->buildApplicationPage( - array( - $crumbs, - $this->renderConfigurationWarningIfRequired(), - $box, - ), - array( - 'title' => pht('Edit Fragment Policies'), - )); + $title = pht('Edit Fragment Policies'); + + $view = array( + $this->renderConfigurationWarningIfRequired(), + $box, + ); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); + } } diff --git a/src/applications/phragment/controller/PhragmentRevertController.php b/src/applications/phragment/controller/PhragmentRevertController.php index 92da1f27a3..e9d56eb112 100644 --- a/src/applications/phragment/controller/PhragmentRevertController.php +++ b/src/applications/phragment/controller/PhragmentRevertController.php @@ -2,21 +2,14 @@ final class PhragmentRevertController extends PhragmentController { - private $dblob; - private $id; - - public function willProcessRequest(array $data) { - $this->dblob = $data['dblob']; - $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'); + $dblob = $request->getURIData('dblob'); $fragment = id(new PhragmentFragmentQuery()) ->setViewer($viewer) - ->withPaths(array($this->dblob)) + ->withPaths(array($dblob)) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, @@ -30,7 +23,7 @@ final class PhragmentRevertController extends PhragmentController { $version = id(new PhragmentFragmentVersionQuery()) ->setViewer($viewer) ->withFragmentPHIDs(array($fragment->getPHID())) - ->withIDs(array($this->id)) + ->withIDs(array($id)) ->executeOne(); if ($version === null) { return new Aphront404Response(); @@ -58,7 +51,7 @@ final class PhragmentRevertController extends PhragmentController { } return id(new AphrontRedirectResponse()) - ->setURI($this->getApplicationURI('/history/'.$this->dblob)); + ->setURI($this->getApplicationURI('/history/'.$dblob)); } return $this->createDialog($fragment, $version); @@ -68,12 +61,11 @@ final class PhragmentRevertController extends PhragmentController { PhragmentFragment $fragment, PhragmentFragmentVersion $version) { - $request = $this->getRequest(); - $viewer = $request->getUser(); + $viewer = $this->getViewer(); $dialog = id(new AphrontDialogView()) ->setTitle(pht('Really revert this fragment?')) - ->setUser($request->getUser()) + ->setUser($this->getViewer()) ->addSubmitButton(pht('Revert')) ->addCancelButton(pht('Cancel')) ->appendParagraph(pht( diff --git a/src/applications/phragment/controller/PhragmentSnapshotCreateController.php b/src/applications/phragment/controller/PhragmentSnapshotCreateController.php index e135819280..c32be32cd7 100644 --- a/src/applications/phragment/controller/PhragmentSnapshotCreateController.php +++ b/src/applications/phragment/controller/PhragmentSnapshotCreateController.php @@ -2,17 +2,11 @@ final class PhragmentSnapshotCreateController extends PhragmentController { - private $dblob; + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); + $dblob = $request->getURIData('dblob'); - public function willProcessRequest(array $data) { - $this->dblob = idx($data, 'dblob', ''); - } - - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); - - $parents = $this->loadParentFragments($this->dblob); + $parents = $this->loadParentFragments($dblob); if ($parents === null) { return new Aphront404Response(); } @@ -159,15 +153,18 @@ final class PhragmentSnapshotCreateController extends PhragmentController { ->setFormErrors($errors) ->setForm($form); - return $this->buildApplicationPage( - array( - $crumbs, - $this->renderConfigurationWarningIfRequired(), - $box, - ), - array( - 'title' => pht('Create Fragment'), - )); + $title = pht('Create Snapshot'); + + $view = array( + $this->renderConfigurationWarningIfRequired(), + $box, + ); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); + } } diff --git a/src/applications/phragment/controller/PhragmentSnapshotDeleteController.php b/src/applications/phragment/controller/PhragmentSnapshotDeleteController.php index 715280a2c2..8f112585d0 100644 --- a/src/applications/phragment/controller/PhragmentSnapshotDeleteController.php +++ b/src/applications/phragment/controller/PhragmentSnapshotDeleteController.php @@ -2,15 +2,9 @@ final class PhragmentSnapshotDeleteController extends PhragmentController { - 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'); $snapshot = id(new PhragmentSnapshotQuery()) ->setViewer($viewer) @@ -18,7 +12,7 @@ final class PhragmentSnapshotDeleteController extends PhragmentController { PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )) - ->withIDs(array($this->id)) + ->withIDs(array($id)) ->executeOne(); if ($snapshot === null) { return new Aphront404Response(); @@ -37,12 +31,11 @@ final class PhragmentSnapshotDeleteController extends PhragmentController { } public function createDialog() { - $request = $this->getRequest(); - $viewer = $request->getUser(); + $viewer = $this->getViewer(); $dialog = id(new AphrontDialogView()) ->setTitle(pht('Really delete this snapshot?')) - ->setUser($request->getUser()) + ->setUser($this->getViewer()) ->addSubmitButton(pht('Delete')) ->addCancelButton(pht('Cancel')) ->appendParagraph(pht( diff --git a/src/applications/phragment/controller/PhragmentSnapshotPromoteController.php b/src/applications/phragment/controller/PhragmentSnapshotPromoteController.php index 18d7df6a5a..981d139742 100644 --- a/src/applications/phragment/controller/PhragmentSnapshotPromoteController.php +++ b/src/applications/phragment/controller/PhragmentSnapshotPromoteController.php @@ -2,32 +2,26 @@ final class PhragmentSnapshotPromoteController extends PhragmentController { - private $dblob; - private $id; private $targetSnapshot; private $targetFragment; private $snapshots; private $options; - public function willProcessRequest(array $data) { - $this->dblob = idx($data, 'dblob', null); - $this->id = idx($data, 'id', null); - } - - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); + $id = $request->getURIData('id'); + $dblob = $request->getURIData('dblob'); // When the user is promoting a snapshot to the latest version, the // identifier is a fragment path. - if ($this->dblob !== null) { + if ($dblob !== null) { $this->targetFragment = id(new PhragmentFragmentQuery()) ->setViewer($viewer) ->requireCapabilities(array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )) - ->withPaths(array($this->dblob)) + ->withPaths(array($dblob)) ->executeOne(); if ($this->targetFragment === null) { return new Aphront404Response(); @@ -41,14 +35,14 @@ final class PhragmentSnapshotPromoteController extends PhragmentController { // When the user is promoting a snapshot to another snapshot, the // identifier is another snapshot ID. - if ($this->id !== null) { + if ($id !== null) { $this->targetSnapshot = id(new PhragmentSnapshotQuery()) ->setViewer($viewer) ->requireCapabilities(array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )) - ->withIDs(array($this->id)) + ->withIDs(array($id)) ->executeOne(); if ($this->targetSnapshot === null) { return new Aphront404Response(); @@ -72,8 +66,8 @@ final class PhragmentSnapshotPromoteController extends PhragmentController { $this->snapshots, 'getName', 'getID'); - if ($this->id !== null) { - unset($this->options[$this->id]); + if ($id !== null) { + unset($this->options[$id]); } // If there's no options, show a dialog telling the @@ -84,7 +78,7 @@ final class PhragmentSnapshotPromoteController extends PhragmentController { ->setTitle(pht('No snapshots to promote')) ->appendParagraph(pht( 'There are no snapshots available to promote.')) - ->setUser($request->getUser()) + ->setUser($this->getViewer()) ->addCancelButton(pht('Cancel'))); } @@ -108,7 +102,7 @@ final class PhragmentSnapshotPromoteController extends PhragmentController { $child->delete(); } - if ($this->id === null) { + if ($id === null) { // The user is promoting the snapshot to the latest version. $children = id(new PhragmentFragmentQuery()) ->setViewer($viewer) @@ -150,7 +144,7 @@ final class PhragmentSnapshotPromoteController extends PhragmentController { } $snapshot->saveTransaction(); - if ($this->id === null) { + if ($id === null) { return id(new AphrontRedirectResponse()) ->setURI($this->targetFragment->getURI()); } else { @@ -159,20 +153,19 @@ final class PhragmentSnapshotPromoteController extends PhragmentController { } } - return $this->createDialog(); + return $this->createDialog($id); } - public function createDialog() { - $request = $this->getRequest(); - $viewer = $request->getUser(); + public function createDialog($id) { + $viewer = $this->getViewer(); $dialog = id(new AphrontDialogView()) ->setTitle(pht('Promote which snapshot?')) - ->setUser($request->getUser()) + ->setUser($this->getViewer()) ->addSubmitButton(pht('Promote')) ->addCancelButton(pht('Cancel')); - if ($this->id === null) { + if ($id === null) { // The user is promoting a snapshot to the latest version. $dialog->appendParagraph(pht( 'Select the snapshot you want to promote to the latest version:')); diff --git a/src/applications/phragment/controller/PhragmentSnapshotViewController.php b/src/applications/phragment/controller/PhragmentSnapshotViewController.php index fe499fffa1..052b5036fb 100644 --- a/src/applications/phragment/controller/PhragmentSnapshotViewController.php +++ b/src/applications/phragment/controller/PhragmentSnapshotViewController.php @@ -2,23 +2,17 @@ final class PhragmentSnapshotViewController extends PhragmentController { - private $id; - public function shouldAllowPublic() { return true; } - public function willProcessRequest(array $data) { - $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'); $snapshot = id(new PhragmentSnapshotQuery()) ->setViewer($viewer) - ->withIDs(array($this->id)) + ->withIDs(array($id)) ->executeOne(); if ($snapshot === null) { return new Aphront404Response(); @@ -71,16 +65,18 @@ final class PhragmentSnapshotViewController extends PhragmentController { $list->addItem($item); } - return $this->buildApplicationPage( - array( - $crumbs, - $this->renderConfigurationWarningIfRequired(), - $box, - $list, - ), - array( - 'title' => pht('View Snapshot'), - )); + $title = pht('View Snapshot'); + + $view = array( + $this->renderConfigurationWarningIfRequired(), + $box, + $list, + ); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); } protected function createSnapshotView($snapshot) { diff --git a/src/applications/phragment/controller/PhragmentUpdateController.php b/src/applications/phragment/controller/PhragmentUpdateController.php index 1b8d10e91f..7fb91ccd4e 100644 --- a/src/applications/phragment/controller/PhragmentUpdateController.php +++ b/src/applications/phragment/controller/PhragmentUpdateController.php @@ -2,17 +2,11 @@ final class PhragmentUpdateController extends PhragmentController { - private $dblob; + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); + $dblob = $request->getURIData('dblob'); - public function willProcessRequest(array $data) { - $this->dblob = idx($data, 'dblob', ''); - } - - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); - - $parents = $this->loadParentFragments($this->dblob); + $parents = $this->loadParentFragments($dblob); if ($parents === null) { return new Aphront404Response(); } @@ -69,15 +63,18 @@ final class PhragmentUpdateController extends PhragmentController { ->setValidationException(null) ->setForm($form); - return $this->buildApplicationPage( - array( - $crumbs, - $this->renderConfigurationWarningIfRequired(), - $box, - ), - array( - 'title' => pht('Update Fragment'), - )); + $title = pht('Update Fragment'); + + $view = array( + $this->renderConfigurationWarningIfRequired(), + $box, + ); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); + } } diff --git a/src/applications/phragment/controller/PhragmentVersionController.php b/src/applications/phragment/controller/PhragmentVersionController.php index 29b920a4e5..2b1ae2bdf0 100644 --- a/src/applications/phragment/controller/PhragmentVersionController.php +++ b/src/applications/phragment/controller/PhragmentVersionController.php @@ -2,23 +2,17 @@ final class PhragmentVersionController extends PhragmentController { - private $id; - public function shouldAllowPublic() { return true; } - public function willProcessRequest(array $data) { - $this->id = idx($data, 'id', 0); - } - - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); + $id = $request->getURIData('id'); $version = id(new PhragmentFragmentVersionQuery()) ->setViewer($viewer) - ->withIDs(array($this->id)) + ->withIDs(array($id)) ->executeOne(); if ($version === null) { return new Aphront404Response(); @@ -71,23 +65,23 @@ final class PhragmentVersionController extends PhragmentController { ->setHeader($header) ->addPropertyList($properties); - return $this->buildApplicationPage( - array( - $crumbs, - $this->renderConfigurationWarningIfRequired(), - $box, - $this->renderPreviousVersionList($version), - ), - array( - 'title' => pht('View Version'), - )); + $title = pht('View Version'); + + $view = array( + $this->renderConfigurationWarningIfRequired(), + $box, + $this->renderPreviousVersionList($version), + ); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); } private function renderPreviousVersionList( PhragmentFragmentVersion $version) { - - $request = $this->getRequest(); - $viewer = $request->getUser(); + $viewer = $this->getViewer(); $previous_versions = id(new PhragmentFragmentVersionQuery()) ->setViewer($viewer) diff --git a/src/applications/phragment/controller/PhragmentZIPController.php b/src/applications/phragment/controller/PhragmentZIPController.php index 0de9f4d344..75d464d9c8 100644 --- a/src/applications/phragment/controller/PhragmentZIPController.php +++ b/src/applications/phragment/controller/PhragmentZIPController.php @@ -2,35 +2,28 @@ final class PhragmentZIPController extends PhragmentController { - private $dblob; - private $snapshot; - private $snapshotCache; public function shouldAllowPublic() { return true; } - public function willProcessRequest(array $data) { - $this->dblob = idx($data, 'dblob', ''); - $this->snapshot = idx($data, 'snapshot', null); - } + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); + $dblob = $request->getURIData('dblob'); + $snapshot = $request->getURIData('snapshot'); - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); - - $parents = $this->loadParentFragments($this->dblob); + $parents = $this->loadParentFragments($dblob); if ($parents === null) { return new Aphront404Response(); } $fragment = idx($parents, count($parents) - 1, null); - if ($this->snapshot !== null) { + if ($snapshot !== null) { $snapshot = id(new PhragmentSnapshotQuery()) ->setViewer($viewer) ->withPrimaryFragmentPHIDs(array($fragment->getPHID())) - ->withNames(array($this->snapshot)) + ->withNames(array($snapshot)) ->executeOne(); if ($snapshot === null) { return new Aphront404Response(); @@ -63,7 +56,7 @@ final class PhragmentZIPController extends PhragmentController { $dialog->setTitle(pht('ZIP Extension Not Installed')); $dialog->appendParagraph($inst); - $dialog->addCancelButton('/phragment/browse/'.$this->dblob); + $dialog->addCancelButton('/phragment/browse/'.$dblob); return id(new AphrontDialogResponse())->setDialog($dialog); } @@ -71,7 +64,8 @@ final class PhragmentZIPController extends PhragmentController { throw new Exception(pht('Unable to create ZIP archive!')); } - $mappings = $this->getFragmentMappings($fragment, $fragment->getPath()); + $mappings = $this->getFragmentMappings( + $fragment, $fragment->getPath(), $snapshot); $phids = array(); foreach ($mappings as $path => $file_phid) { @@ -130,14 +124,17 @@ final class PhragmentZIPController extends PhragmentController { /** * Returns a list of mappings like array('some/path.txt' => 'file PHID'); */ - private function getFragmentMappings(PhragmentFragment $current, $base_path) { + private function getFragmentMappings( + PhragmentFragment $current, + $base_path, + $snapshot) { $mappings = $current->getFragmentMappings( $this->getRequest()->getUser(), $base_path); $result = array(); foreach ($mappings as $path => $fragment) { - $version = $this->getVersion($fragment); + $version = $this->getVersion($fragment, $snapshot); if ($version !== null) { $result[$path] = $version->getFilePHID(); } @@ -145,8 +142,8 @@ final class PhragmentZIPController extends PhragmentController { return $result; } - private function getVersion($fragment) { - if ($this->snapshot === null) { + private function getVersion($fragment, $snapshot) { + if ($snapshot === null) { return $fragment->getLatestVersion(); } else { return idx($this->snapshotCache, $fragment->getPHID(), null); From 83a3ea5705526773008e7bd9db2f972d4821f114 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sat, 2 Apr 2016 14:26:19 +0000 Subject: [PATCH 02/84] Update Phriction Edit/History/Diff UI Summary: Updates various Phriction pages to match new UI Test Plan: New Document, Edit Document, View History, Revert Change Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15572 --- .../controller/PhrictionDiffController.php | 33 ++++++++++--------- .../controller/PhrictionEditController.php | 27 ++++++++++----- .../controller/PhrictionHistoryController.php | 29 +++++++++------- .../controller/PhrictionNewController.php | 10 ++---- 4 files changed, 58 insertions(+), 41 deletions(-) diff --git a/src/applications/phriction/controller/PhrictionDiffController.php b/src/applications/phriction/controller/PhrictionDiffController.php index 11d0b40dfd..5b1a81f940 100644 --- a/src/applications/phriction/controller/PhrictionDiffController.php +++ b/src/applications/phriction/controller/PhrictionDiffController.php @@ -98,6 +98,7 @@ final class PhrictionDiffController extends PhrictionController { ->setRenderingReferences(array("{$l},{$r}")) ->setRenderURI('/phriction/diff/'.$document->getID().'/') ->setTitle(pht('Changes')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setParser($parser); require_celerity_resource('phriction-document-css'); @@ -121,11 +122,10 @@ final class PhrictionDiffController extends PhrictionController { $header = id(new PHUIHeaderView()) ->setHeader($title) - ->setTall(true); + ->setHeaderIcon('fa-history'); $crumbs->addTextCrumb($title, $request->getRequestURI()); - $comparison_table = $this->renderComparisonTable( array( $content_r, @@ -144,7 +144,7 @@ final class PhrictionDiffController extends PhrictionController { 'a', array( 'href' => $uri->alter('l', $l - 1)->alter('r', $r - 1), - 'class' => 'button simple', + 'class' => 'button grey', ), pht("\xC2\xAB Previous Change")); } else { @@ -163,7 +163,7 @@ final class PhrictionDiffController extends PhrictionController { 'a', array( 'href' => $uri->alter('l', $l + 1)->alter('r', $r + 1), - 'class' => 'button simple', + 'class' => 'button grey', ), pht("Next Change \xC2\xBB")); } else { @@ -185,7 +185,6 @@ final class PhrictionDiffController extends PhrictionController { ))); } - $output = hsprintf( '
'. '%s%s'. @@ -198,21 +197,25 @@ final class PhrictionDiffController extends PhrictionController { $revert_l, $revert_r); - $object_box = id(new PHUIObjectBoxView()) - ->setHeader($header) + ->setHeaderText(pht('Edits')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($output); - return $this->buildApplicationPage( - array( - $crumbs, + $crumbs->setBorder(true); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter(array( $object_box, $changes, - ), - array( - 'title' => pht('Document History'), )); + return $this->newPage() + ->setTitle(pht('Document History')) + ->setCrumbs($crumbs) + ->appendChild($view); + } private function renderRevertButton( @@ -238,7 +241,7 @@ final class PhrictionDiffController extends PhrictionController { 'a', array( 'href' => '/phriction/edit/'.$document_id.'/', - 'class' => 'button simple', + 'class' => 'button grey', ), pht('Edit Current Version')); } @@ -248,7 +251,7 @@ final class PhrictionDiffController extends PhrictionController { 'a', array( 'href' => '/phriction/edit/'.$document_id.'/?revert='.$version, - 'class' => 'button simple', + 'class' => 'button grey', ), pht('Revert to Version %s...', $version)); } diff --git a/src/applications/phriction/controller/PhrictionEditController.php b/src/applications/phriction/controller/PhrictionEditController.php index 466d721a03..fd88bf9408 100644 --- a/src/applications/phriction/controller/PhrictionEditController.php +++ b/src/applications/phriction/controller/PhrictionEditController.php @@ -177,8 +177,9 @@ final class PhrictionEditController } if ($document->getID()) { - $panel_header = pht('Edit Phriction Document'); + $panel_header = pht('Edit Document: %s', $content->getTitle()); $page_title = pht('Edit Document'); + $header_icon = 'fa-pencil'; if ($overwrite) { $submit_button = pht('Overwrite Changes'); } else { @@ -188,6 +189,7 @@ final class PhrictionEditController $panel_header = pht('Create New Phriction Document'); $submit_button = pht('Create Document'); $page_title = pht('Create Document'); + $header_icon = 'fa-plus-square'; } $uri = $document->getSlug(); @@ -263,8 +265,9 @@ final class PhrictionEditController ->setValue($submit_button)); $form_box = id(new PHUIObjectBoxView()) - ->setHeaderText($panel_header) + ->setHeaderText(pht('Document')) ->setValidationException($validation_exception) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setForm($form); $preview = id(new PHUIRemarkupPreviewPanel()) @@ -282,17 +285,25 @@ final class PhrictionEditController } else { $crumbs->addTextCrumb(pht('Create')); } + $crumbs->setBorder(true); - return $this->buildApplicationPage( - array( - $crumbs, + $header = id(new PHUIHeaderView()) + ->setHeader($panel_header) + ->setHeaderIcon($header_icon); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter(array( $draft_note, $form_box, $preview, - ), - array( - 'title' => $page_title, )); + + return $this->newPage() + ->setTitle($page_title) + ->setCrumbs($crumbs) + ->appendChild($view); + } } diff --git a/src/applications/phriction/controller/PhrictionHistoryController.php b/src/applications/phriction/controller/PhrictionHistoryController.php index 5ce6c7225c..9b0d3188a2 100644 --- a/src/applications/phriction/controller/PhrictionHistoryController.php +++ b/src/applications/phriction/controller/PhrictionHistoryController.php @@ -3,19 +3,17 @@ final class PhrictionHistoryController extends PhrictionController { - private $slug; - public function shouldAllowPublic() { return true; } public function handleRequest(AphrontRequest $request) { $viewer = $request->getViewer(); - $this->slug = $request->getURIData('slug'); + $slug = $request->getURIData('slug'); $document = id(new PhrictionDocumentQuery()) ->setViewer($viewer) - ->withSlugs(array(PhabricatorSlug::normalize($this->slug))) + ->withSlugs(array(PhabricatorSlug::normalize($slug))) ->needContent(true) ->executeOne(); if (!$document) { @@ -141,6 +139,7 @@ final class PhrictionHistoryController $crumbs->addTextCrumb( pht('History'), PhrictionDocument::getSlugURI($document->getSlug(), 'history')); + $crumbs->setBorder(true); $header = new PHUIHeaderView(); $header->setHeader(phutil_tag( @@ -150,23 +149,31 @@ final class PhrictionHistoryController $header->setSubheader(pht('Document History')); $obj_box = id(new PHUIObjectBoxView()) - ->setHeader($header) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setObjectList($list); $pager = id(new PHUIBoxView()) ->addClass('ml') ->appendChild($pager); - return $this->buildApplicationPage( - array( - $crumbs, + $header = id(new PHUIHeaderView()) + ->setHeader(pht('Document History: %s', head($history)->getTitle())) + ->setHeaderIcon('fa-history'); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter(array( $obj_box, $pager, - ), - array( - 'title' => pht('Document History'), )); + $title = pht('Document History'); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); + } } diff --git a/src/applications/phriction/controller/PhrictionNewController.php b/src/applications/phriction/controller/PhrictionNewController.php index c5659be6cc..bcb8e317ab 100644 --- a/src/applications/phriction/controller/PhrictionNewController.php +++ b/src/applications/phriction/controller/PhrictionNewController.php @@ -16,8 +16,8 @@ final class PhrictionNewController extends PhrictionController { PhrictionDocumentStatus::STATUS_EXISTS; if ($document_exists && $prompt == 'no') { - $dialog = new AphrontDialogView(); - $dialog->setSubmitURI('/phriction/new/') + return $this->newDialog() + ->setSubmitURI('/phriction/new/') ->setTitle(pht('Edit Existing Document?')) ->setUser($viewer) ->appendChild(pht( @@ -27,8 +27,6 @@ final class PhrictionNewController extends PhrictionController { ->addHiddenInput('prompt', 'yes') ->addCancelButton('/w/') ->addSubmitButton(pht('Edit Document')); - - return id(new AphrontDialogResponse())->setDialog($dialog); } $uri = '/phriction/edit/?slug='.$slug; @@ -46,8 +44,7 @@ final class PhrictionNewController extends PhrictionController { ->setValue($slug) ->setName('slug')); - $dialog = id(new AphrontDialogView()) - ->setUser($viewer) + return $this->newDialog() ->setTitle(pht('New Document')) ->setSubmitURI('/phriction/new/') ->appendChild(phutil_tag('p', @@ -57,7 +54,6 @@ final class PhrictionNewController extends PhrictionController { ->addSubmitButton(pht('Create')) ->addCancelButton('/w/'); - return id(new AphrontDialogResponse())->setDialog($dialog); } } From 27e13ea03f92ddef0b33ad451e0dfad2dd37e6b2 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sat, 2 Apr 2016 10:18:52 -0700 Subject: [PATCH 03/84] Update Deamons with new UI Summary: Modernize and use newer UI Test Plan: Bounce around various views. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15574 --- ...PhabricatorDaemonBulkJobViewController.php | 37 +++++++----- .../PhabricatorDaemonConsoleController.php | 18 +++--- ...habricatorDaemonLogEventViewController.php | 28 +++++---- .../PhabricatorDaemonLogListController.php | 9 ++- .../PhabricatorDaemonLogViewController.php | 59 +++++++++++-------- .../PhabricatorWorkerTaskDetailController.php | 27 +++++---- 6 files changed, 99 insertions(+), 79 deletions(-) diff --git a/src/applications/daemon/controller/PhabricatorDaemonBulkJobViewController.php b/src/applications/daemon/controller/PhabricatorDaemonBulkJobViewController.php index f32892cf4c..f78bfdd7df 100644 --- a/src/applications/daemon/controller/PhabricatorDaemonBulkJobViewController.php +++ b/src/applications/daemon/controller/PhabricatorDaemonBulkJobViewController.php @@ -23,13 +23,14 @@ final class PhabricatorDaemonBulkJobViewController $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Bulk Jobs'), '/daemon/bulk/'); $crumbs->addTextCrumb($title); + $crumbs->setBorder(true); $properties = $this->renderProperties($job); - $actions = $this->renderActions($job); - $properties->setActionList($actions); + $curtain = $this->buildCurtainView($job); $box = id(new PHUIObjectBoxView()) - ->setHeaderText($title) + ->setHeaderText(pht('DETAILS')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->addPropertyList($properties); $timeline = $this->buildTransactionTimeline( @@ -37,15 +38,22 @@ final class PhabricatorDaemonBulkJobViewController new PhabricatorWorkerBulkJobTransactionQuery()); $timeline->setShouldTerminate(true); - return $this->buildApplicationPage( - array( - $crumbs, + $header = id(new PHUIHeaderView()) + ->setHeader($title) + ->setHeaderIcon('fa-hourglass'); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setCurtain($curtain) + ->setMainColumn(array( $box, $timeline, - ), - array( - 'title' => $title, )); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); } private function renderProperties(PhabricatorWorkerBulkJob $job) { @@ -64,12 +72,9 @@ final class PhabricatorDaemonBulkJobViewController return $view; } - private function renderActions(PhabricatorWorkerBulkJob $job) { + private function buildCurtainView(PhabricatorWorkerBulkJob $job) { $viewer = $this->getViewer(); - - $actions = id(new PhabricatorActionListView()) - ->setUser($viewer) - ->setObject($job); + $curtain = $this->newCurtainView($job); if ($job->isConfirming()) { $continue_uri = $job->getMonitorURI(); @@ -77,13 +82,13 @@ final class PhabricatorDaemonBulkJobViewController $continue_uri = $job->getDoneURI(); } - $actions->addAction( + $curtain->addAction( id(new PhabricatorActionView()) ->setHref($continue_uri) ->setIcon('fa-arrow-circle-o-right') ->setName(pht('Continue'))); - return $actions; + return $curtain; } } diff --git a/src/applications/daemon/controller/PhabricatorDaemonConsoleController.php b/src/applications/daemon/controller/PhabricatorDaemonConsoleController.php index 9f54726bc9..a488ae3a63 100644 --- a/src/applications/daemon/controller/PhabricatorDaemonConsoleController.php +++ b/src/applications/daemon/controller/PhabricatorDaemonConsoleController.php @@ -117,16 +117,15 @@ final class PhabricatorDaemonConsoleController 'n', )); - $completed_panel = new PHUIObjectBoxView(); - $completed_panel->setHeaderText( - pht('Recently Completed Tasks (Last 15m)')); - $completed_panel->setTable($completed_table); + $completed_panel = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Recently Completed Tasks (Last 15m)')) + ->setTable($completed_table); $daemon_table = new PhabricatorDaemonLogListView(); $daemon_table->setUser($viewer); $daemon_table->setDaemonLogs($logs); - $daemon_panel = new PHUIObjectBoxView(); + $daemon_panel = id(new PHUIObjectBoxView()); $daemon_panel->setHeaderText(pht('Active Daemons')); $daemon_panel->setObjectList($daemon_table); @@ -218,11 +217,10 @@ final class PhabricatorDaemonConsoleController $triggers_panel, )); - return $this->buildApplicationPage( - $nav, - array( - 'title' => pht('Console'), - )); + return $this->newPage() + ->setTitle(pht('Console')) + ->appendChild($nav); + } private function buildTriggersTable(array $triggers) { diff --git a/src/applications/daemon/controller/PhabricatorDaemonLogEventViewController.php b/src/applications/daemon/controller/PhabricatorDaemonLogEventViewController.php index 772ee87fd1..208a20b9a0 100644 --- a/src/applications/daemon/controller/PhabricatorDaemonLogEventViewController.php +++ b/src/applications/daemon/controller/PhabricatorDaemonLogEventViewController.php @@ -17,9 +17,9 @@ final class PhabricatorDaemonLogEventViewController ->setCombinedLog(true) ->setShowFullMessage(true); - $log_panel = new PHUIObjectBoxView(); - $log_panel->setHeaderText(pht('Combined Log')); - $log_panel->appendChild($event_view); + $log_panel = id(new PHUIObjectBoxView()) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->appendChild($event_view); $daemon_id = $event->getLogID(); @@ -27,17 +27,21 @@ final class PhabricatorDaemonLogEventViewController ->addTextCrumb( pht('Daemon %s', $daemon_id), $this->getApplicationURI("log/{$daemon_id}/")) - ->addTextCrumb(pht('Event %s', $event->getID())); + ->addTextCrumb(pht('Event %s', $event->getID())) + ->setBorder(true); + $header = id(new PHUIHeaderView()) + ->setHeader(pht('Combined Log')) + ->setHeaderIcon('fa-file-text'); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter($log_panel); + + return $this->newPage() + ->setTitle(pht('Combined Daemon Log')) + ->appendChild($view); - return $this->buildApplicationPage( - array( - $crumbs, - $log_panel, - ), - array( - 'title' => pht('Combined Daemon Log'), - )); } } diff --git a/src/applications/daemon/controller/PhabricatorDaemonLogListController.php b/src/applications/daemon/controller/PhabricatorDaemonLogListController.php index c1de0b892f..e5ef050d99 100644 --- a/src/applications/daemon/controller/PhabricatorDaemonLogListController.php +++ b/src/applications/daemon/controller/PhabricatorDaemonLogListController.php @@ -31,11 +31,10 @@ final class PhabricatorDaemonLogListController $nav->appendChild($box); $nav->appendChild($pager); - return $this->buildApplicationPage( - $nav, - array( - 'title' => pht('All Daemons'), - )); + return $this->newPage() + ->setTitle(pht('All Daemons')) + ->appendChild($nav); + } } diff --git a/src/applications/daemon/controller/PhabricatorDaemonLogViewController.php b/src/applications/daemon/controller/PhabricatorDaemonLogViewController.php index 32af8f6f13..f2f5121898 100644 --- a/src/applications/daemon/controller/PhabricatorDaemonLogViewController.php +++ b/src/applications/daemon/controller/PhabricatorDaemonLogViewController.php @@ -22,9 +22,11 @@ final class PhabricatorDaemonLogViewController $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Daemon %s', $log->getID())); + $crumbs->setBorder(true); $header = id(new PHUIHeaderView()) - ->setHeader($log->getDaemon()); + ->setHeader($log->getDaemon()) + ->setHeaderIcon('fa-pied-piper-alt'); $tag = id(new PHUITagView()) ->setType(PHUITagView::TYPE_STATE); @@ -32,32 +34,38 @@ final class PhabricatorDaemonLogViewController $status = $log->getStatus(); switch ($status) { case PhabricatorDaemonLog::STATUS_UNKNOWN: - $tag->setBackgroundColor(PHUITagView::COLOR_ORANGE); - $tag->setName(pht('Unknown')); + $color = 'orange'; + $name = pht('Unknown'); + $icon = 'fa-warning'; break; case PhabricatorDaemonLog::STATUS_RUNNING: - $tag->setBackgroundColor(PHUITagView::COLOR_GREEN); - $tag->setName(pht('Running')); + $color = 'green'; + $name = pht('Running'); + $icon = 'fa-rocket'; break; case PhabricatorDaemonLog::STATUS_DEAD: - $tag->setBackgroundColor(PHUITagView::COLOR_RED); - $tag->setName(pht('Dead')); + $color = 'red'; + $name = pht('Dead'); + $icon = 'fa-times'; break; case PhabricatorDaemonLog::STATUS_WAIT: - $tag->setBackgroundColor(PHUITagView::COLOR_BLUE); - $tag->setName(pht('Waiting')); + $color = 'blue'; + $name = pht('Waiting'); + $icon = 'fa-clock-o'; break; case PhabricatorDaemonLog::STATUS_EXITING: - $tag->setBackgroundColor(PHUITagView::COLOR_YELLOW); - $tag->setName(pht('Exiting')); + $color = 'yellow'; + $name = pht('Exiting'); + $icon = 'fa-check'; break; case PhabricatorDaemonLog::STATUS_EXITED: - $tag->setBackgroundColor(PHUITagView::COLOR_GREY); - $tag->setName(pht('Exited')); + $color = 'bluegrey'; + $name = pht('Exited'); + $icon = 'fa-check'; break; } - $header->addTag($tag); + $header->setStatus($icon, $color, $name); $properties = $this->buildPropertyListView($log); @@ -65,23 +73,26 @@ final class PhabricatorDaemonLogViewController ->setUser($viewer) ->setEvents($events); - $event_panel = new PHUIObjectBoxView(); - $event_panel->setHeaderText(pht('Events')); - $event_panel->appendChild($event_view); + $event_panel = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Events')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->appendChild($event_view); $object_box = id(new PHUIObjectBoxView()) - ->setHeader($header) ->addPropertyList($properties); - return $this->buildApplicationPage( - array( - $crumbs, + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter(array( $object_box, $event_panel, - ), - array( - 'title' => pht('Daemon Log'), )); + + return $this->newPage() + ->setTitle(pht('Daemon Log')) + ->setCrumbs($crumbs) + ->appendChild($view); + } private function buildPropertyListView(PhabricatorDaemonLog $daemon) { diff --git a/src/applications/daemon/controller/PhabricatorWorkerTaskDetailController.php b/src/applications/daemon/controller/PhabricatorWorkerTaskDetailController.php index ad8f673fd7..ad15d41b9d 100644 --- a/src/applications/daemon/controller/PhabricatorWorkerTaskDetailController.php +++ b/src/applications/daemon/controller/PhabricatorWorkerTaskDetailController.php @@ -31,17 +31,18 @@ final class PhabricatorWorkerTaskDetailController $title = pht('Task %d', $task->getID()); $header = id(new PHUIHeaderView()) - ->setHeader(pht('Task %d (%s)', + ->setHeader(pht('Task %d: %s', $task->getID(), - $task->getTaskClass())); + $task->getTaskClass())) + ->setHeaderIcon('fa-sort'); $properties = $this->buildPropertyListView($task); $object_box = id(new PHUIObjectBoxView()) - ->setHeader($header) + ->setHeaderText($title) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->addPropertyList($properties); - $retry_head = id(new PHUIHeaderView()) ->setHeader(pht('Retries')); @@ -49,6 +50,7 @@ final class PhabricatorWorkerTaskDetailController $retry_box = id(new PHUIObjectBoxView()) ->setHeader($retry_head) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->addPropertyList($retry_info); $content = array( @@ -59,15 +61,16 @@ final class PhabricatorWorkerTaskDetailController $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb($title); + $crumbs->setBorder(true); - return $this->buildApplicationPage( - array( - $crumbs, - $content, - ), - array( - 'title' => $title, - )); + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter($content); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); } private function buildPropertyListView( From a8c9a5597d64de750f3ec071d815239007a009d1 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sat, 2 Apr 2016 10:34:13 -0700 Subject: [PATCH 04/84] Improve error and header behaviors for Mailgun received mail webhook Summary: Ref T10709. Two issues: - If a user sends an invalid `!command`, we can throw, which means we don't return HTTP 200. This makes Mailgun re-send the mail later. - We don't parse headers of the modern API correctly, so the "Message-ID" failsafe doesn't work. Parse them correctly. I //believe// Mailgun's API changed at some point. Test Plan: This is difficult to test exhaustively in isolation. I used Mailgun's web tools to verify the format of the hook request, and faked some requests locally. I'll keep an eye on this as it goes to production and make sure the fix is correct there. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10709 Differential Revision: https://secure.phabricator.com/D15575 --- ...ricatorMetaMTAMailgunReceiveController.php | 34 ++++++++++++++----- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/applications/metamta/controller/PhabricatorMetaMTAMailgunReceiveController.php b/src/applications/metamta/controller/PhabricatorMetaMTAMailgunReceiveController.php index af5ca34712..467995a186 100644 --- a/src/applications/metamta/controller/PhabricatorMetaMTAMailgunReceiveController.php +++ b/src/applications/metamta/controller/PhabricatorMetaMTAMailgunReceiveController.php @@ -28,14 +28,14 @@ final class PhabricatorMetaMTAMailgunReceiveController pht('Mail signature is not valid. Check your Mailgun API key.')); } - $user = $request->getUser(); - - $raw_headers = $request->getStr('headers'); - $raw_headers = explode("\n", rtrim($raw_headers)); + $raw_headers = $request->getStr('message-headers'); $raw_dict = array(); - foreach (array_filter($raw_headers) as $header) { - list($name, $value) = explode(':', $header, 2); - $raw_dict[$name] = ltrim($value); + if (strlen($raw_headers)) { + $raw_headers = phutil_json_decode($raw_headers); + foreach ($raw_headers as $raw_header) { + list($name, $value) = $raw_header; + $raw_dict[$name] = $value; + } } $headers = array( @@ -65,9 +65,25 @@ final class PhabricatorMetaMTAMailgunReceiveController } } $received->setAttachments($file_phids); - $received->save(); - $received->processReceivedMail(); + try { + $received->save(); + $received->processReceivedMail(); + } catch (Exception $ex) { + // We can get exceptions here in two cases. + + // First, saving the message may throw if we have already received a + // message with the same Message ID. In this case, we're declining to + // process a duplicate message, so failing silently is correct. + + // Second, processing the message may throw (for example, if it contains + // an invalid !command). This will generate an email as a side effect, + // so we don't need to explicitly handle the exception here. + + // In these cases, we want to return HTTP 200. If we do not, MailGun will + // re-transmit the message later. + phlog($ex); + } $response = new AphrontWebpageResponse(); $response->setContent(pht("Got it! Thanks, Mailgun!\n")); From b5f0b589878d88a8fd8d2a76758f481d19591fe1 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sat, 2 Apr 2016 12:32:58 -0700 Subject: [PATCH 05/84] Update chatlog to newPage() Summary: Just clearing these all out. Test Plan: Visit channel list and log page. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15577 --- .../PhabricatorChatLogChannelListController.php | 13 +++++-------- .../PhabricatorChatLogChannelLogController.php | 13 +++++-------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/applications/chatlog/controller/PhabricatorChatLogChannelListController.php b/src/applications/chatlog/controller/PhabricatorChatLogChannelListController.php index cdff189633..530c26770a 100644 --- a/src/applications/chatlog/controller/PhabricatorChatLogChannelListController.php +++ b/src/applications/chatlog/controller/PhabricatorChatLogChannelListController.php @@ -32,13 +32,10 @@ final class PhabricatorChatLogChannelListController ->setHeaderText('Channel List') ->setObjectList($list); - return $this->buildApplicationPage( - array( - $crumbs, - $box, - ), - array( - 'title' => pht('Channel List'), - )); + return $this->newPage() + ->setTitle(pht('Channel List')) + ->setCrumbs($crumbs) + ->appendChild($box); + } } diff --git a/src/applications/chatlog/controller/PhabricatorChatLogChannelLogController.php b/src/applications/chatlog/controller/PhabricatorChatLogChannelLogController.php index fe505ecf60..2c6e58da50 100644 --- a/src/applications/chatlog/controller/PhabricatorChatLogChannelLogController.php +++ b/src/applications/chatlog/controller/PhabricatorChatLogChannelLogController.php @@ -248,14 +248,11 @@ final class PhabricatorChatLogChannelLogController $form, '#'); - return $this->buildApplicationPage( - array( - $crumbs, - $box, - ), - array( - 'title' => pht('Channel Log'), - )); + return $this->newPage() + ->setTitle(pht('Channel Log')) + ->setCrumbs($crumbs) + ->appendChild($box); + } /** From 5a1990487df74cae2d1a09fec736249ef2b519d2 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sat, 2 Apr 2016 12:38:28 -0700 Subject: [PATCH 06/84] Update Conpherence to newPage Summary: Updates Conpherence pages to use `newPage` Test Plan: View a Room, view list of joined rooms. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15578 --- .../controller/ConpherenceListController.php | 8 +++----- .../controller/ConpherenceViewController.php | 10 ++++------ 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/applications/conpherence/controller/ConpherenceListController.php b/src/applications/conpherence/controller/ConpherenceListController.php index dbabc9b7d9..6f06a36fb0 100644 --- a/src/applications/conpherence/controller/ConpherenceListController.php +++ b/src/applications/conpherence/controller/ConpherenceListController.php @@ -127,11 +127,9 @@ final class ConpherenceListController extends ConpherenceController { $layout->setHeader($this->buildHeaderPaneContent( $conpherence, $policy_objects)); - $response = $this->buildApplicationPage( - $layout, - array( - 'title' => $title, - )); + $response = $this->newPage() + ->setTitle($title) + ->appendChild($layout); break; } diff --git a/src/applications/conpherence/controller/ConpherenceViewController.php b/src/applications/conpherence/controller/ConpherenceViewController.php index ca2a87b6d2..2f01979df6 100644 --- a/src/applications/conpherence/controller/ConpherenceViewController.php +++ b/src/applications/conpherence/controller/ConpherenceViewController.php @@ -131,12 +131,10 @@ final class ConpherenceViewController extends ->setLatestTransactionID($data['latest_transaction_id']) ->setRole('thread'); - return $this->buildApplicationPage( - $layout, - array( - 'title' => $title, - 'pageObjects' => array($conpherence->getPHID()), - )); + return $this->newPage() + ->setTitle($title) + ->setPageObjectPHIDs(array($conpherence->getPHID())) + ->appendChild($layout); } private function renderFormContent() { From 18e9e793c37b4bbf2ebe5852d6a1f03e49ad4c84 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sat, 2 Apr 2016 12:45:18 -0700 Subject: [PATCH 07/84] Update Phurl Edit page to new UI Summary: Updates Phurl UI on Edit/Create Test Plan: Edit a Phurl, Create a Phurl. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15579 --- .../PhabricatorPhurlURLEditController.php | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/applications/phurl/controller/PhabricatorPhurlURLEditController.php b/src/applications/phurl/controller/PhabricatorPhurlURLEditController.php index 4136c855d8..3af53802e5 100644 --- a/src/applications/phurl/controller/PhabricatorPhurlURLEditController.php +++ b/src/applications/phurl/controller/PhabricatorPhurlURLEditController.php @@ -24,6 +24,7 @@ final class PhabricatorPhurlURLEditController $viewer); $submit_label = pht('Create'); $page_title = pht('Shorten URL'); + $header_icon = 'fa-plus-square'; $subscribers = array(); $cancel_uri = $this->getApplicationURI(); } else { @@ -42,7 +43,8 @@ final class PhabricatorPhurlURLEditController } $submit_label = pht('Update'); - $page_title = pht('Update URL'); + $page_title = pht('Edit URL: %s', $url->getName()); + $header_icon = 'fa-pencil'; $subscribers = PhabricatorSubscribersQuery::loadSubscribersForPHID( $url->getPHID()); @@ -238,19 +240,27 @@ final class PhabricatorPhurlURLEditController } $crumbs->addTextCrumb($page_title); + $crumbs->setBorder(true); $object_box = id(new PHUIObjectBoxView()) - ->setHeaderText($page_title) + ->setHeaderText(pht('URL')) ->setValidationException($validation_exception) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($form); - return $this->buildApplicationPage( - array( - $crumbs, + $header = id(new PHUIHeaderView()) + ->setHeader($page_title) + ->setHeaderIcon($header_icon); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter(array( $object_box, - ), - array( - 'title' => $page_title, )); + + return $this->newPage() + ->setTitle($page_title) + ->setCrumbs($crumbs) + ->appendChild($view); } } From 891661fbe7e7e14057aee3ef8903069e84f4046c Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sat, 2 Apr 2016 12:49:05 -0700 Subject: [PATCH 08/84] Update PhrictionDocument to newPage Summary: Little straggler here, updates to `newPage` Test Plan: Review a document, no visibile changes Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15580 --- .../controller/PhrictionDocumentController.php | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/applications/phriction/controller/PhrictionDocumentController.php b/src/applications/phriction/controller/PhrictionDocumentController.php index 85bfa1a660..7601888987 100644 --- a/src/applications/phriction/controller/PhrictionDocumentController.php +++ b/src/applications/phriction/controller/PhrictionDocumentController.php @@ -230,16 +230,14 @@ final class PhrictionDocumentController $core_content, )); - return $this->buildApplicationPage( - array( - $crumbs->render(), + return $this->newPage() + ->setTitle($page_title) + ->setCrumbs($crumbs) + ->setPageObjectPHIDs(array($document->getPHID())) + ->appendChild(array( $page_content, $prop_list, $children, - ), - array( - 'pageObjects' => array($document->getPHID()), - 'title' => $page_title, )); } From 64e117f1a588d8fab42fdb85b41f67400c871c5f Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sat, 2 Apr 2016 12:56:06 -0700 Subject: [PATCH 09/84] Modernize Nuance Console page Summary: Uses UI like Alamanc Console page Test Plan: Review Console page, click on items. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15581 --- .../controller/NuanceConsoleController.php | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/applications/nuance/controller/NuanceConsoleController.php b/src/applications/nuance/controller/NuanceConsoleController.php index 6416c19aaf..d4a8f6c9ff 100644 --- a/src/applications/nuance/controller/NuanceConsoleController.php +++ b/src/applications/nuance/controller/NuanceConsoleController.php @@ -35,19 +35,25 @@ final class NuanceConsoleController extends NuanceController { $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Console')); + $crumbs->setBorder(true); $box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Console')) ->setObjectList($menu); - return $this->buildApplicationPage( - array( - $crumbs, + $header = id(new PHUIHeaderView()) + ->setHeader(pht('Nuance Console')) + ->setHeaderIcon('fa-fax'); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter(array( $box, - ), - array( - 'title' => pht('Nuance Console'), )); + + return $this->newPage() + ->setTitle(pht('Nuance Console')) + ->setCrumbs($crumbs) + ->appendChild($view); } } From 7a1d2087d46d97d245091a568595b6790546c1d7 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sat, 2 Apr 2016 13:08:05 -0700 Subject: [PATCH 10/84] Update Tokens for newPage Summary: Minor, moves to `newPage` Test Plan: Test both pages, still work Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15582 --- .../controller/PhabricatorTokenGivenController.php | 10 ++++------ .../controller/PhabricatorTokenLeaderController.php | 9 ++++----- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/applications/tokens/controller/PhabricatorTokenGivenController.php b/src/applications/tokens/controller/PhabricatorTokenGivenController.php index 86318a885a..8352c35d69 100644 --- a/src/applications/tokens/controller/PhabricatorTokenGivenController.php +++ b/src/applications/tokens/controller/PhabricatorTokenGivenController.php @@ -71,12 +71,10 @@ final class PhabricatorTokenGivenController extends PhabricatorTokenController { $nav->appendChild($box); $nav->appendChild($pager); - return $this->buildApplicationPage( - $nav, - array( - 'title' => $title, - )); + return $this->newPage() + ->setTitle($title) + ->appendChild($nav); + } - } diff --git a/src/applications/tokens/controller/PhabricatorTokenLeaderController.php b/src/applications/tokens/controller/PhabricatorTokenLeaderController.php index 53cda0f484..bfef3cd8cf 100644 --- a/src/applications/tokens/controller/PhabricatorTokenLeaderController.php +++ b/src/applications/tokens/controller/PhabricatorTokenLeaderController.php @@ -55,11 +55,10 @@ final class PhabricatorTokenLeaderController $nav->appendChild($box); $nav->appendChild($pager); - return $this->buildApplicationPage( - $nav, - array( - 'title' => $title, - )); + return $this->newPage() + ->setTitle($title) + ->appendChild($nav); + } } From fd94e07aadf030653348d0db49b7b20700be7a5b Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sat, 2 Apr 2016 13:10:45 -0700 Subject: [PATCH 11/84] Update UIExamples for newPage Summary: clever commit summary Test Plan: clever test plan Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15583 --- .../controller/PhabricatorUIExampleRenderController.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/applications/uiexample/controller/PhabricatorUIExampleRenderController.php b/src/applications/uiexample/controller/PhabricatorUIExampleRenderController.php index 8da553104f..6be978a6a9 100644 --- a/src/applications/uiexample/controller/PhabricatorUIExampleRenderController.php +++ b/src/applications/uiexample/controller/PhabricatorUIExampleRenderController.php @@ -50,11 +50,9 @@ final class PhabricatorUIExampleRenderController extends PhabricatorController { $result, )); - return $this->buildApplicationPage( - $nav, - array( - 'title' => $example->getName(), - )); + return $this->newPage() + ->setTitle($example->getName()) + ->appendChild($nav); } } From 8d8f983f6af5da63e7ea02add5f6ba641be1304c Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sat, 2 Apr 2016 16:02:13 -0700 Subject: [PATCH 12/84] Modernize Dashboard UI and code Summary: Pulls everything over to two column UI and new edit pages. Removed history view and consolidated some pages. Test Plan: New Panel, Edit Panel. New Dashboard, Edit Dashboard, View Standalone pages. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15588 --- resources/celerity/map.php | 6 +- src/__phutil_library_map__.php | 2 - .../PhabricatorDashboardApplication.php | 1 - .../PhabricatorDashboardEditController.php | 47 ++++---- .../PhabricatorDashboardHistoryController.php | 48 -------- .../PhabricatorDashboardManageController.php | 103 +++++++++--------- ...habricatorDashboardPanelEditController.php | 59 ++++++---- ...bricatorDashboardPanelRenderController.php | 16 ++- ...habricatorDashboardPanelViewController.php | 80 +++++++------- .../PhabricatorDashboardViewController.php | 12 +- .../css/application/dashboard/dashboard.css | 10 +- 11 files changed, 179 insertions(+), 205 deletions(-) delete mode 100644 src/applications/dashboard/controller/PhabricatorDashboardHistoryController.php diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 3d5f59abe7..17293755c7 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -7,7 +7,7 @@ */ return array( 'names' => array( - 'core.pkg.css' => '2d0339fc', + 'core.pkg.css' => '5de0f7af', 'core.pkg.js' => 'e5484f37', 'darkconsole.pkg.js' => 'e7393ebb', 'differential.pkg.css' => '7ba78475', @@ -54,7 +54,7 @@ return array( 'rsrc/css/application/contentsource/content-source-view.css' => '4b8b05d4', 'rsrc/css/application/countdown/timer.css' => '96696f21', 'rsrc/css/application/daemon/bulk-job.css' => 'df9c1d4a', - 'rsrc/css/application/dashboard/dashboard.css' => 'eb458607', + 'rsrc/css/application/dashboard/dashboard.css' => 'bc6f2127', 'rsrc/css/application/diff/inline-comment-summary.css' => '51efda3a', 'rsrc/css/application/differential/add-comment.css' => 'c47f8c40', 'rsrc/css/application/differential/changeset-view.css' => '3e3b0b76', @@ -753,7 +753,7 @@ return array( 'phabricator-content-source-view-css' => '4b8b05d4', 'phabricator-core-css' => 'd0801452', 'phabricator-countdown-css' => '96696f21', - 'phabricator-dashboard-css' => 'eb458607', + 'phabricator-dashboard-css' => 'bc6f2127', 'phabricator-drag-and-drop-file-upload' => '81f182b5', 'phabricator-draggable-list' => '5a13c79f', 'phabricator-fatal-config-template-css' => '8e6c6fcd', diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index c4bc803f52..58216de058 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2183,7 +2183,6 @@ phutil_register_library_map(array( 'PhabricatorDashboardDashboardHasPanelEdgeType' => 'applications/dashboard/edge/PhabricatorDashboardDashboardHasPanelEdgeType.php', 'PhabricatorDashboardDashboardPHIDType' => 'applications/dashboard/phid/PhabricatorDashboardDashboardPHIDType.php', 'PhabricatorDashboardEditController' => 'applications/dashboard/controller/PhabricatorDashboardEditController.php', - 'PhabricatorDashboardHistoryController' => 'applications/dashboard/controller/PhabricatorDashboardHistoryController.php', 'PhabricatorDashboardInstall' => 'applications/dashboard/storage/PhabricatorDashboardInstall.php', 'PhabricatorDashboardInstallController' => 'applications/dashboard/controller/PhabricatorDashboardInstallController.php', 'PhabricatorDashboardLayoutConfig' => 'applications/dashboard/layoutconfig/PhabricatorDashboardLayoutConfig.php', @@ -6604,7 +6603,6 @@ phutil_register_library_map(array( 'PhabricatorDashboardDashboardHasPanelEdgeType' => 'PhabricatorEdgeType', 'PhabricatorDashboardDashboardPHIDType' => 'PhabricatorPHIDType', 'PhabricatorDashboardEditController' => 'PhabricatorDashboardController', - 'PhabricatorDashboardHistoryController' => 'PhabricatorDashboardController', 'PhabricatorDashboardInstall' => 'PhabricatorDashboardDAO', 'PhabricatorDashboardInstallController' => 'PhabricatorDashboardController', 'PhabricatorDashboardLayoutConfig' => 'Phobject', diff --git a/src/applications/dashboard/application/PhabricatorDashboardApplication.php b/src/applications/dashboard/application/PhabricatorDashboardApplication.php index 9653256630..3e67f76f07 100644 --- a/src/applications/dashboard/application/PhabricatorDashboardApplication.php +++ b/src/applications/dashboard/application/PhabricatorDashboardApplication.php @@ -27,7 +27,6 @@ final class PhabricatorDashboardApplication extends PhabricatorApplication { 'view/(?P\d+)/' => 'PhabricatorDashboardViewController', 'archive/(?P\d+)/' => 'PhabricatorDashboardArchiveController', 'manage/(?P\d+)/' => 'PhabricatorDashboardManageController', - 'history/(?P\d+)/' => 'PhabricatorDashboardHistoryController', 'create/' => 'PhabricatorDashboardEditController', 'copy/(?:(?P\d+)/)?' => 'PhabricatorDashboardCopyController', 'edit/(?:(?P\d+)/)?' => 'PhabricatorDashboardEditController', diff --git a/src/applications/dashboard/controller/PhabricatorDashboardEditController.php b/src/applications/dashboard/controller/PhabricatorDashboardEditController.php index 16fdac5577..6dc4a6b8b2 100644 --- a/src/applications/dashboard/controller/PhabricatorDashboardEditController.php +++ b/src/applications/dashboard/controller/PhabricatorDashboardEditController.php @@ -49,7 +49,7 @@ final class PhabricatorDashboardEditController if ($is_new) { $title = pht('Create Dashboard'); - $header = pht('Create Dashboard'); + $header_icon = 'fa-plus-square'; $button = pht('Create Dashboard'); $cancel_uri = $this->getApplicationURI(); @@ -58,11 +58,11 @@ final class PhabricatorDashboardEditController $id = $dashboard->getID(); $cancel_uri = $this->getApplicationURI('manage/'.$id.'/'); - $title = pht('Edit Dashboard %d', $dashboard->getID()); - $header = pht('Edit Dashboard "%s"', $dashboard->getName()); + $title = pht('Edit Dashboard: %s', $dashboard->getName()); + $header_icon = 'fa-pencil'; $button = pht('Save Changes'); - $crumbs->addTextCrumb(pht('Dashboard %d', $id), $cancel_uri); + $crumbs->addTextCrumb($dashboard->getName(), $cancel_uri); $crumbs->addTextCrumb(pht('Edit')); } @@ -140,6 +140,12 @@ final class PhabricatorDashboardEditController ->setName('name') ->setValue($v_name) ->setError($e_name)) + ->appendChild( + id(new AphrontFormSelectControl()) + ->setLabel(pht('Layout Mode')) + ->setName('layout_mode') + ->setValue($v_layout_mode) + ->setOptions($layout_mode_options)) ->appendChild( id(new AphrontFormPolicyControl()) ->setName('viewPolicy') @@ -151,13 +157,7 @@ final class PhabricatorDashboardEditController ->setName('editPolicy') ->setPolicyObject($dashboard) ->setCapability(PhabricatorPolicyCapability::CAN_EDIT) - ->setPolicies($policies)) - ->appendChild( - id(new AphrontFormSelectControl()) - ->setLabel(pht('Layout Mode')) - ->setName('layout_mode') - ->setValue($v_layout_mode) - ->setOptions($layout_mode_options)); + ->setPolicies($policies)); $form->appendControl( id(new AphrontFormTokenizerControl()) @@ -172,18 +172,25 @@ final class PhabricatorDashboardEditController ->addCancelButton($cancel_uri)); $box = id(new PHUIObjectBoxView()) - ->setHeaderText($header) + ->setHeaderText(pht('Dashboard')) ->setForm($form) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setValidationException($validation_exception); - return $this->buildApplicationPage( - array( - $crumbs, - $box, - ), - array( - 'title' => $title, - )); + $crumbs->setBorder(true); + + $header = id(new PHUIHeaderView()) + ->setHeader($title) + ->setHeaderIcon($header_icon); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter($box); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); } private function processTemplateRequest(AphrontRequest $request) { diff --git a/src/applications/dashboard/controller/PhabricatorDashboardHistoryController.php b/src/applications/dashboard/controller/PhabricatorDashboardHistoryController.php deleted file mode 100644 index ab303b23e6..0000000000 --- a/src/applications/dashboard/controller/PhabricatorDashboardHistoryController.php +++ /dev/null @@ -1,48 +0,0 @@ -getViewer(); - $id = $request->getURIData('id'); - - $dashboard_view_uri = $this->getApplicationURI('view/'.$id.'/'); - $dashboard_manage_uri = $this->getApplicationURI('manage/'.$id.'/'); - - $dashboard = id(new PhabricatorDashboardQuery()) - ->setViewer($viewer) - ->withIDs(array($id)) - ->executeOne(); - if (!$dashboard) { - return new Aphront404Response(); - } - - $title = $dashboard->getName(); - - $crumbs = $this->buildApplicationCrumbs(); - $crumbs->setBorder(true); - $crumbs->addTextCrumb( - pht('Dashboard %d', $dashboard->getID()), - $dashboard_view_uri); - $crumbs->addTextCrumb( - pht('Manage'), - $dashboard_manage_uri); - $crumbs->addTextCrumb(pht('History')); - - $timeline = $this->buildTransactionTimeline( - $dashboard, - new PhabricatorDashboardTransactionQuery()); - $timeline->setShouldTerminate(true); - - return $this->buildApplicationPage( - array( - $crumbs, - $timeline, - ), - array( - 'title' => $title, - )); - } - -} diff --git a/src/applications/dashboard/controller/PhabricatorDashboardManageController.php b/src/applications/dashboard/controller/PhabricatorDashboardManageController.php index 6600ad0d34..475716d18d 100644 --- a/src/applications/dashboard/controller/PhabricatorDashboardManageController.php +++ b/src/applications/dashboard/controller/PhabricatorDashboardManageController.php @@ -34,25 +34,25 @@ final class PhabricatorDashboardManageController pht('Dashboard %d', $dashboard->getID()), $dashboard_uri); $crumbs->addTextCrumb(pht('Manage')); + $crumbs->setBorder(true); $header = $this->buildHeaderView($dashboard); - $actions = $this->buildActionView($dashboard); + $curtain = $this->buildCurtainview($dashboard); $properties = $this->buildPropertyView($dashboard); - $properties->setActionList($actions); - $box = id(new PHUIObjectBoxView()) - ->setHeader($header) - ->addPropertyList($properties); + $timeline = $this->buildTransactionTimeline( + $dashboard, + new PhabricatorDashboardTransactionQuery()); + $info_view = null; if (!$can_edit) { $no_edit = pht( 'You do not have permission to edit this dashboard. If you want to '. 'make changes, make a copy first.'); - $box->setInfoView( - id(new PHUIInfoView()) - ->setSeverity(PHUIInfoView::SEVERITY_NOTICE) - ->setErrors(array($no_edit))); + $info_view = id(new PHUIInfoView()) + ->setSeverity(PHUIInfoView::SEVERITY_NOTICE) + ->setErrors(array($no_edit)); } $rendered_dashboard = id(new PhabricatorDashboardRenderingEngine()) @@ -61,19 +61,30 @@ final class PhabricatorDashboardManageController ->setArrangeMode($can_edit) ->renderDashboard(); - return $this->buildApplicationPage( - array( - $crumbs, - $box, - $rendered_dashboard, - ), - array( - 'title' => $title, - )); + $dashboard_box = id(new PHUIBoxView()) + ->addClass('dashboard-preview-box') + ->appendChild($rendered_dashboard); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setCurtain($curtain) + ->setMainColumn(array( + $info_view, + $properties, + $timeline, + )) + ->setFooter($dashboard_box); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); + } private function buildHeaderView(PhabricatorDashboard $dashboard) { - $viewer = $this->getRequest()->getUser(); + $viewer = $this->getViewer(); + $id = $dashboard->getID(); if ($dashboard->isArchived()) { $status_icon = 'fa-ban'; @@ -87,33 +98,33 @@ final class PhabricatorDashboardManageController PhabricatorDashboard::getStatusNameMap(), $dashboard->getStatus()); + $button = id(new PHUIButtonView()) + ->setTag('a') + ->setText(pht('View Dashboard')) + ->setIcon('fa-columns') + ->setHref($this->getApplicationURI("view/{$id}/")); + return id(new PHUIHeaderView()) ->setUser($viewer) ->setHeader($dashboard->getName()) ->setPolicyObject($dashboard) - ->setStatus($status_icon, $status_color, $status_name); + ->setStatus($status_icon, $status_color, $status_name) + ->setHeaderIcon('fa-dashboard') + ->addActionLink($button); } - private function buildActionView(PhabricatorDashboard $dashboard) { - $viewer = $this->getRequest()->getUser(); + private function buildCurtainView(PhabricatorDashboard $dashboard) { + $viewer = $this->getViewer(); $id = $dashboard->getID(); - $actions = id(new PhabricatorActionListView()) - ->setObject($dashboard) - ->setUser($viewer); + $curtain = $this->newCurtainView($dashboard); $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, $dashboard, PhabricatorPolicyCapability::CAN_EDIT); - $actions->addAction( - id(new PhabricatorActionView()) - ->setName(pht('View Dashboard')) - ->setIcon('fa-columns') - ->setHref($this->getApplicationURI("view/{$id}/"))); - - $actions->addAction( + $curtain->addAction( id(new PhabricatorActionView()) ->setName(pht('Edit Dashboard')) ->setIcon('fa-pencil') @@ -121,7 +132,7 @@ final class PhabricatorDashboardManageController ->setDisabled(!$can_edit)); if ($dashboard->isArchived()) { - $actions->addAction( + $curtain->addAction( id(new PhabricatorActionView()) ->setName(pht('Activate Dashboard')) ->setIcon('fa-check') @@ -129,7 +140,7 @@ final class PhabricatorDashboardManageController ->setDisabled(!$can_edit) ->setWorkflow($can_edit)); } else { - $actions->addAction( + $curtain->addAction( id(new PhabricatorActionView()) ->setName(pht('Archive Dashboard')) ->setIcon('fa-ban') @@ -138,7 +149,7 @@ final class PhabricatorDashboardManageController ->setWorkflow($can_edit)); } - $actions->addAction( + $curtain->addAction( id(new PhabricatorActionView()) ->setName(pht('Copy Dashboard')) ->setIcon('fa-files-o') @@ -158,28 +169,21 @@ final class PhabricatorDashboardManageController $title_install = pht('Install Dashboard'); $href_install = "install/{$id}/"; } - $actions->addAction( + $curtain->addAction( id(new PhabricatorActionView()) ->setName($title_install) ->setIcon('fa-wrench') ->setHref($this->getApplicationURI($href_install)) ->setWorkflow(true)); - $actions->addAction( - id(new PhabricatorActionView()) - ->setName(pht('View History')) - ->setIcon('fa-history') - ->setHref($this->getApplicationURI("history/{$id}/"))); - - return $actions; + return $curtain; } private function buildPropertyView(PhabricatorDashboard $dashboard) { - $viewer = $this->getRequest()->getUser(); + $viewer = $this->getViewer(); $properties = id(new PHUIPropertyListView()) - ->setUser($viewer) - ->setObject($dashboard); + ->setUser($viewer); $descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions( $viewer, @@ -193,9 +197,10 @@ final class PhabricatorDashboardManageController pht('Panels'), $viewer->renderHandleList($dashboard->getPanelPHIDs())); - $properties->invokeWillRenderEvent(); - - return $properties; + return id(new PHUIObjectBoxView()) + ->setHeaderText(pht('DETAILS')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->addPropertyList($properties); } } diff --git a/src/applications/dashboard/controller/PhabricatorDashboardPanelEditController.php b/src/applications/dashboard/controller/PhabricatorDashboardPanelEditController.php index beccd3551a..790b8d11da 100644 --- a/src/applications/dashboard/controller/PhabricatorDashboardPanelEditController.php +++ b/src/applications/dashboard/controller/PhabricatorDashboardPanelEditController.php @@ -90,18 +90,18 @@ final class PhabricatorDashboardPanelEditController } if ($is_create) { - $title = pht('New Panel'); - $header = pht('Create New Panel'); + $title = pht('Create New Panel'); $button = pht('Create Panel'); + $header_icon = 'fa-plus-square'; if ($dashboard) { $cancel_uri = $manage_uri; } else { $cancel_uri = $this->getApplicationURI('panel/'); } } else { - $title = pht('Edit %s', $panel->getMonogram()); - $header = pht('Edit %s %s', $panel->getMonogram(), $panel->getName()); + $title = pht('Edit Panel: %s', $panel->getName()); $button = pht('Save Panel'); + $header_icon = 'fa-pencil'; if ($dashboard) { $cancel_uri = $manage_uri; } else { @@ -260,10 +260,11 @@ final class PhabricatorDashboardPanelEditController '/'.$panel->getMonogram()); $crumbs->addTextCrumb(pht('Edit')); } + $crumbs->setBorder(true); if ($request->isAjax()) { return $this->newDialog() - ->setTitle($header) + ->setTitle($title) ->setSubmitURI($submit_uri) ->setWidth(AphrontDialogView::WIDTH_FORM) ->setValidationException($validation_exception) @@ -279,18 +280,23 @@ final class PhabricatorDashboardPanelEditController } $box = id(new PHUIObjectBoxView()) - ->setHeaderText($header) + ->setHeaderText(pht('Panel')) ->setValidationException($validation_exception) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setForm($form); - return $this->buildApplicationPage( - array( - $crumbs, - $box, - ), - array( - 'title' => $title, - )); + $header = id(new PHUIHeaderView()) + ->setHeader($title) + ->setHeaderIcon($header_icon); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter($box); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); } private function processPanelTypeRequest(AphrontRequest $request) { @@ -349,26 +355,33 @@ final class PhabricatorDashboardPanelEditController } $title = pht('Create Dashboard Panel'); + $header_icon = 'fa-plus-square'; $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb( pht('Panels'), $this->getApplicationURI('panel/')); $crumbs->addTextCrumb(pht('New Panel')); + $crumbs->setBorder(true); $box = id(new PHUIObjectBoxView()) - ->setHeaderText($title) + ->setHeaderText(pht('Panel')) ->setFormErrors($errors) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setForm($form); - return $this->buildApplicationPage( - array( - $crumbs, - $box, - ), - array( - 'title' => $title, - )); + $header = id(new PHUIHeaderView()) + ->setHeader($title) + ->setHeaderIcon($header_icon); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter($box); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); } private function processPanelCloneRequest( diff --git a/src/applications/dashboard/controller/PhabricatorDashboardPanelRenderController.php b/src/applications/dashboard/controller/PhabricatorDashboardPanelRenderController.php index b1718421ea..0a0e7e30c0 100644 --- a/src/applications/dashboard/controller/PhabricatorDashboardPanelRenderController.php +++ b/src/applications/dashboard/controller/PhabricatorDashboardPanelRenderController.php @@ -50,20 +50,18 @@ final class PhabricatorDashboardPanelRenderController $crumbs = $this->buildApplicationCrumbs() ->addTextCrumb(pht('Panels'), $this->getApplicationURI('panel/')) ->addTextCrumb($panel->getMonogram(), '/'.$panel->getMonogram()) - ->addTextCrumb(pht('Standalone View')); + ->addTextCrumb(pht('Standalone View')) + ->setBorder(true); $view = id(new PHUIBoxView()) ->addClass('dashboard-view') ->appendChild($rendered_panel); - return $this->buildApplicationPage( - array( - $crumbs, - $view, - ), - array( - 'title' => array(pht('Panel'), $panel->getName()), - )); + return $this->newPage() + ->setTitle(array(pht('Panel'), $panel->getName())) + ->setCrumbs($crumbs) + ->appendChild($view); + } } diff --git a/src/applications/dashboard/controller/PhabricatorDashboardPanelViewController.php b/src/applications/dashboard/controller/PhabricatorDashboardPanelViewController.php index bc13374596..b16f60abd7 100644 --- a/src/applications/dashboard/controller/PhabricatorDashboardPanelViewController.php +++ b/src/applications/dashboard/controller/PhabricatorDashboardPanelViewController.php @@ -25,19 +25,15 @@ final class PhabricatorDashboardPanelViewController pht('Panels'), $this->getApplicationURI('panel/')); $crumbs->addTextCrumb($panel->getMonogram()); + $crumbs->setBorder(true); $header = $this->buildHeaderView($panel); - $actions = $this->buildActionView($panel); + $curtain = $this->buildCurtainView($panel); $properties = $this->buildPropertyView($panel); + $timeline = $this->buildTransactionTimeline( $panel, new PhabricatorDashboardPanelTransactionQuery()); - $timeline->setShouldTerminate(true); - - $properties->setActionList($actions); - $box = id(new PHUIObjectBoxView()) - ->setHeader($header) - ->addPropertyList($properties); $rendered_panel = id(new PhabricatorDashboardPanelRenderingEngine()) ->setViewer($viewer) @@ -45,31 +41,41 @@ final class PhabricatorDashboardPanelViewController ->setParentPanelPHIDs(array()) ->renderPanel(); - $view = id(new PHUIBoxView()) - ->addMargin(PHUI::MARGIN_LARGE_LEFT) - ->addMargin(PHUI::MARGIN_LARGE_RIGHT) - ->addMargin(PHUI::MARGIN_LARGE_TOP) + $preview = id(new PHUIBoxView()) + ->addClass('dashboard-preview-box') ->appendChild($rendered_panel); - return $this->buildApplicationPage( - array( - $crumbs, - $box, - $view, + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setCurtain($curtain) + ->setMainColumn(array( + $properties, $timeline, - ), - array( - 'title' => $title, - )); + )) + ->setFooter($rendered_panel); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); } private function buildHeaderView(PhabricatorDashboardPanel $panel) { - $viewer = $this->getRequest()->getUser(); + $viewer = $this->getViewer(); + $id = $panel->getID(); + + $button = id(new PHUIButtonView()) + ->setTag('a') + ->setText(pht('View Panel')) + ->setIcon('fa-columns') + ->setHref($this->getApplicationURI("panel/render/{$id}/")); $header = id(new PHUIHeaderView()) ->setUser($viewer) ->setHeader($panel->getName()) - ->setPolicyObject($panel); + ->setPolicyObject($panel) + ->setHeaderIcon('fa-columns') + ->addActionLink($button); if (!$panel->getIsArchived()) { $header->setStatus('fa-check', 'bluegrey', pht('Active')); @@ -79,20 +85,18 @@ final class PhabricatorDashboardPanelViewController return $header; } - private function buildActionView(PhabricatorDashboardPanel $panel) { - $viewer = $this->getRequest()->getUser(); + private function buildCurtainView(PhabricatorDashboardPanel $panel) { + $viewer = $this->getViewer(); $id = $panel->getID(); - $actions = id(new PhabricatorActionListView()) - ->setObject($panel) - ->setUser($viewer); + $curtain = $this->newCurtainView($panel); $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, $panel, PhabricatorPolicyCapability::CAN_EDIT); - $actions->addAction( + $curtain->addAction( id(new PhabricatorActionView()) ->setName(pht('Edit Panel')) ->setIcon('fa-pencil') @@ -108,7 +112,7 @@ final class PhabricatorDashboardPanelViewController $archive_icon = 'fa-check'; } - $actions->addAction( + $curtain->addAction( id(new PhabricatorActionView()) ->setName($archive_text) ->setIcon($archive_icon) @@ -116,21 +120,14 @@ final class PhabricatorDashboardPanelViewController ->setDisabled(!$can_edit) ->setWorkflow(true)); - $actions->addAction( - id(new PhabricatorActionView()) - ->setName(pht('View Standalone')) - ->setIcon('fa-eye') - ->setHref($this->getApplicationURI("panel/render/{$id}/"))); - - return $actions; + return $curtain; } private function buildPropertyView(PhabricatorDashboardPanel $panel) { - $viewer = $this->getRequest()->getUser(); + $viewer = $this->getViewer(); $properties = id(new PHUIPropertyListView()) - ->setUser($viewer) - ->setObject($panel); + ->setUser($viewer); $descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions( $viewer, @@ -167,7 +164,10 @@ final class PhabricatorDashboardPanelViewController ? $viewer->renderHandleList($dashboard_phids) : phutil_tag('em', array(), $does_not_appear)); - return $properties; + return id(new PHUIObjectBoxView()) + ->setHeaderText(pht('DETAILS')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->addPropertyList($properties); } } diff --git a/src/applications/dashboard/controller/PhabricatorDashboardViewController.php b/src/applications/dashboard/controller/PhabricatorDashboardViewController.php index 4caa68d2e5..dd2214df24 100644 --- a/src/applications/dashboard/controller/PhabricatorDashboardViewController.php +++ b/src/applications/dashboard/controller/PhabricatorDashboardViewController.php @@ -36,14 +36,10 @@ final class PhabricatorDashboardViewController $rendered_dashboard = $this->buildEmptyView(); } - return $this->buildApplicationPage( - array( - $crumbs, - $rendered_dashboard, - ), - array( - 'title' => $title, - )); + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($rendered_dashboard); } protected function buildApplicationCrumbs() { diff --git a/webroot/rsrc/css/application/dashboard/dashboard.css b/webroot/rsrc/css/application/dashboard/dashboard.css index 045e63eb8f..d8f4d3450e 100644 --- a/webroot/rsrc/css/application/dashboard/dashboard.css +++ b/webroot/rsrc/css/application/dashboard/dashboard.css @@ -55,8 +55,8 @@ .aphront-multi-column-column.dashboard-column-empty .dashboard-panel-placeholder { display: block; - padding: 24px; - margin: 16px 16px 0px 16px; + padding: 20px; + margin: 0 0 12px 0; text-decoration: none; border: 1px {$greyborder} dashed; color: {$greytext}; @@ -73,3 +73,9 @@ .aphront-multi-column-column .phui-info-view { margin: 0; } + +.dashboard-preview-box { + border: 1px solid {$lightblueborder}; + border-radius: 3px; + background-color: rgba(255,255,255,.33); +} From 13176e4185c8819fd751bf6e4a88f29af65134f1 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sat, 2 Apr 2016 13:47:30 -0700 Subject: [PATCH 13/84] Normalize Paste edit header Summary: I've been rolling out "Edit ObjectName: ObjectTitle" consistantly here. Test Plan: Review Paste edit title Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15586 --- src/applications/paste/editor/PhabricatorPasteEditEngine.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/paste/editor/PhabricatorPasteEditEngine.php b/src/applications/paste/editor/PhabricatorPasteEditEngine.php index 7916279f69..f88ab76697 100644 --- a/src/applications/paste/editor/PhabricatorPasteEditEngine.php +++ b/src/applications/paste/editor/PhabricatorPasteEditEngine.php @@ -35,7 +35,7 @@ final class PhabricatorPasteEditEngine } protected function getObjectEditTitleText($object) { - return pht('Edit %s %s', $object->getMonogram(), $object->getTitle()); + return pht('Edit Paste: %s', $object->getTitle()); } protected function getObjectEditShortText($object) { From 2992b5277a04e219537d5c09c0e010578c306d5f Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sat, 2 Apr 2016 13:33:07 -0700 Subject: [PATCH 14/84] Update Diviner to modern UI Summary: Moves to `newPage`, updates UI on edit page. Test Plan: Edit a book, view a book, main, list, search. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15585 --- .../controller/DivinerAtomController.php | 10 +++---- .../controller/DivinerBookController.php | 10 +++---- .../controller/DivinerBookEditController.php | 29 ++++++++++++------- .../controller/DivinerFindController.php | 9 +++--- .../controller/DivinerMainController.php | 10 +++---- 5 files changed, 36 insertions(+), 32 deletions(-) diff --git a/src/applications/diviner/controller/DivinerAtomController.php b/src/applications/diviner/controller/DivinerAtomController.php index 1785ae6741..bf69497b16 100644 --- a/src/applications/diviner/controller/DivinerAtomController.php +++ b/src/applications/diviner/controller/DivinerAtomController.php @@ -240,14 +240,12 @@ final class DivinerAtomController extends DivinerController { $prop_list = phutil_tag_div('phui-document-view-pro-box', $prop_list); - return $this->buildApplicationPage( - array( - $crumbs, + return $this->newPage() + ->setTitle($symbol->getTitle()) + ->setCrumbs($crumbs) + ->appendChild(array( $document, $prop_list, - ), - array( - 'title' => $symbol->getTitle(), )); } diff --git a/src/applications/diviner/controller/DivinerBookController.php b/src/applications/diviner/controller/DivinerBookController.php index 74b2a8f3d9..1aa8790ffa 100644 --- a/src/applications/diviner/controller/DivinerBookController.php +++ b/src/applications/diviner/controller/DivinerBookController.php @@ -92,13 +92,11 @@ final class DivinerBookController extends DivinerController { $document->appendChild($preface_view); $document->appendChild($out); - return $this->buildApplicationPage( - array( - $crumbs, + return $this->newPage() + ->setTitle($book->getTitle()) + ->setCrumbs($crumbs) + ->appendChild(array( $document, - ), - array( - 'title' => $book->getTitle(), )); } diff --git a/src/applications/diviner/controller/DivinerBookEditController.php b/src/applications/diviner/controller/DivinerBookEditController.php index b5c3e2dea5..2b1f90579b 100644 --- a/src/applications/diviner/controller/DivinerBookEditController.php +++ b/src/applications/diviner/controller/DivinerBookEditController.php @@ -57,8 +57,10 @@ final class DivinerBookEditController extends DivinerController { $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Edit Basics')); + $crumbs->setBorder(true); - $title = pht('Edit %s', $book->getTitle()); + $title = pht('Edit Book: %s', $book->getTitle()); + $header_icon = 'fa-pencil'; $policies = id(new PhabricatorPolicyQuery()) ->setViewer($viewer) @@ -104,8 +106,9 @@ final class DivinerBookEditController extends DivinerController { ->setValue(pht('Save')) ->addCancelButton($view_uri)); - $object_box = id(new PHUIObjectBoxView()) - ->setHeaderText($title) + $box = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Book')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setForm($form); $timeline = $this->buildTransactionTimeline( @@ -113,15 +116,21 @@ final class DivinerBookEditController extends DivinerController { new DivinerLiveBookTransactionQuery()); $timeline->setShouldTerminate(true); - return $this->buildApplicationPage( - array( - $crumbs, - $object_box, + $header = id(new PHUIHeaderView()) + ->setHeader($title) + ->setHeaderIcon($header_icon); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter(array( + $box, $timeline, - ), - array( - 'title' => $title, )); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); } } diff --git a/src/applications/diviner/controller/DivinerFindController.php b/src/applications/diviner/controller/DivinerFindController.php index b9e165fc1d..1bedd65b06 100644 --- a/src/applications/diviner/controller/DivinerFindController.php +++ b/src/applications/diviner/controller/DivinerFindController.php @@ -84,11 +84,12 @@ final class DivinerFindController extends DivinerController { $list = $this->renderAtomList($atoms); - return $this->buildApplicationPage( - $list, - array( - 'title' => array(pht('Find'), pht('"%s"', $query_text)), + return $this->newPage() + ->setTitle(array(pht('Find'), pht('"%s"', $query_text))) + ->appendChild(array( + $list, )); + } } diff --git a/src/applications/diviner/controller/DivinerMainController.php b/src/applications/diviner/controller/DivinerMainController.php index e85769060f..e7d179c077 100644 --- a/src/applications/diviner/controller/DivinerMainController.php +++ b/src/applications/diviner/controller/DivinerMainController.php @@ -65,13 +65,11 @@ final class DivinerMainController extends DivinerController { $document->appendChild($text); } - return $this->buildApplicationPage( - array( - $crumbs, + return $this->newPage() + ->setTitle(pht('Documentation Books')) + ->setCrumbs($crumbs) + ->appendChild(array( $document, - ), - array( - 'title' => pht('Documentation Books'), )); } } From bdeb5cf141f6b0dcb0bf935370029a2ff4ebd1bb Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sat, 2 Apr 2016 13:16:51 -0700 Subject: [PATCH 15/84] Update Multimeter to new UI Summary: Converts to two column UI Test Plan: Review a multiple sample page Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15584 --- .../controller/MultimeterSampleController.php | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/applications/multimeter/controller/MultimeterSampleController.php b/src/applications/multimeter/controller/MultimeterSampleController.php index f9a36b37d1..0023038185 100644 --- a/src/applications/multimeter/controller/MultimeterSampleController.php +++ b/src/applications/multimeter/controller/MultimeterSampleController.php @@ -230,17 +230,15 @@ final class MultimeterSampleController extends MultimeterController { )); $box = id(new PHUIObjectBoxView()) - ->setHeaderText( - pht( - 'Samples (%s - %s)', - phabricator_datetime($ago, $viewer), - phabricator_datetime($now, $viewer))) + ->setHeaderText(pht('Samples')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setTable($table); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb( pht('Samples'), $this->getGroupURI(array(), true)); + $crumbs->setBorder(true); $crumb_map = array( 'host' => pht('By Host'), @@ -262,14 +260,23 @@ final class MultimeterSampleController extends MultimeterController { $this->getGroupURI($parts, true)); } - return $this->buildApplicationPage( - array( - $crumbs, - $box, - ), - array( - 'title' => pht('Samples'), - )); + $header = id(new PHUIHeaderView()) + ->setHeader( + pht( + 'Samples (%s - %s)', + phabricator_datetime($ago, $viewer), + phabricator_datetime($now, $viewer))) + ->setHeaderIcon('fa-motorcycle'); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter($box); + + return $this->newPage() + ->setTitle(pht('Samples')) + ->setCrumbs($crumbs) + ->appendChild($view); + } private function renderGroupingLink(array $group, $key, $name = null) { From 72d12be8500c0f515c0cbc3272a37a0fd14ba74f Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sun, 3 Apr 2016 00:25:03 +0000 Subject: [PATCH 16/84] Update Legalpad with modern UI Summary: Updates Legalpad Manage/Edit with new UI layouts. Test Plan: Wrote a new document with and without a preamble, edit document, sign document Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15576 --- .../LegalpadDocumentEditController.php | 30 +++-- .../LegalpadDocumentManageController.php | 108 ++++++++++-------- 2 files changed, 79 insertions(+), 59 deletions(-) diff --git a/src/applications/legalpad/controller/LegalpadDocumentEditController.php b/src/applications/legalpad/controller/LegalpadDocumentEditController.php index 84a879a7a2..3804885edb 100644 --- a/src/applications/legalpad/controller/LegalpadDocumentEditController.php +++ b/src/applications/legalpad/controller/LegalpadDocumentEditController.php @@ -220,27 +220,30 @@ final class LegalpadDocumentEditController extends LegalpadController { $submit->addCancelButton($this->getApplicationURI()); $title = pht('Create Document'); $short = pht('Create'); + $header_icon = 'fa-plus-square'; } else { $submit->setValue(pht('Save Document')); $submit->addCancelButton( $this->getApplicationURI('view/'.$document->getID())); - $title = pht('Edit Document'); + $title = pht('Edit Document: %s', $document->getTitle()); $short = pht('Edit'); + $header_icon = 'fa-pencil'; $crumbs->addTextCrumb( $document->getMonogram(), $this->getApplicationURI('view/'.$document->getID())); } - $form - ->appendChild($submit); + $form->appendChild($submit); $form_box = id(new PHUIObjectBoxView()) - ->setHeaderText($title) + ->setHeaderText(pht('Document')) ->setFormErrors($errors) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setForm($form); $crumbs->addTextCrumb($short); + $crumbs->setBorder(true); $preview = id(new PHUIRemarkupPreviewPanel()) ->setHeader($document->getTitle()) @@ -248,15 +251,22 @@ final class LegalpadDocumentEditController extends LegalpadController { ->setControlID('document-text') ->setPreviewType(PHUIRemarkupPreviewPanel::DOCUMENT); - return $this->buildApplicationPage( - array( - $crumbs, + $header = id(new PHUIHeaderView()) + ->setHeader($title) + ->setHeaderIcon($header_icon); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter(array( $form_box, $preview, - ), - array( - 'title' => $title, )); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); + } } diff --git a/src/applications/legalpad/controller/LegalpadDocumentManageController.php b/src/applications/legalpad/controller/LegalpadDocumentManageController.php index baeefeaf4b..134dada128 100644 --- a/src/applications/legalpad/controller/LegalpadDocumentManageController.php +++ b/src/applications/legalpad/controller/LegalpadDocumentManageController.php @@ -43,10 +43,12 @@ final class LegalpadDocumentManageController extends LegalpadController { $header = id(new PHUIHeaderView()) ->setHeader($title) ->setUser($viewer) - ->setPolicyObject($document); + ->setPolicyObject($document) + ->setHeaderIcon('fa-gavel'); - $actions = $this->buildActionView($document); - $properties = $this->buildPropertyView($document, $engine, $actions); + $curtain = $this->buildCurtainView($document); + $properties = $this->buildPropertyView($document, $engine); + $document_view = $this->buildDocumentView($document, $engine); $comment_form_id = celerity_generate_unique_node_id(); @@ -57,48 +59,58 @@ final class LegalpadDocumentManageController extends LegalpadController { $document->getMonogram(), '/'.$document->getMonogram()); $crumbs->addTextCrumb(pht('Manage')); + $crumbs->setBorder(true); - $object_box = id(new PHUIObjectBoxView()) + + $view = id(new PHUITwoColumnView()) ->setHeader($header) - ->addPropertyList($properties) - ->addPropertyList($this->buildDocument($engine, $document_body)); - - $content = array( - $crumbs, - $object_box, - $timeline, - $add_comment, - ); - - return $this->buildApplicationPage( - $content, - array( - 'title' => $title, - 'pageObjects' => array($document->getPHID()), + ->setCurtain($curtain) + ->setMainColumn(array( + $properties, + $document_view, + $timeline, + $add_comment, )); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->setPageObjectPHIDs(array($document->getPHID())) + ->appendChild($view); } - private function buildDocument( - PhabricatorMarkupEngine - $engine, LegalpadDocumentBody $body) { + private function buildDocumentView( + LegalpadDocument $document, + PhabricatorMarkupEngine $engine) { - $view = new PHUIPropertyListView(); - $view->addClass('legalpad'); - $view->addSectionHeader( - pht('Document'), 'fa-file-text-o'); - $view->addTextContent( - $engine->getOutput($body, LegalpadDocumentBody::MARKUP_FIELD_TEXT)); - - return $view; - - } - - private function buildActionView(LegalpadDocument $document) { $viewer = $this->getViewer(); - $actions = id(new PhabricatorActionListView()) - ->setUser($viewer) - ->setObject($document); + $view = id(new PHUIPropertyListView()) + ->setUser($viewer); + $document_body = $document->getDocumentBody(); + $document_text = $engine->getOutput( + $document_body, LegalpadDocumentBody::MARKUP_FIELD_TEXT); + + $preamble_box = null; + if (strlen($document->getPreamble())) { + $preamble_text = new PHUIRemarkupView($viewer, $document->getPreamble()); + $view->addTextContent($preamble_text); + $view->addSectionHeader(''); + $view->addTextContent($document_text); + } else { + $view->addTextContent($document_text); + } + + return id(new PHUIObjectBoxView()) + ->setHeaderText(pht('DOCUMENT')) + ->addPropertyList($view) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY); + } + + private function buildCurtainView(LegalpadDocument $document) { + $viewer = $this->getViewer(); + + $curtain = $this->newCurtainView($document); $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, @@ -107,13 +119,13 @@ final class LegalpadDocumentManageController extends LegalpadController { $doc_id = $document->getID(); - $actions->addAction( + $curtain->addAction( id(new PhabricatorActionView()) ->setIcon('fa-pencil-square') ->setName(pht('View/Sign Document')) ->setHref('/'.$document->getMonogram())); - $actions->addAction( + $curtain->addAction( id(new PhabricatorActionView()) ->setIcon('fa-pencil') ->setName(pht('Edit Document')) @@ -121,26 +133,23 @@ final class LegalpadDocumentManageController extends LegalpadController { ->setDisabled(!$can_edit) ->setWorkflow(!$can_edit)); - $actions->addAction( + $curtain->addAction( id(new PhabricatorActionView()) ->setIcon('fa-terminal') ->setName(pht('View Signatures')) ->setHref($this->getApplicationURI('/signatures/'.$doc_id.'/'))); - return $actions; + return $curtain; } private function buildPropertyView( LegalpadDocument $document, - PhabricatorMarkupEngine $engine, - PhabricatorActionListView $actions) { + PhabricatorMarkupEngine $engine) { $viewer = $this->getViewer(); $properties = id(new PHUIPropertyListView()) - ->setUser($viewer) - ->setObject($document) - ->setActionList($actions); + ->setUser($viewer); $properties->addProperty( pht('Signature Type'), @@ -166,9 +175,10 @@ final class LegalpadDocumentManageController extends LegalpadController { ->setAsInline(true)); } - $properties->invokeWillRenderEvent(); - - return $properties; + return id(new PHUIObjectBoxView()) + ->setHeaderText(pht('PROPERTIES')) + ->addPropertyList($properties) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY); } private function buildAddCommentView( From 839e7e2fc6e61b0721802e881d0690934c76a880 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sun, 3 Apr 2016 00:27:39 +0000 Subject: [PATCH 17/84] Update Config to new UI Summary: Converts Config to new UI, updates to `newPage` Test Plan: Review all pages in Config, setup issues, ignore an issue, edit a config option Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15587 --- .../PhabricatorConfigAllController.php | 8 ++---- .../PhabricatorConfigCacheController.php | 8 ++---- ...abricatorConfigDatabaseIssueController.php | 8 ++---- ...bricatorConfigDatabaseStatusController.php | 8 ++---- .../PhabricatorConfigEditController.php | 28 +++++++++++-------- .../PhabricatorConfigGroupController.php | 24 +++++++++------- .../PhabricatorConfigHistoryController.php | 10 ++----- .../PhabricatorConfigIgnoreController.php | 4 +-- .../PhabricatorConfigIssueListController.php | 8 ++---- .../PhabricatorConfigIssueViewController.php | 12 +++----- .../PhabricatorConfigListController.php | 8 ++---- .../PhabricatorConfigModuleController.php | 12 ++++---- .../PhabricatorConfigWelcomeController.php | 8 ++---- 13 files changed, 65 insertions(+), 81 deletions(-) diff --git a/src/applications/config/controller/PhabricatorConfigAllController.php b/src/applications/config/controller/PhabricatorConfigAllController.php index d805168eb1..7f15273b88 100644 --- a/src/applications/config/controller/PhabricatorConfigAllController.php +++ b/src/applications/config/controller/PhabricatorConfigAllController.php @@ -64,12 +64,10 @@ final class PhabricatorConfigAllController $nav->setCrumbs($crumbs); $nav->appendChild($panel); + return $this->newPage() + ->setTitle($title) + ->appendChild($nav); - return $this->buildApplicationPage( - $nav, - array( - 'title' => $title, - )); } } diff --git a/src/applications/config/controller/PhabricatorConfigCacheController.php b/src/applications/config/controller/PhabricatorConfigCacheController.php index 67fbf6e120..0f5681dea6 100644 --- a/src/applications/config/controller/PhabricatorConfigCacheController.php +++ b/src/applications/config/controller/PhabricatorConfigCacheController.php @@ -25,11 +25,9 @@ final class PhabricatorConfigCacheController $data_box, )); - return $this->buildApplicationPage( - $nav, - array( - 'title' => $title, - )); + return $this->newPage() + ->setTitle($title) + ->appendChild($nav); } private function renderCodeBox() { diff --git a/src/applications/config/controller/PhabricatorConfigDatabaseIssueController.php b/src/applications/config/controller/PhabricatorConfigDatabaseIssueController.php index 22df5104e4..32ec2fa70b 100644 --- a/src/applications/config/controller/PhabricatorConfigDatabaseIssueController.php +++ b/src/applications/config/controller/PhabricatorConfigDatabaseIssueController.php @@ -159,11 +159,9 @@ final class PhabricatorConfigDatabaseIssueController $table_box, )); - return $this->buildApplicationPage( - $nav, - array( - 'title' => $title, - )); + return $this->newPage() + ->setTitle($title) + ->appendChild($nav); } } diff --git a/src/applications/config/controller/PhabricatorConfigDatabaseStatusController.php b/src/applications/config/controller/PhabricatorConfigDatabaseStatusController.php index d886e98d74..1b2f9dbcb4 100644 --- a/src/applications/config/controller/PhabricatorConfigDatabaseStatusController.php +++ b/src/applications/config/controller/PhabricatorConfigDatabaseStatusController.php @@ -94,11 +94,9 @@ final class PhabricatorConfigDatabaseStatusController $nav->setCrumbs($crumbs); $nav->appendChild($body); - return $this->buildApplicationPage( - $nav, - array( - 'title' => $title, - )); + return $this->newPage() + ->setTitle($title) + ->appendChild($nav); } diff --git a/src/applications/config/controller/PhabricatorConfigEditController.php b/src/applications/config/controller/PhabricatorConfigEditController.php index 0c93c86f09..ab246b56ce 100644 --- a/src/applications/config/controller/PhabricatorConfigEditController.php +++ b/src/applications/config/controller/PhabricatorConfigEditController.php @@ -227,11 +227,13 @@ final class PhabricatorConfigEditController ->setValue($examples)); } - $title = pht('Edit %s', $key); + $title = pht('Edit Option: %s', $key); + $header_icon = 'fa-pencil'; $short = pht('Edit'); $form_box = id(new PHUIObjectBoxView()) - ->setHeaderText($title) + ->setHeaderText(pht('Config Option')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setForm($form); if ($error_view) { @@ -246,21 +248,25 @@ final class PhabricatorConfigEditController } $crumbs->addTextCrumb($key, '/config/edit/'.$key); + $crumbs->setBorder(true); $timeline = $this->buildTransactionTimeline( $config_entry, new PhabricatorConfigTransactionQuery()); $timeline->setShouldTerminate(true); - return $this->buildApplicationPage( - array( - $crumbs, - $form_box, - $timeline, - ), - array( - 'title' => $title, - )); + $header = id(new PHUIHeaderView()) + ->setHeader($title) + ->setHeaderIcon($header_icon); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter($form_box); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); } private function readRequest( diff --git a/src/applications/config/controller/PhabricatorConfigGroupController.php b/src/applications/config/controller/PhabricatorConfigGroupController.php index 4569491427..af5ed41222 100644 --- a/src/applications/config/controller/PhabricatorConfigGroupController.php +++ b/src/applications/config/controller/PhabricatorConfigGroupController.php @@ -17,22 +17,26 @@ final class PhabricatorConfigGroupController $list = $this->buildOptionList($options->getOptions()); $box = id(new PHUIObjectBoxView()) - ->setHeaderText($title) ->setObjectList($list); $crumbs = $this ->buildApplicationCrumbs() ->addTextCrumb(pht('Config'), $this->getApplicationURI()) - ->addTextCrumb($options->getName(), $this->getApplicationURI()); + ->addTextCrumb($options->getName(), $this->getApplicationURI()) + ->setBorder(true); - return $this->buildApplicationPage( - array( - $crumbs, - $box, - ), - array( - 'title' => $title, - )); + $header = id(new PHUIHeaderView()) + ->setHeader($title) + ->setHeaderIcon('fa-sliders'); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter($box); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); } private function buildOptionList(array $options) { diff --git a/src/applications/config/controller/PhabricatorConfigHistoryController.php b/src/applications/config/controller/PhabricatorConfigHistoryController.php index d86fb79878..9fc538332f 100644 --- a/src/applications/config/controller/PhabricatorConfigHistoryController.php +++ b/src/applications/config/controller/PhabricatorConfigHistoryController.php @@ -40,13 +40,9 @@ final class PhabricatorConfigHistoryController $nav->setCrumbs($crumbs); $nav->appendChild($timeline); - return $this->buildApplicationPage( - array( - $nav, - ), - array( - 'title' => $title, - )); + return $this->newPage() + ->setTitle($title) + ->appendChild($nav); } } diff --git a/src/applications/config/controller/PhabricatorConfigIgnoreController.php b/src/applications/config/controller/PhabricatorConfigIgnoreController.php index 80a859c147..cfe5a225ef 100644 --- a/src/applications/config/controller/PhabricatorConfigIgnoreController.php +++ b/src/applications/config/controller/PhabricatorConfigIgnoreController.php @@ -32,14 +32,12 @@ final class PhabricatorConfigIgnoreController throw new Exception(pht('Unrecognized verb: %s', $verb)); } - $dialog = id(new AphrontDialogView()) - ->setUser($request->getUser()) + return $this->newDialog() ->setTitle($title) ->appendChild($body) ->addSubmitButton($submit_title) ->addCancelButton($issue_uri); - return id(new AphrontDialogResponse())->setDialog($dialog); } public function manageApplication($issue) { diff --git a/src/applications/config/controller/PhabricatorConfigIssueListController.php b/src/applications/config/controller/PhabricatorConfigIssueListController.php index 89b8ea7cd6..5f30a0411d 100644 --- a/src/applications/config/controller/PhabricatorConfigIssueListController.php +++ b/src/applications/config/controller/PhabricatorConfigIssueListController.php @@ -66,11 +66,9 @@ final class PhabricatorConfigIssueListController $nav->setCrumbs($crumbs); - return $this->buildApplicationPage( - $nav, - array( - 'title' => $title, - )); + return $this->newPage() + ->setTitle($title) + ->appendChild($nav); } private function buildIssueList(array $issues, $group) { diff --git a/src/applications/config/controller/PhabricatorConfigIssueViewController.php b/src/applications/config/controller/PhabricatorConfigIssueViewController.php index e8d6e188a4..b1d1c299a5 100644 --- a/src/applications/config/controller/PhabricatorConfigIssueViewController.php +++ b/src/applications/config/controller/PhabricatorConfigIssueViewController.php @@ -36,14 +36,10 @@ final class PhabricatorConfigIssueViewController ->addTextCrumb(pht('Setup Issues'), $this->getApplicationURI('issue/')) ->addTextCrumb($title, $request->getRequestURI()); - return $this->buildApplicationPage( - array( - $crumbs, - $content, - ), - array( - 'title' => $title, - )); + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($content); } private function renderIssue(PhabricatorSetupIssue $issue) { diff --git a/src/applications/config/controller/PhabricatorConfigListController.php b/src/applications/config/controller/PhabricatorConfigListController.php index 9e87995ebc..220d47cfcd 100644 --- a/src/applications/config/controller/PhabricatorConfigListController.php +++ b/src/applications/config/controller/PhabricatorConfigListController.php @@ -35,11 +35,9 @@ final class PhabricatorConfigListController $nav->setCrumbs($crumbs); - return $this->buildApplicationPage( - $nav, - array( - 'title' => $title, - )); + return $this->newPage() + ->setTitle($title) + ->appendChild($nav); } private function buildConfigOptionsList(array $groups, $type) { diff --git a/src/applications/config/controller/PhabricatorConfigModuleController.php b/src/applications/config/controller/PhabricatorConfigModuleController.php index 76343ecc11..fee5cb9756 100644 --- a/src/applications/config/controller/PhabricatorConfigModuleController.php +++ b/src/applications/config/controller/PhabricatorConfigModuleController.php @@ -14,10 +14,10 @@ final class PhabricatorConfigModuleController $module = $all_modules[$key]; $content = $module->renderModuleStatus($request); - $name = $module->getModuleName(); + $title = $module->getModuleName(); $crumbs = $this->buildApplicationCrumbs(); - $crumbs->addTextCrumb($name); + $crumbs->addTextCrumb($title); $nav = $this->buildSideNavView(); $nav->selectFilter('module/'.$key.'/'); @@ -27,11 +27,9 @@ final class PhabricatorConfigModuleController $content, )); - return $this->buildApplicationPage( - $nav, - array( - 'title' => $name, - )); + return $this->newPage() + ->setTitle($title) + ->appendChild($nav); } } diff --git a/src/applications/config/controller/PhabricatorConfigWelcomeController.php b/src/applications/config/controller/PhabricatorConfigWelcomeController.php index e2d704e0b7..435ce6f01e 100644 --- a/src/applications/config/controller/PhabricatorConfigWelcomeController.php +++ b/src/applications/config/controller/PhabricatorConfigWelcomeController.php @@ -18,11 +18,9 @@ final class PhabricatorConfigWelcomeController $nav->setCrumbs($crumbs); $nav->appendChild($this->buildWelcomeScreen($request)); - return $this->buildApplicationPage( - $nav, - array( - 'title' => $title, - )); + return $this->newPage() + ->setTitle($title) + ->appendChild($nav); } public function buildWelcomeScreen(AphrontRequest $request) { From 219357aa9fa30c1f1a71ee0b36440b077d37852b Mon Sep 17 00:00:00 2001 From: lkassianik Date: Fri, 1 Apr 2016 17:52:52 -0700 Subject: [PATCH 18/84] Adding awarder info to badge cards displayed on user profile pages Summary: Ref T8940 Test Plan: Award badge, open recipient profile page, badge should appear in badges list, and flipping the badge card should show who awarded it. Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: chad, Korvin Maniphest Tasks: T8940 Differential Revision: https://secure.phabricator.com/D15570 --- ...PhabricatorPeopleProfileViewController.php | 42 ++++++++++++++----- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/src/applications/people/controller/PhabricatorPeopleProfileViewController.php b/src/applications/people/controller/PhabricatorPeopleProfileViewController.php index cb9c558d5a..06a2f02950 100644 --- a/src/applications/people/controller/PhabricatorPeopleProfileViewController.php +++ b/src/applications/people/controller/PhabricatorPeopleProfileViewController.php @@ -181,26 +181,48 @@ final class PhabricatorPeopleProfileViewController return null; } - $badge_phids = $user->getBadgePHIDs(); - if ($badge_phids) { + $awards = array(); + $badges = array(); + if ($user->getBadgePHIDs()) { + $awards = id(new PhabricatorBadgesAwardQuery()) + ->setViewer($viewer) + ->withRecipientPHIDs(array($user->getPHID())) + ->execute(); + $awards = mpull($awards, null, 'getBadgePHID'); + + $badge_phids = mpull($awards, 'getBadgePHID'); $badges = id(new PhabricatorBadgesQuery()) ->setViewer($viewer) ->withPHIDs($badge_phids) ->withStatuses(array(PhabricatorBadgesBadge::STATUS_ACTIVE)) ->execute(); - } else { - $badges = array(); + $badges = mpull($badges, null, 'getPHID'); } if (count($badges)) { $flex = new PHUIBadgeBoxView(); + foreach ($badges as $badge) { - $item = id(new PHUIBadgeView()) - ->setIcon($badge->getIcon()) - ->setHeader($badge->getName()) - ->setSubhead($badge->getFlavor()) - ->setQuality($badge->getQuality()); - $flex->addItem($item); + if ($badge) { + $awarder_info = array(); + + $award = idx($awards, $badge->getPHID(), null); + $awarder_phid = $award->getAwarderPHID(); + $awarder_handle = $viewer->renderHandle($awarder_phid); + + $awarder_info = pht( + 'Awarded by %s', + $awarder_handle->render()); + + $item = id(new PHUIBadgeView()) + ->setIcon($badge->getIcon()) + ->setHeader($badge->getName()) + ->setSubhead($badge->getFlavor()) + ->setQuality($badge->getQuality()) + ->addByLine($awarder_info); + + $flex->addItem($item); + } } } else { $error = id(new PHUIBoxView()) From e66bf175050e2f7f9b6ec0dd626840c37d608b3f Mon Sep 17 00:00:00 2001 From: lkassianik Date: Sat, 2 Apr 2016 18:51:23 -0700 Subject: [PATCH 19/84] Fixing the badges query, yet again Summary: Forgot a more efficient way to get badge from award Test Plan: Badges on user profiles should still show up with awarder handle on the back of the card Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15589 --- .../PhabricatorPeopleProfileViewController.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/applications/people/controller/PhabricatorPeopleProfileViewController.php b/src/applications/people/controller/PhabricatorPeopleProfileViewController.php index 06a2f02950..f77933df90 100644 --- a/src/applications/people/controller/PhabricatorPeopleProfileViewController.php +++ b/src/applications/people/controller/PhabricatorPeopleProfileViewController.php @@ -190,13 +190,13 @@ final class PhabricatorPeopleProfileViewController ->execute(); $awards = mpull($awards, null, 'getBadgePHID'); - $badge_phids = mpull($awards, 'getBadgePHID'); - $badges = id(new PhabricatorBadgesQuery()) - ->setViewer($viewer) - ->withPHIDs($badge_phids) - ->withStatuses(array(PhabricatorBadgesBadge::STATUS_ACTIVE)) - ->execute(); - $badges = mpull($badges, null, 'getPHID'); + $badges = array(); + foreach ($awards as $award) { + $badge = $award->getBadge(); + if ($badge->getStatus() == PhabricatorBadgesBadge::STATUS_ACTIVE) { + $badges[$award->getBadgePHID()] = $badge; + } + } } if (count($badges)) { From fa6151778fe84ee77b455e0c802fac7e84cfbf5b Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sat, 2 Apr 2016 19:51:53 -0700 Subject: [PATCH 20/84] Fix Legalpad "Sign" box Summary: Missed converting this page, scenario. The box was poorly formatted. Test Plan: Create a new document that needs signed, verify box is correctly spaced and colored. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15591 --- resources/celerity/map.php | 4 ++-- .../LegalpadDocumentSignController.php | 17 +++++++++-------- webroot/rsrc/css/phui/phui-document-pro.css | 4 ---- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 17293755c7..2b6873c89d 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -128,7 +128,7 @@ return array( 'rsrc/css/phui/phui-chart.css' => '6bf6f78e', 'rsrc/css/phui/phui-crumbs-view.css' => '79d536e5', 'rsrc/css/phui/phui-curtain-view.css' => '7148ae25', - 'rsrc/css/phui/phui-document-pro.css' => '92d5b648', + 'rsrc/css/phui/phui-document-pro.css' => '73e45fd2', 'rsrc/css/phui/phui-document-summary.css' => '9ca48bdf', 'rsrc/css/phui/phui-document.css' => '9c71d2bf', 'rsrc/css/phui/phui-feed-story.css' => '04aec08f', @@ -816,7 +816,7 @@ return array( 'phui-curtain-view-css' => '7148ae25', 'phui-document-summary-view-css' => '9ca48bdf', 'phui-document-view-css' => '9c71d2bf', - 'phui-document-view-pro-css' => '92d5b648', + 'phui-document-view-pro-css' => '73e45fd2', 'phui-feed-story-css' => '04aec08f', 'phui-font-icon-base-css' => '6449bce8', 'phui-fontkit-css' => '9cda225e', diff --git a/src/applications/legalpad/controller/LegalpadDocumentSignController.php b/src/applications/legalpad/controller/LegalpadDocumentSignController.php index 00549c1adc..70b750c38a 100644 --- a/src/applications/legalpad/controller/LegalpadDocumentSignController.php +++ b/src/applications/legalpad/controller/LegalpadDocumentSignController.php @@ -301,12 +301,15 @@ final class LegalpadDocumentSignController extends LegalpadController { case LegalpadDocument::SIGNATURE_TYPE_INDIVIDUAL: case LegalpadDocument::SIGNATURE_TYPE_CORPORATION: $box = id(new PHUIObjectBoxView()) + ->addClass('document-sign-box') ->setHeaderText(pht('Agree and Sign Document')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setForm($signature_form); if ($error_view) { $box->setInfoView($error_view); } - $signature_box = phutil_tag_div('phui-document-view-pro-box', $box); + $signature_box = phutil_tag_div( + 'phui-document-view-pro-box plt', $box); break; } @@ -317,15 +320,13 @@ final class LegalpadDocumentSignController extends LegalpadController { $crumbs->setBorder(true); $crumbs->addTextCrumb($document->getMonogram()); - return $this->buildApplicationPage( - array( - $crumbs, + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->setPageObjectPHIDs(array($document->getPHID())) + ->appendChild(array( $content, $signature_box, - ), - array( - 'title' => $title, - 'pageObjects' => array($document->getPHID()), )); } diff --git a/webroot/rsrc/css/phui/phui-document-pro.css b/webroot/rsrc/css/phui/phui-document-pro.css index c77ffa77e2..85f8150015 100644 --- a/webroot/rsrc/css/phui/phui-document-pro.css +++ b/webroot/rsrc/css/phui/phui-document-pro.css @@ -190,10 +190,6 @@ a.button.phui-document-toc { margin: 0; } -.phui-document-view-pro-box .phui-object-box .phui-form-view { - padding-bottom: 0; -} - .phui-document-view-pro-box .phui-object-box .remarkup-assist-textarea { height: 9em; } From 88d15ce79910e78ea94df53dda4ca2fc9f3dc9cd Mon Sep 17 00:00:00 2001 From: lkassianik Date: Sat, 2 Apr 2016 19:17:36 -0700 Subject: [PATCH 21/84] Adding awarder info to recipient list on badge view Summary: Closes T8940, recipient list in badge view should show awarder and date info. Took a first stab at how we want to make the date look, but not sure. Looks odd as it is. Test Plan: Open badge that has awards. Each recipient in list should have a subheader such as "Awarded by ... on ..." Reviewers: epriestley, #blessed_reviewers Reviewed By: epriestley, #blessed_reviewers Subscribers: Korvin Maniphest Tasks: T8940 Differential Revision: https://secure.phabricator.com/D15590 --- .../view/PhabricatorBadgesRecipientsListView.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/applications/badges/view/PhabricatorBadgesRecipientsListView.php b/src/applications/badges/view/PhabricatorBadgesRecipientsListView.php index 20fe2a0b3d..7b2ceaa38b 100644 --- a/src/applications/badges/view/PhabricatorBadgesRecipientsListView.php +++ b/src/applications/badges/view/PhabricatorBadgesRecipientsListView.php @@ -20,6 +20,7 @@ final class PhabricatorBadgesRecipientsListView extends AphrontView { $badge = $this->badge; $handles = $this->handles; + $awards = mpull($badge->getAwards(), null, 'getRecipientPHID'); $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, @@ -34,8 +35,17 @@ final class PhabricatorBadgesRecipientsListView extends AphrontView { $remove_uri = '/badges/recipients/'. $badge->getID().'/remove/?phid='.$handle->getPHID(); + $award = $awards[$handle->getPHID()]; + $awarder_handle = $viewer->renderHandle($award->getAwarderPHID()); + $award_date = phabricator_date($award->getDateCreated(), $viewer); + $awarder_info = pht( + 'Awarded by %s on %s', + $awarder_handle->render(), + $award_date); + $item = id(new PHUIObjectItemView()) ->setHeader($handle->getFullName()) + ->setSubhead($awarder_info) ->setHref($handle->getURI()) ->setImageURI($handle->getImageURI()); From 7694a6729f6aa3d0e625cd557d4ccd75dc9f39b7 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sun, 3 Apr 2016 12:24:58 -0700 Subject: [PATCH 22/84] Update Owners edit paths page to new UI Summary: Brings the edit paths page in owners up to new UI Test Plan: Edit some paths, yo. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15596 --- .../PhabricatorOwnersPathsController.php | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/applications/owners/controller/PhabricatorOwnersPathsController.php b/src/applications/owners/controller/PhabricatorOwnersPathsController.php index ccca55b6c5..ad7db5cc6e 100644 --- a/src/applications/owners/controller/PhabricatorOwnersPathsController.php +++ b/src/applications/owners/controller/PhabricatorOwnersPathsController.php @@ -139,8 +139,9 @@ final class PhabricatorOwnersPathsController ->addCancelButton($cancel_uri) ->setValue(pht('Save Paths'))); - $form_box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Edit Paths')) + $box = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Paths')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setForm($form); $crumbs = $this->buildApplicationCrumbs(); @@ -148,18 +149,23 @@ final class PhabricatorOwnersPathsController $package->getName(), $this->getApplicationURI('package/'.$package->getID().'/')); $crumbs->addTextCrumb(pht('Edit Paths')); + $crumbs->setBorder(true); - return $this->buildApplicationPage( - array( - $crumbs, - $form_box, - ), - array( - 'title' => array( - $package->getName(), - pht('Edit Paths'), - ), - )); - } + $header = id(new PHUIHeaderView()) + ->setHeader(pht('Edit Paths: %s', $package->getName())) + ->setHeaderIcon('fa-pencil'); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter($box); + + $title = array($package->getName(), pht('Edit Paths')); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); + + } } From 867c699fee2431b2cc0b85e39188f2e8d202a5f1 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sun, 3 Apr 2016 12:34:50 -0700 Subject: [PATCH 23/84] Update Macro Audio edit page for new UI Summary: Updates Macro Audit Edit page with new UI and newPage Test Plan: Edit Audio on macro, see new layout, save file. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15598 --- .../PhabricatorMacroAudioController.php | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/applications/macro/controller/PhabricatorMacroAudioController.php b/src/applications/macro/controller/PhabricatorMacroAudioController.php index 92b98e5564..7e2b924637 100644 --- a/src/applications/macro/controller/PhabricatorMacroAudioController.php +++ b/src/applications/macro/controller/PhabricatorMacroAudioController.php @@ -104,20 +104,19 @@ final class PhabricatorMacroAudioController extends PhabricatorMacroController { 'Best for ambient sounds.')); $form->appendChild($options); - - $form - ->appendChild( + $form->appendChild( id(new AphrontFormSubmitControl()) ->setValue(pht('Save Audio Behavior')) ->addCancelButton($view_uri)); $crumbs = $this->buildApplicationCrumbs(); - $title = pht('Edit Audio Behavior'); + $title = pht('Edit Audio: %s', $macro->getName()); $crumb = pht('Edit Audio'); $crumbs->addTextCrumb(pht('Macro "%s"', $macro->getName()), $view_uri); $crumbs->addTextCrumb($crumb, $request->getRequestURI()); + $crumbs->setBorder(true); $upload_form = id(new AphrontFormView()) ->setEncType('multipart/form-data') @@ -132,22 +131,30 @@ final class PhabricatorMacroAudioController extends PhabricatorMacroController { $upload = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Upload New Audio')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setForm($upload_form); $form_box = id(new PHUIObjectBoxView()) - ->setHeaderText($title) + ->setHeaderText(pht('Behavior')) ->setFormErrors($errors) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setForm($form); - return $this->buildApplicationPage( - array( - $crumbs, + $header = id(new PHUIHeaderView()) + ->setHeader($title) + ->setHeaderIcon('fa-pencil'); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter(array( $form_box, $upload, - ), - array( - 'title' => $title, )); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); } } From 4e7e204ae9c42d0420f7975c40ffff345750a0e3 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sun, 3 Apr 2016 11:49:33 -0700 Subject: [PATCH 24/84] Update Fund edit page for new UI Summary: Updates fund for new edit UI Test Plan: Create Fund, Edit Fund Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15595 --- .../FundInitiativeEditController.php | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/applications/fund/controller/FundInitiativeEditController.php b/src/applications/fund/controller/FundInitiativeEditController.php index 75f7d338c9..354d686bd5 100644 --- a/src/applications/fund/controller/FundInitiativeEditController.php +++ b/src/applications/fund/controller/FundInitiativeEditController.php @@ -30,13 +30,14 @@ final class FundInitiativeEditController $title = pht('Create Initiative'); $button_text = pht('Create Initiative'); $cancel_uri = $this->getApplicationURI(); + $header_icon = 'fa-plus-square'; } else { $title = pht( - 'Edit %s %s', - $initiative->getMonogram(), + 'Edit Initiative: %s', $initiative->getName()); $button_text = pht('Save Changes'); $cancel_uri = '/'.$initiative->getMonogram(); + $header_icon = 'fa-pencil'; } $e_name = true; @@ -230,20 +231,26 @@ final class FundInitiativeEditController '/'.$initiative->getMonogram()); $crumbs->addTextCrumb(pht('Edit')); } + $crumbs->setBorder(true); $box = id(new PHUIObjectBoxView()) ->setValidationException($validation_exception) - ->setHeaderText($title) + ->setHeaderText(pht('Initiative')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($form); - return $this->buildApplicationPage( - array( - $crumbs, - $box, - ), - array( - 'title' => $title, - )); + $header = id(new PHUIHeaderView()) + ->setHeader($title) + ->setHeaderIcon($header_icon); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter($box); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); } } From e61e426108cc299a46ba831ae61b81ed1e07608b Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sun, 3 Apr 2016 14:24:51 -0700 Subject: [PATCH 25/84] Update Facts for newPage Summary: No UI updates, just swapping over to `newPage` Test Plan: Pull up each page. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin, yelirekim Differential Revision: https://secure.phabricator.com/D15601 --- .../controller/PhabricatorFactChartController.php | 15 +++++++-------- .../controller/PhabricatorFactHomeController.php | 13 +++++++------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/applications/fact/controller/PhabricatorFactChartController.php b/src/applications/fact/controller/PhabricatorFactChartController.php index 18f769cc47..16df8fca69 100644 --- a/src/applications/fact/controller/PhabricatorFactChartController.php +++ b/src/applications/fact/controller/PhabricatorFactChartController.php @@ -77,14 +77,13 @@ final class PhabricatorFactChartController extends PhabricatorFactController { $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Chart')); - return $this->buildApplicationPage( - array( - $crumbs, - $box, - ), - array( - 'title' => pht('Chart'), - )); + $title = pht('Chart'); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($box); + } } diff --git a/src/applications/fact/controller/PhabricatorFactHomeController.php b/src/applications/fact/controller/PhabricatorFactHomeController.php index 8e5c9511eb..f4eeca0387 100644 --- a/src/applications/fact/controller/PhabricatorFactHomeController.php +++ b/src/applications/fact/controller/PhabricatorFactHomeController.php @@ -59,15 +59,16 @@ final class PhabricatorFactHomeController extends PhabricatorFactController { $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Home')); - return $this->buildApplicationPage( - array( - $crumbs, + $title = pht('Facts'); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild(array( $chart_form, $panel, - ), - array( - 'title' => pht('Facts'), )); + } private function buildChartForm() { From 49e5763cd0f11824f428a3795180c9338e15674b Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sun, 3 Apr 2016 13:48:05 -0700 Subject: [PATCH 26/84] Update PhortuneProviderAction to newPage Summary: Updates to `newPage` Test Plan: Unsure what specifically to test? Couldn't find where it's called. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15600 --- .../controller/PhortuneProviderActionController.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/applications/phortune/controller/PhortuneProviderActionController.php b/src/applications/phortune/controller/PhortuneProviderActionController.php index 52d5453f42..4d9b9f8386 100644 --- a/src/applications/phortune/controller/PhortuneProviderActionController.php +++ b/src/applications/phortune/controller/PhortuneProviderActionController.php @@ -39,11 +39,12 @@ final class PhortuneProviderActionController return $response; } - return $this->buildApplicationPage( - $response, - array( - 'title' => pht('Phortune'), - )); + $title = pht('Phortune'); + + return $this->newPage() + ->setTitle($title) + ->appendChild($response); + } From 5452f215ee17286e1ff6be04c33096dec72d7c42 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sun, 3 Apr 2016 13:42:54 -0700 Subject: [PATCH 27/84] Update Feed Story page for newPage Summary: Cleans up Feed Story individual page Test Plan: View an individual story by clicking on date. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15599 --- .../controller/PhabricatorFeedDetailController.php | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/applications/feed/controller/PhabricatorFeedDetailController.php b/src/applications/feed/controller/PhabricatorFeedDetailController.php index 4fac10d1a8..f136d01202 100644 --- a/src/applications/feed/controller/PhabricatorFeedDetailController.php +++ b/src/applications/feed/controller/PhabricatorFeedDetailController.php @@ -26,19 +26,13 @@ final class PhabricatorFeedDetailController extends PhabricatorFeedController { $title = pht('Story'); - $feed_view = phutil_tag_div('phabricator-feed-frame', $feed_view); - $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb($title); - return $this->buildApplicationPage( - array( - $crumbs, - $feed_view, - ), - array( - 'title' => $title, - )); + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($feed_view); } } From f90cd8a1edc87614b302b36428388fcfc74e004c Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sun, 3 Apr 2016 21:01:48 -0700 Subject: [PATCH 28/84] Modernize People UI Summary: Updates various /people/ pages for new UI and newPage Test Plan: Review creating people, new people, sending invites, editing a profile, setting a new picture, something with LDAP Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15604 --- .../PhabricatorPeopleApproveController.php | 5 +-- .../PhabricatorPeopleCreateController.php | 24 ++++++++----- .../PhabricatorPeopleInviteSendController.php | 36 ++++++++++++------- .../PhabricatorPeopleLdapController.php | 10 +++--- .../PhabricatorPeopleNewController.php | 26 ++++++++------ ...PhabricatorPeopleProfileEditController.php | 21 +++++++---- ...bricatorPeopleProfilePictureController.php | 20 ++++++++--- 7 files changed, 90 insertions(+), 52 deletions(-) diff --git a/src/applications/people/controller/PhabricatorPeopleApproveController.php b/src/applications/people/controller/PhabricatorPeopleApproveController.php index a906b15655..a63682239f 100644 --- a/src/applications/people/controller/PhabricatorPeopleApproveController.php +++ b/src/applications/people/controller/PhabricatorPeopleApproveController.php @@ -53,8 +53,7 @@ final class PhabricatorPeopleApproveController return id(new AphrontRedirectResponse())->setURI($done_uri); } - $dialog = id(new AphrontDialogView()) - ->setUser($admin) + return $this->newDialog() ->setTitle(pht('Confirm Approval')) ->appendChild( pht( @@ -62,7 +61,5 @@ final class PhabricatorPeopleApproveController phutil_tag('strong', array(), $user->getUsername()))) ->addCancelButton($done_uri) ->addSubmitButton(pht('Approve Account')); - - return id(new AphrontDialogResponse())->setDialog($dialog); } } diff --git a/src/applications/people/controller/PhabricatorPeopleCreateController.php b/src/applications/people/controller/PhabricatorPeopleCreateController.php index 15a34d0063..82a27e2f9e 100644 --- a/src/applications/people/controller/PhabricatorPeopleCreateController.php +++ b/src/applications/people/controller/PhabricatorPeopleCreateController.php @@ -90,19 +90,25 @@ final class PhabricatorPeopleCreateController $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb($title); + $crumbs->setBorder(true); + + $header = id(new PHUIHeaderView()) + ->setHeader($title) + ->setHeaderIcon('fa-user'); $box = id(new PHUIObjectBoxView()) - ->setHeaderText($title) + ->setHeaderText(pht('User')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setForm($form); - return $this->buildApplicationPage( - array( - $crumbs, - $box, - ), - array( - 'title' => $title, - )); + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter($box); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); } } diff --git a/src/applications/people/controller/PhabricatorPeopleInviteSendController.php b/src/applications/people/controller/PhabricatorPeopleInviteSendController.php index 6a7049215d..e611606a79 100644 --- a/src/applications/people/controller/PhabricatorPeopleInviteSendController.php +++ b/src/applications/people/controller/PhabricatorPeopleInviteSendController.php @@ -125,8 +125,10 @@ final class PhabricatorPeopleInviteSendController } else { $crumbs->addTextCrumb(pht('Invite Users')); } + $crumbs->setBorder(true); $confirm_box = null; + $info_view = null; if ($is_confirm) { $handles = array(); @@ -157,14 +159,15 @@ final class PhabricatorPeopleInviteSendController ->setValue(pht('Send Invitations'))); } + $info_view = id(new PHUIInfoView()) + ->setErrors($confirm_errors) + ->setSeverity($severity); + $confirm_box = id(new PHUIObjectBoxView()) - ->setInfoView( - id(new PHUIInfoView()) - ->setErrors($confirm_errors) - ->setSeverity($severity)) ->setHeaderText(pht('Confirm Invites')) ->setTable($invite_table) - ->appendChild($confirm_form); + ->appendChild($confirm_form) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY); } $form = id(new AphrontFormView()) @@ -197,23 +200,32 @@ final class PhabricatorPeopleInviteSendController : pht('Continue')) ->addCancelButton($this->getApplicationURI('invite/'))); + $header = id(new PHUIHeaderView()) + ->setHeader($title) + ->setHeaderIcon('fa-group'); + $box = id(new PHUIObjectBoxView()) ->setHeaderText( $is_confirm ? pht('Revise Invites') : pht('Invite Users')) ->setFormErrors($errors) - ->setForm($form); + ->setForm($form) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY); - return $this->buildApplicationPage( - array( - $crumbs, + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter(array( + $info_view, $confirm_box, $box, - ), - array( - 'title' => $title, )); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); + } } diff --git a/src/applications/people/controller/PhabricatorPeopleLdapController.php b/src/applications/people/controller/PhabricatorPeopleLdapController.php index 2e030ad580..1a6530ab31 100644 --- a/src/applications/people/controller/PhabricatorPeopleLdapController.php +++ b/src/applications/people/controller/PhabricatorPeopleLdapController.php @@ -42,7 +42,6 @@ final class PhabricatorPeopleLdapController $this->getApplicationURI('/ldap/')); $nav = $this->buildSideNavView(); - $nav->setCrumbs($crumbs); $nav->selectFilter('ldap'); $nav->appendChild($content); @@ -56,11 +55,10 @@ final class PhabricatorPeopleLdapController $nav->appendChild($this->processSearchRequest($request)); } - return $this->buildApplicationPage( - $nav, - array( - 'title' => pht('Import Ldap Users'), - )); + return $this->newPage() + ->setTitle(pht('Import Ldap Users')) + ->setCrumbs($crumbs) + ->setNavigation($nav); } private function processImportRequest($request) { diff --git a/src/applications/people/controller/PhabricatorPeopleNewController.php b/src/applications/people/controller/PhabricatorPeopleNewController.php index 2590129c09..60f9f47b5d 100644 --- a/src/applications/people/controller/PhabricatorPeopleNewController.php +++ b/src/applications/people/controller/PhabricatorPeopleNewController.php @@ -208,22 +208,28 @@ final class PhabricatorPeopleNewController $title = pht('Create New User'); - $form_box = id(new PHUIObjectBoxView()) - ->setHeaderText($title) + $box = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('User')) ->setFormErrors($errors) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setForm($form); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb($title); + $crumbs->setBorder(true); - return $this->buildApplicationPage( - array( - $crumbs, - $form_box, - ), - array( - 'title' => $title, - )); + $header = id(new PHUIHeaderView()) + ->setHeader($title) + ->setHeaderIcon('fa-user'); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter($box); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); } } diff --git a/src/applications/people/controller/PhabricatorPeopleProfileEditController.php b/src/applications/people/controller/PhabricatorPeopleProfileEditController.php index cb258d17ac..9f132a18d8 100644 --- a/src/applications/people/controller/PhabricatorPeopleProfileEditController.php +++ b/src/applications/people/controller/PhabricatorPeopleProfileEditController.php @@ -75,24 +75,33 @@ final class PhabricatorPeopleProfileEditController } $form_box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Edit Profile')) + ->setHeaderText(pht('Profile')) ->setValidationException($validation_exception) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setForm($form); - if ($note) { - $form_box->setInfoView($note); - } - $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Edit Profile')); + $crumbs->setBorder(true); $nav = $this->getProfileMenu(); $nav->selectFilter(PhabricatorPeopleProfilePanelEngine::PANEL_MANAGE); + $header = id(new PHUIHeaderView()) + ->setHeader(pht('Edit Profile: %s', $user->getFullName())) + ->setHeaderIcon('fa-pencil'); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter(array( + $note, + $form_box, + )); + return $this->newPage() ->setTitle($title) ->setCrumbs($crumbs) ->setNavigation($nav) - ->appendChild($form_box); + ->appendChild($view); } } diff --git a/src/applications/people/controller/PhabricatorPeopleProfilePictureController.php b/src/applications/people/controller/PhabricatorPeopleProfilePictureController.php index cf78dd9c03..29b2290153 100644 --- a/src/applications/people/controller/PhabricatorPeopleProfilePictureController.php +++ b/src/applications/people/controller/PhabricatorPeopleProfilePictureController.php @@ -228,6 +228,7 @@ final class PhabricatorPeopleProfilePictureController $form_box = id(new PHUIObjectBoxView()) ->setHeaderText($title) ->setFormErrors($errors) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setForm($form); $upload_form = id(new AphrontFormView()) @@ -247,22 +248,31 @@ final class PhabricatorPeopleProfilePictureController $upload_box = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Upload New Picture')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setForm($upload_form); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Edit Profile Picture')); + $crumbs->setBorder(true); $nav = $this->getProfileMenu(); $nav->selectFilter(PhabricatorPeopleProfilePanelEngine::PANEL_MANAGE); + $header = id(new PHUIHeaderView()) + ->setHeader(pht('Edit Profile Picture')) + ->setHeaderIcon('fa-camera'); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter(array( + $form_box, + $upload_box, + )); + return $this->newPage() ->setTitle($title) ->setCrumbs($crumbs) ->setNavigation($nav) - ->appendChild( - array( - $form_box, - $upload_box, - )); + ->appendChild($view); } } From 12dca28193924a29ee2e23f22bdbb5a5b7873657 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sun, 3 Apr 2016 14:59:11 -0700 Subject: [PATCH 29/84] Update Phlux to new UI Summary: Updates view, list, edit pages on Phlux. Test Plan: Create a variable, see variable, edit variable, view lists. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15602 --- .../phlux/controller/PhluxEditController.php | 30 ++++++--- .../phlux/controller/PhluxListController.php | 27 ++++++-- .../phlux/controller/PhluxViewController.php | 67 +++++++++++-------- 3 files changed, 78 insertions(+), 46 deletions(-) diff --git a/src/applications/phlux/controller/PhluxEditController.php b/src/applications/phlux/controller/PhluxEditController.php index 775737c9b7..f37795a754 100644 --- a/src/applications/phlux/controller/PhluxEditController.php +++ b/src/applications/phlux/controller/PhluxEditController.php @@ -154,24 +154,34 @@ final class PhluxEditController extends PhluxController { if ($is_new) { $title = pht('Create Variable'); $crumbs->addTextCrumb($title, $request->getRequestURI()); + $header_icon = 'fa-plus-square'; } else { - $title = pht('Edit %s', $key); + $title = pht('Edit Variable: %s', $key); + $header_icon = 'fa-pencil'; $crumbs->addTextCrumb($title, $request->getRequestURI()); } + $crumbs->setBorder(true); - $form_box = id(new PHUIObjectBoxView()) - ->setHeaderText($title) + $box = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Variable')) ->setFormErrors($errors) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setForm($form); - return $this->buildApplicationPage( - array( - $crumbs, - $form_box, - ), - array( - 'title' => $title, + $header = id(new PHUIHeaderView()) + ->setHeader($title) + ->setHeaderIcon($header_icon); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter(array( + $box, )); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); } } diff --git a/src/applications/phlux/controller/PhluxListController.php b/src/applications/phlux/controller/PhluxListController.php index 550f571a6a..e75fb09999 100644 --- a/src/applications/phlux/controller/PhluxListController.php +++ b/src/applications/phlux/controller/PhluxListController.php @@ -13,6 +13,7 @@ final class PhluxListController extends PhluxController { $vars = $query->executeWithCursorPager($pager); $view = new PHUIObjectItemListView(); + $view->setFlush(true); foreach ($vars as $var) { $key = $var->getVariableKey(); @@ -28,19 +29,31 @@ final class PhluxListController extends PhluxController { $crumbs = $this->buildApplicationCrumbs(); + $box = id(new PHUIObjectBoxView()) + ->setHeaderText('Variables') + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->appendChild($view); + $title = pht('Variable List'); + $header = id(new PHUIHeaderView()) + ->setHeader($title) + ->setHeaderIcon('fa-copy'); $crumbs->addTextCrumb($title, $this->getApplicationURI()); + $crumbs->setBorder(true); - return $this->buildApplicationPage( - array( - $crumbs, - $view, + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter(array( + $box, $pager, - ), - array( - 'title' => $title, )); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); + } } diff --git a/src/applications/phlux/controller/PhluxViewController.php b/src/applications/phlux/controller/PhluxViewController.php index 384e15b57e..b3019443be 100644 --- a/src/applications/phlux/controller/PhluxViewController.php +++ b/src/applications/phlux/controller/PhluxViewController.php @@ -15,40 +15,24 @@ final class PhluxViewController extends PhluxController { return new Aphront404Response(); } - $crumbs = $this->buildApplicationCrumbs(); - $title = $var->getVariableKey(); + $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb($title, $request->getRequestURI()); + $crumbs->setBorder(true); + + $curtain = $this->buildCurtainView($var); $header = id(new PHUIHeaderView()) ->setHeader($title) ->setUser($viewer) - ->setPolicyObject($var); - - $actions = id(new PhabricatorActionListView()) - ->setUser($viewer) - ->setObject($var); - - $can_edit = PhabricatorPolicyFilter::hasCapability( - $viewer, - $var, - PhabricatorPolicyCapability::CAN_EDIT); - - $actions->addAction( - id(new PhabricatorActionView()) - ->setIcon('fa-pencil') - ->setName(pht('Edit Variable')) - ->setHref($this->getApplicationURI('/edit/'.$var->getVariableKey().'/')) - ->setDisabled(!$can_edit) - ->setWorkflow(!$can_edit)); + ->setPolicyObject($var) + ->setHeaderIcon('fa-copy'); $display_value = json_encode($var->getVariableValue()); $properties = id(new PHUIPropertyListView()) ->setUser($viewer) - ->setObject($var) - ->setActionList($actions) ->addProperty(pht('Value'), $display_value); $timeline = $this->buildTransactionTimeline( @@ -57,18 +41,43 @@ final class PhluxViewController extends PhluxController { $timeline->setShouldTerminate(true); $object_box = id(new PHUIObjectBoxView()) - ->setHeader($header) + ->setHeaderText(pht('DETAILS')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->addPropertyList($properties); - return $this->buildApplicationPage( - array( - $crumbs, + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setCurtain($curtain) + ->setMainColumn(array( $object_box, $timeline, - ), - array( - 'title' => $title, )); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); + } + + private function buildCurtainView(PhluxVariable $var) { + $viewer = $this->getViewer(); + + $curtain = $this->newCurtainView($var); + + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $var, + PhabricatorPolicyCapability::CAN_EDIT); + + $curtain->addAction( + id(new PhabricatorActionView()) + ->setIcon('fa-pencil') + ->setName(pht('Edit Variable')) + ->setHref($this->getApplicationURI('/edit/'.$var->getVariableKey().'/')) + ->setDisabled(!$can_edit) + ->setWorkflow(!$can_edit)); + + return $curtain; } } From f54a2007eaa640a45c8af32df9bdcc3a222d7fed Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sun, 3 Apr 2016 20:16:25 -0700 Subject: [PATCH 30/84] Update XHProf for newPage Summary: Simple Conversion Test Plan: Pull up /xhprof/ Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15603 --- .../PhabricatorXHProfSampleListController.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/applications/xhprof/controller/PhabricatorXHProfSampleListController.php b/src/applications/xhprof/controller/PhabricatorXHProfSampleListController.php index 65ca5593f3..5faab70c7a 100644 --- a/src/applications/xhprof/controller/PhabricatorXHProfSampleListController.php +++ b/src/applications/xhprof/controller/PhabricatorXHProfSampleListController.php @@ -87,11 +87,12 @@ final class PhabricatorXHProfSampleListController $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('XHProf Samples')); - return $this->buildApplicationPage( - array($crumbs, $list), - array( - 'title' => pht('XHProf Samples'), - )); + $title = pht('XHProf Samples'); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($list); } } From 694a8543d8092ecbe15021e37fdeee2a045c510c Mon Sep 17 00:00:00 2001 From: epriestley Date: Sun, 3 Apr 2016 07:25:33 -0700 Subject: [PATCH 31/84] Modernize some OAuth Server code Summary: Ref T7303. This inches toward properly-behaved cluster logout. - Use IDs instead of PHIDs in URIs. - Slightly more modern code. - Fix some crumb stuff. Test Plan: Created, edited, viewed, deleted, showed secret for, authorized, test-auth'd an application. Reviewers: chad Reviewed By: chad Maniphest Tasks: T7303 Differential Revision: https://secure.phabricator.com/D15592 --- src/__phutil_library_map__.php | 4 +-- .../PhabricatorOAuthServerApplication.php | 10 +++--- .../PhabricatorOAuthServerAuthController.php | 6 ++++ .../PhabricatorOAuthServerController.php | 7 ---- ...PhabricatorOAuthClientDeleteController.php | 14 ++++---- .../PhabricatorOAuthClientEditController.php | 23 ++++++------- ...PhabricatorOAuthClientSecretController.php | 18 +++++------ .../PhabricatorOAuthClientTestController.php} | 4 +-- .../PhabricatorOAuthClientViewController.php | 32 ++++++++----------- ...abricatorOAuthServerClientSearchEngine.php | 11 +------ .../storage/PhabricatorOAuthServerClient.php | 9 ++++-- 11 files changed, 59 insertions(+), 79 deletions(-) rename src/applications/oauthserver/controller/{PhabricatorOAuthServerTestController.php => client/PhabricatorOAuthClientTestController.php} (95%) diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 58216de058..f43540c166 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2705,6 +2705,7 @@ phutil_register_library_map(array( 'PhabricatorOAuthClientEditController' => 'applications/oauthserver/controller/client/PhabricatorOAuthClientEditController.php', 'PhabricatorOAuthClientListController' => 'applications/oauthserver/controller/client/PhabricatorOAuthClientListController.php', 'PhabricatorOAuthClientSecretController' => 'applications/oauthserver/controller/client/PhabricatorOAuthClientSecretController.php', + 'PhabricatorOAuthClientTestController' => 'applications/oauthserver/controller/client/PhabricatorOAuthClientTestController.php', 'PhabricatorOAuthClientViewController' => 'applications/oauthserver/controller/client/PhabricatorOAuthClientViewController.php', 'PhabricatorOAuthResponse' => 'applications/oauthserver/PhabricatorOAuthResponse.php', 'PhabricatorOAuthServer' => 'applications/oauthserver/PhabricatorOAuthServer.php', @@ -2723,7 +2724,6 @@ phutil_register_library_map(array( 'PhabricatorOAuthServerDAO' => 'applications/oauthserver/storage/PhabricatorOAuthServerDAO.php', 'PhabricatorOAuthServerScope' => 'applications/oauthserver/PhabricatorOAuthServerScope.php', 'PhabricatorOAuthServerTestCase' => 'applications/oauthserver/__tests__/PhabricatorOAuthServerTestCase.php', - 'PhabricatorOAuthServerTestController' => 'applications/oauthserver/controller/PhabricatorOAuthServerTestController.php', 'PhabricatorOAuthServerTokenController' => 'applications/oauthserver/controller/PhabricatorOAuthServerTokenController.php', 'PhabricatorObjectHandle' => 'applications/phid/PhabricatorObjectHandle.php', 'PhabricatorObjectHasAsanaSubtaskEdgeType' => 'applications/doorkeeper/edge/PhabricatorObjectHasAsanaSubtaskEdgeType.php', @@ -7192,6 +7192,7 @@ phutil_register_library_map(array( 'PhabricatorOAuthClientEditController' => 'PhabricatorOAuthClientController', 'PhabricatorOAuthClientListController' => 'PhabricatorOAuthClientController', 'PhabricatorOAuthClientSecretController' => 'PhabricatorOAuthClientController', + 'PhabricatorOAuthClientTestController' => 'PhabricatorOAuthClientController', 'PhabricatorOAuthClientViewController' => 'PhabricatorOAuthClientController', 'PhabricatorOAuthResponse' => 'AphrontResponse', 'PhabricatorOAuthServer' => 'Phobject', @@ -7214,7 +7215,6 @@ phutil_register_library_map(array( 'PhabricatorOAuthServerDAO' => 'PhabricatorLiskDAO', 'PhabricatorOAuthServerScope' => 'Phobject', 'PhabricatorOAuthServerTestCase' => 'PhabricatorTestCase', - 'PhabricatorOAuthServerTestController' => 'PhabricatorOAuthServerController', 'PhabricatorOAuthServerTokenController' => 'PhabricatorOAuthServerController', 'PhabricatorObjectHandle' => array( 'Phobject', diff --git a/src/applications/oauthserver/application/PhabricatorOAuthServerApplication.php b/src/applications/oauthserver/application/PhabricatorOAuthServerApplication.php index 7bd7eed9ac..eb953bd391 100644 --- a/src/applications/oauthserver/application/PhabricatorOAuthServerApplication.php +++ b/src/applications/oauthserver/application/PhabricatorOAuthServerApplication.php @@ -50,14 +50,14 @@ final class PhabricatorOAuthServerApplication extends PhabricatorApplication { '(?:query/(?P[^/]+)/)?' => 'PhabricatorOAuthClientListController', 'auth/' => 'PhabricatorOAuthServerAuthController', - 'test/(?P\d+)/' => 'PhabricatorOAuthServerTestController', 'token/' => 'PhabricatorOAuthServerTokenController', 'client/' => array( 'create/' => 'PhabricatorOAuthClientEditController', - 'delete/(?P[^/]+)/' => 'PhabricatorOAuthClientDeleteController', - 'edit/(?P[^/]+)/' => 'PhabricatorOAuthClientEditController', - 'view/(?P[^/]+)/' => 'PhabricatorOAuthClientViewController', - 'secret/(?P[^/]+)/' => 'PhabricatorOAuthClientSecretController', + 'delete/(?P\d+)/' => 'PhabricatorOAuthClientDeleteController', + 'edit/(?P\d+)/' => 'PhabricatorOAuthClientEditController', + 'view/(?P\d+)/' => 'PhabricatorOAuthClientViewController', + 'secret/(?P\d+)/' => 'PhabricatorOAuthClientSecretController', + 'test/(?P\d+)/' => 'PhabricatorOAuthClientTestController', ), ), ); diff --git a/src/applications/oauthserver/controller/PhabricatorOAuthServerAuthController.php b/src/applications/oauthserver/controller/PhabricatorOAuthServerAuthController.php index d473e1557a..675b88e6da 100644 --- a/src/applications/oauthserver/controller/PhabricatorOAuthServerAuthController.php +++ b/src/applications/oauthserver/controller/PhabricatorOAuthServerAuthController.php @@ -3,6 +3,12 @@ final class PhabricatorOAuthServerAuthController extends PhabricatorOAuthServerController { + protected function buildApplicationCrumbs() { + // We're specifically not putting an "OAuth Server" application crumb + // on the auth pages because it doesn't make sense to send users there. + return new PHUICrumbsView(); + } + public function handleRequest(AphrontRequest $request) { $viewer = $this->getViewer(); diff --git a/src/applications/oauthserver/controller/PhabricatorOAuthServerController.php b/src/applications/oauthserver/controller/PhabricatorOAuthServerController.php index 00be8a1e8d..b71faae89e 100644 --- a/src/applications/oauthserver/controller/PhabricatorOAuthServerController.php +++ b/src/applications/oauthserver/controller/PhabricatorOAuthServerController.php @@ -5,11 +5,4 @@ abstract class PhabricatorOAuthServerController const CONTEXT_AUTHORIZE = 'oauthserver.authorize'; - protected function buildApplicationCrumbs() { - // We're specifically not putting an "OAuth Server" application crumb - // on these pages because it doesn't make sense to send users there on - // the auth workflows. - return new PHUICrumbsView(); - } - } diff --git a/src/applications/oauthserver/controller/client/PhabricatorOAuthClientDeleteController.php b/src/applications/oauthserver/controller/client/PhabricatorOAuthClientDeleteController.php index 409063d9e9..a3c47e1604 100644 --- a/src/applications/oauthserver/controller/client/PhabricatorOAuthClientDeleteController.php +++ b/src/applications/oauthserver/controller/client/PhabricatorOAuthClientDeleteController.php @@ -3,13 +3,12 @@ final class PhabricatorOAuthClientDeleteController extends PhabricatorOAuthClientController { - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); + public function handleRequest(AphrontRequest $request) { + $viewer = $this->getViewer(); $client = id(new PhabricatorOAuthServerClientQuery()) ->setViewer($viewer) - ->withPHIDs(array($this->getClientPHID())) + ->withIDs(array($request->getURIData('id'))) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, @@ -20,14 +19,15 @@ final class PhabricatorOAuthClientDeleteController return new Aphront404Response(); } + // TODO: This should be "disable", not "delete"! + if ($request->isFormPost()) { $client->delete(); $app_uri = $this->getApplicationURI(); return id(new AphrontRedirectResponse())->setURI($app_uri); } - $dialog = id(new AphrontDialogView()) - ->setUser($viewer) + return $this->newDialog() ->setTitle(pht('Delete OAuth Application?')) ->appendParagraph( pht( @@ -35,8 +35,6 @@ final class PhabricatorOAuthClientDeleteController phutil_tag('strong', array(), $client->getName()))) ->addCancelButton($client->getViewURI()) ->addSubmitButton(pht('Delete Application')); - - return id(new AphrontDialogResponse())->setDialog($dialog); } } diff --git a/src/applications/oauthserver/controller/client/PhabricatorOAuthClientEditController.php b/src/applications/oauthserver/controller/client/PhabricatorOAuthClientEditController.php index cd194aa74b..d6278345f6 100644 --- a/src/applications/oauthserver/controller/client/PhabricatorOAuthClientEditController.php +++ b/src/applications/oauthserver/controller/client/PhabricatorOAuthClientEditController.php @@ -3,15 +3,14 @@ final class PhabricatorOAuthClientEditController extends PhabricatorOAuthClientController { - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); + public function handleRequest(AphrontRequest $request) { + $viewer = $this->getViewer(); + $id = $request->getURIData('id'); - $phid = $this->getClientPHID(); - if ($phid) { + if ($id) { $client = id(new PhabricatorOAuthServerClientQuery()) ->setViewer($viewer) - ->withPHIDs(array($phid)) + ->withIDs(array($id)) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, @@ -124,14 +123,10 @@ final class PhabricatorOAuthClientEditController ->setFormErrors($errors) ->setForm($form); - return $this->buildApplicationPage( - array( - $crumbs, - $box, - ), - array( - 'title' => $title, - )); + return $this->newPage() + ->setCrumbs($crumbs) + ->setTitle($title) + ->appendChild($box); } } diff --git a/src/applications/oauthserver/controller/client/PhabricatorOAuthClientSecretController.php b/src/applications/oauthserver/controller/client/PhabricatorOAuthClientSecretController.php index ceb1379b7a..9f4e0d0683 100644 --- a/src/applications/oauthserver/controller/client/PhabricatorOAuthClientSecretController.php +++ b/src/applications/oauthserver/controller/client/PhabricatorOAuthClientSecretController.php @@ -8,7 +8,7 @@ final class PhabricatorOAuthClientSecretController $client = id(new PhabricatorOAuthServerClientQuery()) ->setViewer($viewer) - ->withPHIDs(array($this->getClientPHID())) + ->withIDs(array($request->getURIData('id'))) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, @@ -27,22 +27,20 @@ final class PhabricatorOAuthClientSecretController if ($request->isFormPost()) { $secret = $client->getSecret(); + $body = id(new PHUIFormLayoutView()) ->appendChild( id(new AphrontFormTextAreaControl()) - ->setLabel(pht('Plaintext')) - ->setReadOnly(true) - ->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_SHORT) - ->setValue($secret)); + ->setLabel(pht('Plaintext')) + ->setReadOnly(true) + ->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_SHORT) + ->setValue($secret)); - $dialog = id(new AphrontDialogView()) - ->setUser($viewer) + return $this->newDialog() ->setWidth(AphrontDialogView::WIDTH_FORM) ->setTitle(pht('Application Secret')) ->appendChild($body) ->addCancelButton($view_uri, pht('Done')); - - return id(new AphrontDialogResponse())->setDialog($dialog); } @@ -59,8 +57,8 @@ final class PhabricatorOAuthClientSecretController 'your monitor to create a human shield, keeping it safe from prying '. 'eyes. Protect company secrets!'); } + return $this->newDialog() - ->setUser($viewer) ->setTitle(pht('Really show application secret?')) ->appendChild($body) ->addSubmitButton(pht('Show Application Secret')) diff --git a/src/applications/oauthserver/controller/PhabricatorOAuthServerTestController.php b/src/applications/oauthserver/controller/client/PhabricatorOAuthClientTestController.php similarity index 95% rename from src/applications/oauthserver/controller/PhabricatorOAuthServerTestController.php rename to src/applications/oauthserver/controller/client/PhabricatorOAuthClientTestController.php index e5966518f5..9efcb608c2 100644 --- a/src/applications/oauthserver/controller/PhabricatorOAuthServerTestController.php +++ b/src/applications/oauthserver/controller/client/PhabricatorOAuthClientTestController.php @@ -1,7 +1,7 @@ getViewer(); diff --git a/src/applications/oauthserver/controller/client/PhabricatorOAuthClientViewController.php b/src/applications/oauthserver/controller/client/PhabricatorOAuthClientViewController.php index 368de86f10..c7667ef6ae 100644 --- a/src/applications/oauthserver/controller/client/PhabricatorOAuthClientViewController.php +++ b/src/applications/oauthserver/controller/client/PhabricatorOAuthClientViewController.php @@ -3,13 +3,12 @@ final class PhabricatorOAuthClientViewController extends PhabricatorOAuthClientController { - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); + public function handleRequest(AphrontRequest $request) { + $viewer = $this->getViewer(); $client = id(new PhabricatorOAuthServerClientQuery()) ->setViewer($viewer) - ->withPHIDs(array($this->getClientPHID())) + ->withIDs(array($request->getURIData('id'))) ->executeOne(); if (!$client) { return new Aphront404Response(); @@ -27,18 +26,16 @@ final class PhabricatorOAuthClientViewController ->setHeader($header) ->addPropertyList($properties); - return $this->buildApplicationPage( - array( - $crumbs, - $box, - ), - array( - 'title' => pht('OAuth Application: %s', $client->getName()), - )); + $title = pht('OAuth Application: %s', $client->getName()); + + return $this->newPage() + ->setCrumbs($crumbs) + ->setTitle($title) + ->appendChild($box); } private function buildHeaderView(PhabricatorOAuthServerClient $client) { - $viewer = $this->getRequest()->getUser(); + $viewer = $this->getViewer(); $header = id(new PHUIHeaderView()) ->setUser($viewer) @@ -49,7 +46,7 @@ final class PhabricatorOAuthClientViewController } private function buildActionView(PhabricatorOAuthServerClient $client) { - $viewer = $this->getRequest()->getUser(); + $viewer = $this->getViewer(); $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, @@ -63,7 +60,6 @@ final class PhabricatorOAuthClientViewController ->executeOne(); $is_authorized = (bool)$authorization; $id = $client->getID(); - $phid = $client->getPHID(); $view = id(new PhabricatorActionListView()) ->setUser($viewer); @@ -80,7 +76,7 @@ final class PhabricatorOAuthClientViewController id(new PhabricatorActionView()) ->setName(pht('Show Application Secret')) ->setIcon('fa-eye') - ->setHref($this->getApplicationURI("client/secret/{$phid}/")) + ->setHref($this->getApplicationURI("client/secret/{$id}/")) ->setDisabled(!$can_edit) ->setWorkflow(true)); @@ -98,7 +94,7 @@ final class PhabricatorOAuthClientViewController ->setIcon('fa-wrench') ->setWorkflow(true) ->setDisabled($is_authorized) - ->setHref($this->getApplicationURI('test/'.$id.'/'))); + ->setHref($this->getApplicationURI("client/test/{$id}/"))); return $view; } @@ -110,7 +106,7 @@ final class PhabricatorOAuthClientViewController ->setUser($viewer); $view->addProperty( - pht('Client ID'), + pht('Client PHID'), $client->getPHID()); $view->addProperty( diff --git a/src/applications/oauthserver/query/PhabricatorOAuthServerClientSearchEngine.php b/src/applications/oauthserver/query/PhabricatorOAuthServerClientSearchEngine.php index 8168527f4d..212ae5218a 100644 --- a/src/applications/oauthserver/query/PhabricatorOAuthServerClientSearchEngine.php +++ b/src/applications/oauthserver/query/PhabricatorOAuthServerClientSearchEngine.php @@ -79,12 +79,6 @@ final class PhabricatorOAuthServerClientSearchEngine return parent::buildSavedQueryFromBuiltin($query_key); } - protected function getRequiredHandlePHIDsForResultList( - array $clients, - PhabricatorSavedQuery $query) { - return mpull($clients, 'getCreatorPHID'); - } - protected function renderResultList( array $clients, PhabricatorSavedQuery $query, @@ -96,14 +90,11 @@ final class PhabricatorOAuthServerClientSearchEngine $list = id(new PHUIObjectItemListView()) ->setUser($viewer); foreach ($clients as $client) { - $creator = $handles[$client->getCreatorPHID()]; - $item = id(new PHUIObjectItemView()) ->setObjectName(pht('Application %d', $client->getID())) ->setHeader($client->getName()) ->setHref($client->getViewURI()) - ->setObject($client) - ->addByline(pht('Creator: %s', $creator->renderLink())); + ->setObject($client); $list->addItem($item); } diff --git a/src/applications/oauthserver/storage/PhabricatorOAuthServerClient.php b/src/applications/oauthserver/storage/PhabricatorOAuthServerClient.php index 79de173bad..8306dd08b0 100644 --- a/src/applications/oauthserver/storage/PhabricatorOAuthServerClient.php +++ b/src/applications/oauthserver/storage/PhabricatorOAuthServerClient.php @@ -15,15 +15,18 @@ final class PhabricatorOAuthServerClient protected $editPolicy; public function getEditURI() { - return '/oauthserver/client/edit/'.$this->getPHID().'/'; + $id = $this->getID(); + return "/oauthserver/client/edit/{$id}/"; } public function getViewURI() { - return '/oauthserver/client/view/'.$this->getPHID().'/'; + $id = $this->getID(); + return "/oauthserver/client/view/{$id}/"; } public function getDeleteURI() { - return '/oauthserver/client/delete/'.$this->getPHID().'/'; + $id = $this->getID(); + return "/oauthserver/client/delete/{$id}/"; } public static function initializeNewClient(PhabricatorUser $actor) { From 60133b6fa5d5aaf0e7abe5ec28aa64b27ba56496 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sun, 3 Apr 2016 08:25:02 -0700 Subject: [PATCH 32/84] Begin cleaning up OAuth scope handling Summary: Ref T7303. OAuth scope handling never got fully modernized and is a bit of a mess. Also introduce implicit "ALWAYS" and "NEVER" scopes. Always give tokens access to meta-methods like `conduit.getcapabilities` and `conduit.query`. These do not expose user information. Test Plan: - Used a token to call `user.whoami`. - Used a token to call `conduit.query`. - Used a token to try to call `user.query`, got rebuffed. Reviewers: chad Reviewed By: chad Maniphest Tasks: T7303 Differential Revision: https://secure.phabricator.com/D15593 --- src/applications/conduit/call/ConduitCall.php | 4 -- .../PhabricatorConduitAPIController.php | 61 +++++++++++++------ .../PhabricatorConduitConsoleController.php | 30 +++++++++ .../conduit/method/ConduitAPIMethod.php | 5 +- ...ConduitGetCapabilitiesConduitAPIMethod.php | 4 ++ .../method/ConduitQueryConduitAPIMethod.php | 4 ++ .../oauthserver/PhabricatorOAuthServer.php | 47 +++++++------- .../PhabricatorOAuthServerScope.php | 6 -- .../PhabricatorOAuthServerTokenController.php | 2 +- ...abricatorOAuthClientAuthorizationQuery.php | 42 ++++++------- .../PhabricatorOAuthServerAccessToken.php | 14 +++++ 11 files changed, 138 insertions(+), 81 deletions(-) diff --git a/src/applications/conduit/call/ConduitCall.php b/src/applications/conduit/call/ConduitCall.php index 89bddad542..017d96ae8b 100644 --- a/src/applications/conduit/call/ConduitCall.php +++ b/src/applications/conduit/call/ConduitCall.php @@ -65,10 +65,6 @@ final class ConduitCall extends Phobject { return $this->handler->shouldAllowUnguardedWrites(); } - public function getRequiredScope() { - return $this->handler->getRequiredScope(); - } - public function getErrorDescription($code) { return $this->handler->getErrorDescription($code); } diff --git a/src/applications/conduit/controller/PhabricatorConduitAPIController.php b/src/applications/conduit/controller/PhabricatorConduitAPIController.php index ce215468d9..ac0d115a40 100644 --- a/src/applications/conduit/controller/PhabricatorConduitAPIController.php +++ b/src/applications/conduit/controller/PhabricatorConduitAPIController.php @@ -45,7 +45,6 @@ final class PhabricatorConduitAPIController $auth_error = null; $conduit_username = '-'; if ($call->shouldRequireAuthentication()) { - $metadata['scope'] = $call->getRequiredScope(); $auth_error = $this->authenticateUser($api_request, $metadata, $method); // If we've explicitly authenticated the user here and either done // CSRF validation or are using a non-web authentication mechanism. @@ -185,11 +184,6 @@ final class PhabricatorConduitAPIController // First, verify the signature. try { $protocol_data = $metadata; - - // TODO: We should stop writing this into the protocol data when - // processing a request. - unset($protocol_data['scope']); - ConduitClient::verifySignature( $method, $api_request->getAllParameters(), @@ -362,11 +356,8 @@ final class PhabricatorConduitAPIController $user); } - // handle oauth $access_token = idx($metadata, 'access_token'); - $method_scope = idx($metadata, 'scope'); - if ($access_token && - $method_scope != PhabricatorOAuthServerScope::SCOPE_NOT_ACCESSIBLE) { + if ($access_token) { $token = id(new PhabricatorOAuthServerAccessToken()) ->loadOneWhere('token = %s', $access_token); if (!$token) { @@ -377,25 +368,33 @@ final class PhabricatorConduitAPIController } $oauth_server = new PhabricatorOAuthServer(); - $valid = $oauth_server->validateAccessToken($token, - $method_scope); - if (!$valid) { + $authorization = $oauth_server->authorizeToken($token); + if (!$authorization) { return array( 'ERR-INVALID-AUTH', - pht('Access token is invalid.'), + pht('Access token is invalid or expired.'), ); } - // valid token, so let's log in the user! - $user_phid = $token->getUserPHID(); - $user = id(new PhabricatorUser()) - ->loadOneWhere('phid = %s', $user_phid); + $user = id(new PhabricatorPeopleQuery()) + ->setViewer(PhabricatorUser::getOmnipotentUser()) + ->withPHIDs(array($token->getUserPHID())) + ->executeOne(); if (!$user) { return array( 'ERR-INVALID-AUTH', pht('Access token is for invalid user.'), ); } + + $ok = $this->authorizeOAuthMethodAccess($authorization, $method); + if (!$ok) { + return array( + 'ERR-OAUTH-ACCESS', + pht('You do not have authorization to call this method.'), + ); + } + return $this->validateAuthenticatedUser( $api_request, $user); @@ -642,4 +641,30 @@ final class PhabricatorConduitAPIController return array($metadata, $params); } + private function authorizeOAuthMethodAccess( + PhabricatorOAuthClientAuthorization $authorization, + $method_name) { + + $method = ConduitAPIMethod::getConduitMethod($method_name); + if (!$method) { + return false; + } + + $required_scope = $method->getRequiredScope(); + switch ($required_scope) { + case ConduitAPIMethod::SCOPE_ALWAYS: + return true; + case ConduitAPIMethod::SCOPE_NEVER: + return false; + } + + $authorization_scope = $authorization->getScope(); + if (!empty($authorization_scope[$required_scope])) { + return true; + } + + return false; + } + + } diff --git a/src/applications/conduit/controller/PhabricatorConduitConsoleController.php b/src/applications/conduit/controller/PhabricatorConduitConsoleController.php index a0481126c6..f11415795e 100644 --- a/src/applications/conduit/controller/PhabricatorConduitConsoleController.php +++ b/src/applications/conduit/controller/PhabricatorConduitConsoleController.php @@ -142,6 +142,36 @@ final class PhabricatorConduitConsoleController pht('Errors'), $error_description); + + $scope = $method->getRequiredScope(); + switch ($scope) { + case ConduitAPIMethod::SCOPE_ALWAYS: + $oauth_icon = 'fa-globe green'; + $oauth_description = pht( + 'OAuth clients may always call this method.'); + break; + case ConduitAPIMethod::SCOPE_NEVER: + $oauth_icon = 'fa-ban red'; + $oauth_description = pht( + 'OAuth clients may never call this method.'); + break; + default: + $oauth_icon = 'fa-unlock-alt blue'; + $oauth_description = pht( + 'OAuth clients may call this method after requesting access to '. + 'the "%s" scope.', + $scope); + break; + } + + $view->addProperty( + pht('OAuth Scope'), + array( + id(new PHUIIconView())->setIcon($oauth_icon), + ' ', + $oauth_description, + )); + $view->addSectionHeader( pht('Description'), PHUIPropertyListView::ICON_SUMMARY); $view->addTextContent( diff --git a/src/applications/conduit/method/ConduitAPIMethod.php b/src/applications/conduit/method/ConduitAPIMethod.php index 4fd7c416bb..5b6c16bb93 100644 --- a/src/applications/conduit/method/ConduitAPIMethod.php +++ b/src/applications/conduit/method/ConduitAPIMethod.php @@ -15,6 +15,8 @@ abstract class ConduitAPIMethod const METHOD_STATUS_UNSTABLE = 'unstable'; const METHOD_STATUS_DEPRECATED = 'deprecated'; + const SCOPE_NEVER = 'scope.never'; + const SCOPE_ALWAYS = 'scope.always'; /** * Get a short, human-readable text summary of the method. @@ -108,8 +110,7 @@ abstract class ConduitAPIMethod } public function getRequiredScope() { - // by default, conduit methods are not accessible via OAuth - return PhabricatorOAuthServerScope::SCOPE_NOT_ACCESSIBLE; + return self::SCOPE_NEVER; } public function executeMethod(ConduitAPIRequest $request) { diff --git a/src/applications/conduit/method/ConduitGetCapabilitiesConduitAPIMethod.php b/src/applications/conduit/method/ConduitGetCapabilitiesConduitAPIMethod.php index 44acf3e0d3..2cb84b1f83 100644 --- a/src/applications/conduit/method/ConduitGetCapabilitiesConduitAPIMethod.php +++ b/src/applications/conduit/method/ConduitGetCapabilitiesConduitAPIMethod.php @@ -24,6 +24,10 @@ final class ConduitGetCapabilitiesConduitAPIMethod extends ConduitAPIMethod { return 'dict'; } + public function getRequiredScope() { + return self::SCOPE_ALWAYS; + } + protected function execute(ConduitAPIRequest $request) { $authentication = array( 'token', diff --git a/src/applications/conduit/method/ConduitQueryConduitAPIMethod.php b/src/applications/conduit/method/ConduitQueryConduitAPIMethod.php index 04e8b6d05b..a63d004e5e 100644 --- a/src/applications/conduit/method/ConduitQueryConduitAPIMethod.php +++ b/src/applications/conduit/method/ConduitQueryConduitAPIMethod.php @@ -18,6 +18,10 @@ final class ConduitQueryConduitAPIMethod extends ConduitAPIMethod { return 'dict'; } + public function getRequiredScope() { + return self::SCOPE_ALWAYS; + } + protected function execute(ConduitAPIRequest $request) { $methods = id(new PhabricatorConduitMethodQuery()) ->setViewer($request->getUser()) diff --git a/src/applications/oauthserver/PhabricatorOAuthServer.php b/src/applications/oauthserver/PhabricatorOAuthServer.php index 38b2e34623..dc4a9ab2e9 100644 --- a/src/applications/oauthserver/PhabricatorOAuthServer.php +++ b/src/applications/oauthserver/PhabricatorOAuthServer.php @@ -29,7 +29,6 @@ final class PhabricatorOAuthServer extends Phobject { const AUTHORIZATION_CODE_TIMEOUT = 300; - const ACCESS_TOKEN_TIMEOUT = 3600; private $user; private $client; @@ -158,39 +157,35 @@ final class PhabricatorOAuthServer extends Phobject { /** * @task token */ - public function validateAccessToken( - PhabricatorOAuthServerAccessToken $token, - $required_scope) { + public function authorizeToken( + PhabricatorOAuthServerAccessToken $token) { - $created_time = $token->getDateCreated(); - $must_be_used_by = $created_time + self::ACCESS_TOKEN_TIMEOUT; - $expired = time() > $must_be_used_by; - $authorization = id(new PhabricatorOAuthClientAuthorization()) - ->loadOneWhere( - 'userPHID = %s AND clientPHID = %s', - $token->getUserPHID(), - $token->getClientPHID()); + $user_phid = $token->getUserPHID(); + $client_phid = $token->getClientPHID(); + $authorization = id(new PhabricatorOAuthClientAuthorizationQuery()) + ->setViewer(PhabricatorUser::getOmnipotentUser()) + ->withUserPHIDs(array($user_phid)) + ->withClientPHIDs(array($client_phid)) + ->executeOne(); if (!$authorization) { - return false; - } - $token_scope = $authorization->getScope(); - if (!isset($token_scope[$required_scope])) { - return false; + return null; } - $valid = true; - if ($expired) { - $valid = false; - // check if the scope includes "offline_access", which makes the - // token valid despite being expired - if (isset( - $token_scope[PhabricatorOAuthServerScope::SCOPE_OFFLINE_ACCESS])) { - $valid = true; + // TODO: This should probably be reworked; expiration should be an + // exclusive property of the token. For now, this logic reads: tokens for + // authorizations with "offline_access" never expire. + + $is_expired = $token->isExpired(); + if ($is_expired) { + $offline_access = PhabricatorOAuthServerScope::SCOPE_OFFLINE_ACCESS; + $authorization_scope = $authorization->getScope(); + if (empty($authorization_scope[$offline_access])) { + return null; } } - return $valid; + return $authorization; } /** diff --git a/src/applications/oauthserver/PhabricatorOAuthServerScope.php b/src/applications/oauthserver/PhabricatorOAuthServerScope.php index 4ab6c455d8..105c9cd33d 100644 --- a/src/applications/oauthserver/PhabricatorOAuthServerScope.php +++ b/src/applications/oauthserver/PhabricatorOAuthServerScope.php @@ -4,13 +4,7 @@ final class PhabricatorOAuthServerScope extends Phobject { const SCOPE_OFFLINE_ACCESS = 'offline_access'; const SCOPE_WHOAMI = 'whoami'; - const SCOPE_NOT_ACCESSIBLE = 'not_accessible'; - /* - * Note this does not contain SCOPE_NOT_ACCESSIBLE which is magic - * used to simplify code for data that is not currently accessible - * via OAuth. - */ public static function getScopesDict() { return array( self::SCOPE_OFFLINE_ACCESS => 1, diff --git a/src/applications/oauthserver/controller/PhabricatorOAuthServerTokenController.php b/src/applications/oauthserver/controller/PhabricatorOAuthServerTokenController.php index 7e35574f61..7197ef368d 100644 --- a/src/applications/oauthserver/controller/PhabricatorOAuthServerTokenController.php +++ b/src/applications/oauthserver/controller/PhabricatorOAuthServerTokenController.php @@ -144,7 +144,7 @@ final class PhabricatorOAuthServerTokenController $result = array( 'access_token' => $access_token->getToken(), 'token_type' => 'Bearer', - 'expires_in' => PhabricatorOAuthServer::ACCESS_TOKEN_TIMEOUT, + 'expires_in' => $access_token->getExpiresDuration(), ); return $response->setContent($result); } catch (Exception $e) { diff --git a/src/applications/oauthserver/query/PhabricatorOAuthClientAuthorizationQuery.php b/src/applications/oauthserver/query/PhabricatorOAuthClientAuthorizationQuery.php index f19be14434..a746008f55 100644 --- a/src/applications/oauthserver/query/PhabricatorOAuthClientAuthorizationQuery.php +++ b/src/applications/oauthserver/query/PhabricatorOAuthClientAuthorizationQuery.php @@ -7,7 +7,7 @@ final class PhabricatorOAuthClientAuthorizationQuery private $userPHIDs; private $clientPHIDs; - public function witHPHIDs(array $phids) { + public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } @@ -22,19 +22,12 @@ final class PhabricatorOAuthClientAuthorizationQuery return $this; } + public function newResultObject() { + return new PhabricatorOAuthClientAuthorization(); + } + protected function loadPage() { - $table = new PhabricatorOAuthClientAuthorization(); - $conn_r = $table->establishConnection('r'); - - $data = queryfx_all( - $conn_r, - 'SELECT * FROM %T auth %Q %Q %Q', - $table->getTableName(), - $this->buildWhereClause($conn_r), - $this->buildOrderClause($conn_r), - $this->buildLimitClause($conn_r)); - - return $table->loadAllFromArray($data); + return $this->loadStandardPage($this->newResultObject()); } protected function willFilterPage(array $authorizations) { @@ -49,43 +42,44 @@ final class PhabricatorOAuthClientAuthorizationQuery foreach ($authorizations as $key => $authorization) { $client = idx($clients, $authorization->getClientPHID()); + if (!$client) { + $this->didRejectResult($authorization); unset($authorizations[$key]); continue; } + $authorization->attachClient($client); } return $authorizations; } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { - $where = array(); + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { + $where = parent::buildWhereClauseParts($conn); - if ($this->phids) { + if ($this->phids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'phid IN (%Ls)', $this->phids); } - if ($this->userPHIDs) { + if ($this->userPHIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'userPHID IN (%Ls)', $this->userPHIDs); } - if ($this->clientPHIDs) { + if ($this->clientPHIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'clientPHID IN (%Ls)', $this->clientPHIDs); } - $where[] = $this->buildPagingClause($conn_r); - - return $this->formatWhereClause($where); + return $where; } public function getQueryApplicationClass() { diff --git a/src/applications/oauthserver/storage/PhabricatorOAuthServerAccessToken.php b/src/applications/oauthserver/storage/PhabricatorOAuthServerAccessToken.php index 68b21d23cb..c50cac93ea 100644 --- a/src/applications/oauthserver/storage/PhabricatorOAuthServerAccessToken.php +++ b/src/applications/oauthserver/storage/PhabricatorOAuthServerAccessToken.php @@ -22,4 +22,18 @@ final class PhabricatorOAuthServerAccessToken ) + parent::getConfiguration(); } + public function isExpired() { + $now = PhabricatorTime::getNow(); + $expires_epoch = $this->getExpiresEpoch(); + return ($now > $expires_epoch); + } + + public function getExpiresEpoch() { + return $this->getDateCreated() + 3600; + } + + public function getExpiresDuration() { + return PhabricatorTime::getNow() - $this->getExpiresEpoch(); + } + } From e55522cade3fa137ac32347b917a138eedaf2300 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sun, 3 Apr 2016 09:06:26 -0700 Subject: [PATCH 33/84] Implement "auth.logout" Conduit API method Summary: Ref T7303. Ref T7673. This implements an "auth.logout" which: - terminates all web sessions; - terminates the current OAuth token if called via OAuth; and - may always be called via OAuth. (Since it consumes an OAuth token, even a "malicious" OAuth application can't really be that much of a jerk with this: it can't continuously log you out, since calling the method once kills the token. The application would need to ask your permission again to get a fresh token.) The primary goal here is to let Phacility instances call this against the Phacility upstream, so that when you log out of an instance it also logs you out of your Phacility account (possibly with a checkbox or something). This also smooths over the session token code. Before this change, your sessions would get logged out but when you reloaded we'd tell you your session was invalid. Instead, try to clear the invalid session before telling the user there's an issue. I think that ssentially 100% of invalid sessions are a result of something in this vein (e.g., forced logout via Settings) nowadays, since the session code is generally stable and sane and has been for a long time. Test Plan: - Called `auth.logout` via console, got a reasonable logout experience. - Called `auth.logout` via OAuth. - Tried to make another call, verified OAuth token had been invalidated. - Verified web session had been invalidated. Reviewers: chad Reviewed By: chad Maniphest Tasks: T7303, T7673 Differential Revision: https://secure.phabricator.com/D15594 --- src/__phutil_library_map__.php | 2 + .../PhabricatorAuthLogoutConduitAPIMethod.php | 51 +++++++++++++++++++ .../PhabricatorAuthStartController.php | 29 ++++++++--- .../PhabricatorConduitAPIController.php | 2 + .../conduit/protocol/ConduitAPIRequest.php | 11 ++++ 5 files changed, 89 insertions(+), 6 deletions(-) create mode 100644 src/applications/auth/conduit/PhabricatorAuthLogoutConduitAPIMethod.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index f43540c166..1020bad54d 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1807,6 +1807,7 @@ phutil_register_library_map(array( 'PhabricatorAuthListController' => 'applications/auth/controller/config/PhabricatorAuthListController.php', 'PhabricatorAuthLoginController' => 'applications/auth/controller/PhabricatorAuthLoginController.php', 'PhabricatorAuthLoginHandler' => 'applications/auth/handler/PhabricatorAuthLoginHandler.php', + 'PhabricatorAuthLogoutConduitAPIMethod' => 'applications/auth/conduit/PhabricatorAuthLogoutConduitAPIMethod.php', 'PhabricatorAuthMainMenuBarExtension' => 'applications/auth/extension/PhabricatorAuthMainMenuBarExtension.php', 'PhabricatorAuthManagementCachePKCS8Workflow' => 'applications/auth/management/PhabricatorAuthManagementCachePKCS8Workflow.php', 'PhabricatorAuthManagementLDAPWorkflow' => 'applications/auth/management/PhabricatorAuthManagementLDAPWorkflow.php', @@ -6149,6 +6150,7 @@ phutil_register_library_map(array( 'PhabricatorAuthListController' => 'PhabricatorAuthProviderConfigController', 'PhabricatorAuthLoginController' => 'PhabricatorAuthController', 'PhabricatorAuthLoginHandler' => 'Phobject', + 'PhabricatorAuthLogoutConduitAPIMethod' => 'PhabricatorAuthConduitAPIMethod', 'PhabricatorAuthMainMenuBarExtension' => 'PhabricatorMainMenuBarExtension', 'PhabricatorAuthManagementCachePKCS8Workflow' => 'PhabricatorAuthManagementWorkflow', 'PhabricatorAuthManagementLDAPWorkflow' => 'PhabricatorAuthManagementWorkflow', diff --git a/src/applications/auth/conduit/PhabricatorAuthLogoutConduitAPIMethod.php b/src/applications/auth/conduit/PhabricatorAuthLogoutConduitAPIMethod.php new file mode 100644 index 0000000000..2dd485d8f0 --- /dev/null +++ b/src/applications/auth/conduit/PhabricatorAuthLogoutConduitAPIMethod.php @@ -0,0 +1,51 @@ +getUser(); + + // Destroy all web sessions. + $engine = id(new PhabricatorAuthSessionEngine()); + $engine->terminateLoginSessions($viewer); + + // If we were called via OAuth, destroy the OAuth token. + $oauth_token = $request->getOAuthToken(); + if ($oauth_token) { + $oauth_token->delete(); + } + + return null; + } + +} diff --git a/src/applications/auth/controller/PhabricatorAuthStartController.php b/src/applications/auth/controller/PhabricatorAuthStartController.php index d591b6313f..633339a356 100644 --- a/src/applications/auth/controller/PhabricatorAuthStartController.php +++ b/src/applications/auth/controller/PhabricatorAuthStartController.php @@ -29,6 +29,7 @@ final class PhabricatorAuthStartController // it and warn the user they may need to nuke their cookies. $session_token = $request->getCookie(PhabricatorCookies::COOKIE_SESSION); + $did_clear = $request->getStr('cleared'); if (strlen($session_token)) { $kind = PhabricatorAuthSessionEngine::getSessionKindFromToken( @@ -39,18 +40,34 @@ final class PhabricatorAuthStartController // be logged in, so we can just continue. break; default: - // The session cookie is invalid, so clear it. + // The session cookie is invalid, so try to clear it. $request->clearCookie(PhabricatorCookies::COOKIE_USERNAME); $request->clearCookie(PhabricatorCookies::COOKIE_SESSION); - return $this->renderError( - pht( - 'Your login session is invalid. Try reloading the page and '. - 'logging in again. If that does not work, clear your browser '. - 'cookies.')); + // We've previously tried to clear the cookie but we ended up back + // here, so it didn't work. Hard fatal instead of trying again. + if ($did_clear) { + return $this->renderError( + pht( + 'Your login session is invalid, and clearing the session '. + 'cookie was unsuccessful. Try clearing your browser cookies.')); + } + + $redirect_uri = $request->getRequestURI(); + $redirect_uri->setQueryParam('cleared', 1); + return id(new AphrontRedirectResponse())->setURI($redirect_uri); } } + // If we just cleared the session cookie and it worked, clean up after + // ourselves by redirecting to get rid of the "cleared" parameter. The + // the workflow will continue normally. + if ($did_clear) { + $redirect_uri = $request->getRequestURI(); + $redirect_uri->setQueryParam('cleared', null); + return id(new AphrontRedirectResponse())->setURI($redirect_uri); + } + $providers = PhabricatorAuthProvider::getAllEnabledProviders(); foreach ($providers as $key => $provider) { if (!$provider->shouldAllowLogin()) { diff --git a/src/applications/conduit/controller/PhabricatorConduitAPIController.php b/src/applications/conduit/controller/PhabricatorConduitAPIController.php index ac0d115a40..e335772b7f 100644 --- a/src/applications/conduit/controller/PhabricatorConduitAPIController.php +++ b/src/applications/conduit/controller/PhabricatorConduitAPIController.php @@ -395,6 +395,8 @@ final class PhabricatorConduitAPIController ); } + $api_request->setOAuthToken($token); + return $this->validateAuthenticatedUser( $api_request, $user); diff --git a/src/applications/conduit/protocol/ConduitAPIRequest.php b/src/applications/conduit/protocol/ConduitAPIRequest.php index 3e322fcb32..47cc31fba0 100644 --- a/src/applications/conduit/protocol/ConduitAPIRequest.php +++ b/src/applications/conduit/protocol/ConduitAPIRequest.php @@ -5,6 +5,7 @@ final class ConduitAPIRequest extends Phobject { protected $params; private $user; private $isClusterRequest = false; + private $oauthToken; public function __construct(array $params) { $this->params = $params; @@ -48,6 +49,16 @@ final class ConduitAPIRequest extends Phobject { return $this->user; } + public function setOAuthToken( + PhabricatorOAuthServerAccessToken $oauth_token) { + $this->oauthToken = $oauth_token; + return $this; + } + + public function getOAuthToken() { + return $this->oauthToken; + } + public function setIsClusterRequest($is_cluster_request) { $this->isClusterRequest = $is_cluster_request; return $this; From 5f957807a725487a09a18057241e02a6c7344e6c Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 4 Apr 2016 07:13:23 -0700 Subject: [PATCH 34/84] Update OAuthServer for modern SearchEngine fields Summary: Ref T7303. Small modernization. Test Plan: - Searched by various users. - Viewed all, reordered, etc. Reviewers: chad Reviewed By: chad Maniphest Tasks: T7303 Differential Revision: https://secure.phabricator.com/D15606 --- ...abricatorOAuthServerClientSearchEngine.php | 42 +++++++------------ 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/src/applications/oauthserver/query/PhabricatorOAuthServerClientSearchEngine.php b/src/applications/oauthserver/query/PhabricatorOAuthServerClientSearchEngine.php index 212ae5218a..6b51a880b4 100644 --- a/src/applications/oauthserver/query/PhabricatorOAuthServerClientSearchEngine.php +++ b/src/applications/oauthserver/query/PhabricatorOAuthServerClientSearchEngine.php @@ -11,40 +11,30 @@ final class PhabricatorOAuthServerClientSearchEngine return 'PhabricatorOAuthServerApplication'; } - public function buildSavedQueryFromRequest(AphrontRequest $request) { - $saved = new PhabricatorSavedQuery(); - - $saved->setParameter( - 'creatorPHIDs', - $this->readUsersFromRequest($request, 'creators')); - - return $saved; + public function newQuery() { + return id(new PhabricatorOAuthServerClientQuery()); } - public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { - $query = id(new PhabricatorOAuthServerClientQuery()); + protected function buildQueryFromParameters(array $map) { + $query = $this->newQuery(); - $creator_phids = $saved->getParameter('creatorPHIDs', array()); - if ($creator_phids) { - $query->withCreatorPHIDs($saved->getParameter('creatorPHIDs', array())); + if ($map['creatorPHIDs']) { + $query->withCreatorPHIDs($map['creatorPHIDs']); } return $query; } - public function buildSearchForm( - AphrontFormView $form, - PhabricatorSavedQuery $saved_query) { - - $creator_phids = $saved_query->getParameter('creatorPHIDs', array()); - - $form - ->appendControl( - id(new AphrontFormTokenizerControl()) - ->setDatasource(new PhabricatorPeopleDatasource()) - ->setName('creators') - ->setLabel(pht('Creators')) - ->setValue($creator_phids)); + protected function buildCustomSearchFields() { + return array( + id(new PhabricatorUsersSearchField()) + ->setAliases(array('creators')) + ->setKey('creatorPHIDs') + ->setConduitKey('creators') + ->setLabel(pht('Creators')) + ->setDescription( + pht('Search for applications created by particular users.')), + ); } protected function getURI($path) { From b07a524b4b078cda65a26950a567953084ebc265 Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 4 Apr 2016 09:40:41 -0700 Subject: [PATCH 35/84] Fix resolution of commits in SVN repositories without callsigns Summary: Fixes T10721. When trying to load commits by identifier, we would take some bad pathways in Subversion if the repository had no callsign and end up missing the commits. Fix this logic so it works for either callsigns (e.g., if passed `rXyyy`) or with PHIDs if passed repositories. Test Plan: - Viewed SVN commit in a Subversion repository with no callsign. - Added a callsign, looked at it again. - Viewed non-SVN commits in callsign and non-callsign repositories. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10721 Differential Revision: https://secure.phabricator.com/D15607 --- .../diffusion/query/DiffusionCommitQuery.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/applications/diffusion/query/DiffusionCommitQuery.php b/src/applications/diffusion/query/DiffusionCommitQuery.php index ec76468332..4aeca4c38e 100644 --- a/src/applications/diffusion/query/DiffusionCommitQuery.php +++ b/src/applications/diffusion/query/DiffusionCommitQuery.php @@ -364,7 +364,7 @@ final class DiffusionCommitQuery if ($repo === null) { if ($this->defaultRepository) { - $repo = $this->defaultRepository->getCallsign(); + $repo = $this->defaultRepository->getPHID(); } } @@ -375,7 +375,7 @@ final class DiffusionCommitQuery $bare[] = $commit_identifier; } else { $refs[] = array( - 'callsign' => $repo, + 'repository' => $repo, 'identifier' => $commit_identifier, ); } @@ -392,17 +392,16 @@ final class DiffusionCommitQuery } if ($refs) { - $callsigns = ipull($refs, 'callsign'); + $repositories = ipull($refs, 'repository'); $repos = id(new PhabricatorRepositoryQuery()) ->setViewer($this->getViewer()) - ->withIdentifiers($callsigns); + ->withIdentifiers($repositories); $repos->execute(); $repos = $repos->getIdentifierMap(); foreach ($refs as $key => $ref) { - $repo = idx($repos, $ref['callsign']); - + $repo = idx($repos, $ref['repository']); if (!$repo) { continue; } From 3317086fdb0cc537a1308c5369e9f21199bdafc7 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Mon, 4 Apr 2016 10:31:58 -0700 Subject: [PATCH 36/84] Convert missing commit page to newPage Summary: Minor conversion. Test Plan: Fake a missing commit. View same layout. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15608 --- .../controller/DiffusionCommitController.php | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/applications/diffusion/controller/DiffusionCommitController.php b/src/applications/diffusion/controller/DiffusionCommitController.php index 890c512870..69c40b044f 100644 --- a/src/applications/diffusion/controller/DiffusionCommitController.php +++ b/src/applications/diffusion/controller/DiffusionCommitController.php @@ -57,14 +57,13 @@ final class DiffusionCommitController extends DiffusionController { 'Failed to load the commit because the commit has not been '. 'parsed yet.')); - return $this->buildApplicationPage( - array( - $crumbs, - $error, - ), - array( - 'title' => pht('Commit Still Parsing'), - )); + $title = pht('Commit Still Parsing'); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($error); + } $audit_requests = $commit->getAudits(); From 23979a05aa95d3b43216e2f3f13caf67678c20e0 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Mon, 4 Apr 2016 19:26:05 +0000 Subject: [PATCH 37/84] Upate notifications for newPage Summary: Single callsite, swap to `newPage` Test Plan: Visit page, see same status message. Also remove device ready flag. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15610 --- .../PhabricatorNotificationStatusController.php | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/applications/notification/controller/PhabricatorNotificationStatusController.php b/src/applications/notification/controller/PhabricatorNotificationStatusController.php index eb79a3a8c5..49d3fb9d9b 100644 --- a/src/applications/notification/controller/PhabricatorNotificationStatusController.php +++ b/src/applications/notification/controller/PhabricatorNotificationStatusController.php @@ -24,15 +24,12 @@ final class PhabricatorNotificationStatusController $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Status')); - return $this->buildApplicationPage( - array( - $crumbs, - $status, - ), - array( - 'title' => pht('Notification Server Status'), - 'device' => false, - )); + $title = pht('Notification Server Status'); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($status); } private function renderServerStatus(array $status) { From 968a75b5792c5f9aab3644ad93a64b138696c13c Mon Sep 17 00:00:00 2001 From: Chad Little Date: Mon, 4 Apr 2016 12:37:32 -0700 Subject: [PATCH 38/84] Update typeahead for new UI Summary: Uses modern UI, `newPage`, etc. Changes table behavior to always scroll if too large for container, can't find anything this breaks, but be on the lookout. Test Plan: Pull up help and view pages, search for some people and projects. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15611 --- resources/celerity/map.php | 6 +++--- ...ricatorTypeaheadFunctionHelpController.php | 15 ++++++-------- ...orTypeaheadModularDatasourceController.php | 20 +++++++++++++------ webroot/rsrc/css/aphront/table-view.css | 3 +-- 4 files changed, 24 insertions(+), 20 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 2b6873c89d..930a2e5b41 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -7,7 +7,7 @@ */ return array( 'names' => array( - 'core.pkg.css' => '5de0f7af', + 'core.pkg.css' => 'a3016dac', 'core.pkg.js' => 'e5484f37', 'darkconsole.pkg.js' => 'e7393ebb', 'differential.pkg.css' => '7ba78475', @@ -25,7 +25,7 @@ return array( 'rsrc/css/aphront/notification.css' => '7f684b62', 'rsrc/css/aphront/panel-view.css' => '8427b78d', 'rsrc/css/aphront/phabricator-nav-view.css' => 'ac79a758', - 'rsrc/css/aphront/table-view.css' => '036b6cdc', + 'rsrc/css/aphront/table-view.css' => '9258e19f', 'rsrc/css/aphront/tokenizer.css' => '056da01b', 'rsrc/css/aphront/tooltip.css' => '1a07aea8', 'rsrc/css/aphront/typeahead-browse.css' => 'd8581d2c', @@ -526,7 +526,7 @@ return array( 'aphront-list-filter-view-css' => '5d6f0526', 'aphront-multi-column-view-css' => 'fd18389d', 'aphront-panel-view-css' => '8427b78d', - 'aphront-table-view-css' => '036b6cdc', + 'aphront-table-view-css' => '9258e19f', 'aphront-tokenizer-control-css' => '056da01b', 'aphront-tooltip-css' => '1a07aea8', 'aphront-typeahead-control-css' => 'd4f16145', diff --git a/src/applications/typeahead/controller/PhabricatorTypeaheadFunctionHelpController.php b/src/applications/typeahead/controller/PhabricatorTypeaheadFunctionHelpController.php index a771bf0747..3084b2d434 100644 --- a/src/applications/typeahead/controller/PhabricatorTypeaheadFunctionHelpController.php +++ b/src/applications/typeahead/controller/PhabricatorTypeaheadFunctionHelpController.php @@ -126,21 +126,18 @@ final class PhabricatorTypeaheadFunctionHelpController $header = id(new PHUIHeaderView()) ->setHeader($title); - $document = id(new PHUIDocumentView()) + $document = id(new PHUIDocumentViewPro()) ->setHeader($header) ->appendChild($content_box); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Function Help')); + $crumbs->setBorder(true); - return $this->buildApplicationPage( - array( - $crumbs, - $document, - ), - array( - 'title' => $title, - )); + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($document); } } diff --git a/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php b/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php index b3687b85e8..15650a33fd 100644 --- a/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php +++ b/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php @@ -309,6 +309,7 @@ final class PhabricatorTypeaheadModularDatasourceController $form_box = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Token Query')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setForm($form); $table = new AphrontTableView($content); @@ -329,17 +330,24 @@ final class PhabricatorTypeaheadModularDatasourceController $result_box = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Token Results (%s)', $class)) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($table); - return $this->buildApplicationPage( - array( + $title = pht('Typeahead Results'); + + $header = id(new PHUIHeaderView()) + ->setHeader($title); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter(array( $form_box, $result_box, - ), - array( - 'title' => pht('Typeahead Results'), - 'device' => false, )); + + return $this->newPage() + ->setTitle($title) + ->appendChild($view); } } diff --git a/webroot/rsrc/css/aphront/table-view.css b/webroot/rsrc/css/aphront/table-view.css index b2efc8c3c0..af304c930a 100644 --- a/webroot/rsrc/css/aphront/table-view.css +++ b/webroot/rsrc/css/aphront/table-view.css @@ -2,8 +2,7 @@ * @provides aphront-table-view-css */ -.device-phone .aphront-table-wrap, -.device-tablet .aphront-table-wrap { +.aphront-table-wrap { overflow-x: auto; } From 0b54810ba120316245a4ced2fc48022307e74214 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Mon, 4 Apr 2016 13:37:44 -0700 Subject: [PATCH 39/84] Update Passphrase Edit/Create UI Summary: Updates pages to modern UI, newPage Test Plan: Create Crediential, Edit Credential Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15612 --- .../PassphraseCredentialCreateController.php | 24 ++++++++----- .../PassphraseCredentialEditController.php | 35 +++++++++++-------- .../PassphraseCredentialPublicController.php | 4 +-- 3 files changed, 36 insertions(+), 27 deletions(-) diff --git a/src/applications/passphrase/controller/PassphraseCredentialCreateController.php b/src/applications/passphrase/controller/PassphraseCredentialCreateController.php index cfddcbcc4a..87afe22cc6 100644 --- a/src/applications/passphrase/controller/PassphraseCredentialCreateController.php +++ b/src/applications/passphrase/controller/PassphraseCredentialCreateController.php @@ -49,20 +49,26 @@ final class PassphraseCredentialCreateController extends PassphraseController { $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Create')); + $crumbs->setBorder(true); $box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Create New Credential')) + ->setHeaderText(pht('Credential')) ->setFormErrors($errors) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setForm($form); - return $this->buildApplicationPage( - array( - $crumbs, - $box, - ), - array( - 'title' => $title, - )); + $header = id(new PHUIHeaderView()) + ->setHeader($title) + ->setHeaderIcon('fa-plus-square'); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter($box); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); } } diff --git a/src/applications/passphrase/controller/PassphraseCredentialEditController.php b/src/applications/passphrase/controller/PassphraseCredentialEditController.php index 72c21a9e39..4aa687ddc1 100644 --- a/src/applications/passphrase/controller/PassphraseCredentialEditController.php +++ b/src/applications/passphrase/controller/PassphraseCredentialEditController.php @@ -311,20 +311,21 @@ final class PassphraseCredentialEditController extends PassphraseController { } $crumbs = $this->buildApplicationCrumbs(); + $crumbs->setBorder(true); if ($is_new) { - $title = pht('Create Credential'); - $header = pht('Create New Credential'); + $title = pht('Create New Credential'); $crumbs->addTextCrumb(pht('Create')); $cancel_uri = $this->getApplicationURI(); + $header_icon = 'fa-plus-square'; } else { - $title = pht('Edit Credential'); - $header = pht('Edit Credential %s', 'K'.$credential->getID()); + $title = pht('Edit Credential: %s', $credential->getName()); $crumbs->addTextCrumb( 'K'.$credential->getID(), '/K'.$credential->getID()); $crumbs->addTextCrumb(pht('Edit')); $cancel_uri = '/K'.$credential->getID(); + $header_icon = 'fa-pencil'; } if ($request->isAjax()) { @@ -332,16 +333,13 @@ final class PassphraseCredentialEditController extends PassphraseController { $errors = id(new PHUIInfoView())->setErrors($errors); } - $dialog = id(new AphrontDialogView()) - ->setUser($viewer) + return $this->newDialog() ->setWidth(AphrontDialogView::WIDTH_FORM) ->setTitle($title) ->appendChild($errors) ->appendChild($form->buildLayoutView()) ->addSubmitButton(pht('Create Credential')) ->addCancelButton($cancel_uri); - - return id(new AphrontDialogResponse())->setDialog($dialog); } $form->appendChild( @@ -350,19 +348,26 @@ final class PassphraseCredentialEditController extends PassphraseController { ->addCancelButton($cancel_uri)); $box = id(new PHUIObjectBoxView()) - ->setHeaderText($header) + ->setHeaderText(pht('Credential')) ->setFormErrors($errors) ->setValidationException($validation_exception) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setForm($form); - return $this->buildApplicationPage( - array( - $crumbs, + $header = id(new PHUIHeaderView()) + ->setHeader($title) + ->setHeaderIcon($header_icon); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter(array( $box, - ), - array( - 'title' => $title, )); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); } private function getCredentialType($type_const) { diff --git a/src/applications/passphrase/controller/PassphraseCredentialPublicController.php b/src/applications/passphrase/controller/PassphraseCredentialPublicController.php index 56fc6ac4ae..481d111fd9 100644 --- a/src/applications/passphrase/controller/PassphraseCredentialPublicController.php +++ b/src/applications/passphrase/controller/PassphraseCredentialPublicController.php @@ -40,14 +40,12 @@ final class PassphraseCredentialPublicController ->setReadOnly(true) ->setValue($public_key)); - $dialog = id(new AphrontDialogView()) - ->setUser($viewer) + return $this->newDialog() ->setWidth(AphrontDialogView::WIDTH_FORM) ->setTitle(pht('Public Key (%s)', $credential->getMonogram())) ->appendChild($body) ->addCancelButton($view_uri, pht('Done')); - return id(new AphrontDialogResponse())->setDialog($dialog); } } From e2685a248b8c3071ab5619506ee7d2c2171f03c5 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Mon, 4 Apr 2016 16:30:55 -0700 Subject: [PATCH 40/84] Update Conduit for new UI Summary: View various conduit pages and update to new UI and add calls to newPage Test Plan: View list, view method, make a call. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15613 --- .../PhabricatorConduitAPIController.php | 39 ++++++++++++------- .../PhabricatorConduitConsoleController.php | 34 +++++++++------- .../PhabricatorConduitController.php | 1 + .../PhabricatorConduitTokenController.php | 23 +++++++---- .../PhabricatorSearchEngineAPIMethod.php | 6 +++ .../PhabricatorEditEngineAPIMethod.php | 2 + 6 files changed, 69 insertions(+), 36 deletions(-) diff --git a/src/applications/conduit/controller/PhabricatorConduitAPIController.php b/src/applications/conduit/controller/PhabricatorConduitAPIController.php index e335772b7f..005b23d505 100644 --- a/src/applications/conduit/controller/PhabricatorConduitAPIController.php +++ b/src/applications/conduit/controller/PhabricatorConduitAPIController.php @@ -511,19 +511,22 @@ final class PhabricatorConduitAPIController 'wide', )); - $param_panel = new PHUIObjectBoxView(); - $param_panel->setHeaderText(pht('Method Parameters')); - $param_panel->setTable($param_table); + $param_panel = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Method Parameters')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->setTable($param_table); - $result_panel = new PHUIObjectBoxView(); - $result_panel->setHeaderText(pht('Method Result')); - $result_panel->setTable($result_table); + $result_panel = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Method Result')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->setTable($result_table); $method_uri = $this->getApplicationURI('method/'.$method.'/'); $crumbs = $this->buildApplicationCrumbs() ->addTextCrumb($method, $method_uri) - ->addTextCrumb(pht('Call')); + ->addTextCrumb(pht('Call')) + ->setBorder(true); $example_panel = null; if ($request && $method_implementation) { @@ -533,16 +536,26 @@ final class PhabricatorConduitAPIController $params); } - return $this->buildApplicationPage( - array( - $crumbs, + $title = pht('Method Call Result'); + $header = id(new PHUIHeaderView()) + ->setHeader($title) + ->setHeaderIcon('fa-exchange'); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter(array( $param_panel, $result_panel, $example_panel, - ), - array( - 'title' => pht('Method Call Result'), )); + + $title = pht('Method Call Result'); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); + } private function renderAPIValue($value) { diff --git a/src/applications/conduit/controller/PhabricatorConduitConsoleController.php b/src/applications/conduit/controller/PhabricatorConduitConsoleController.php index f11415795e..5ee76b44f7 100644 --- a/src/applications/conduit/controller/PhabricatorConduitConsoleController.php +++ b/src/applications/conduit/controller/PhabricatorConduitConsoleController.php @@ -85,37 +85,41 @@ final class PhabricatorConduitConsoleController $header = id(new PHUIHeaderView()) ->setUser($viewer) - ->setHeader($method->getAPIMethodName()); + ->setHeader($method->getAPIMethodName()) + ->setHeaderIcon('fa-tty'); $form_box = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Call Method')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setForm($form); - $content = array(); - $properties = $this->buildMethodProperties($method); $info_box = id(new PHUIObjectBoxView()) ->setHeaderText(pht('API Method: %s', $method->getAPIMethodName())) ->setFormErrors($errors) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($properties); - $content[] = $info_box; - $content[] = $method->getMethodDocumentation(); - $content[] = $form_box; - $content[] = $this->renderExampleBox($method, null); - $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb($method->getAPIMethodName()); + $crumbs->setBorder(true); - return $this->buildApplicationPage( - array( - $crumbs, - $content, - ), - array( - 'title' => $method->getAPIMethodName(), + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter(array( + $info_box, + $method->getMethodDocumentation(), + $form_box, + $this->renderExampleBox($method, null), )); + + $title = $method->getAPIMethodName(); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); } private function buildMethodProperties(ConduitAPIMethod $method) { diff --git a/src/applications/conduit/controller/PhabricatorConduitController.php b/src/applications/conduit/controller/PhabricatorConduitController.php index 4fa11dbfad..000d01f888 100644 --- a/src/applications/conduit/controller/PhabricatorConduitController.php +++ b/src/applications/conduit/controller/PhabricatorConduitController.php @@ -56,6 +56,7 @@ abstract class PhabricatorConduitController extends PhabricatorController { return id(new PHUIObjectBoxView()) ->setHeaderText(pht('Examples')) ->setInfoView($info_view) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->addPropertyList($arc_example, pht('arc call-conduit')) ->addPropertyList($curl_example, pht('cURL')) ->addPropertyList($php_example, pht('PHP')); diff --git a/src/applications/conduit/controller/PhabricatorConduitTokenController.php b/src/applications/conduit/controller/PhabricatorConduitTokenController.php index b5501a2805..fe6d676b68 100644 --- a/src/applications/conduit/controller/PhabricatorConduitTokenController.php +++ b/src/applications/conduit/controller/PhabricatorConduitTokenController.php @@ -55,19 +55,26 @@ final class PhabricatorConduitTokenController $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Install Certificate')); + $crumbs->setBorder(true); $object_box = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Certificate Token')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setForm($form); - return $this->buildApplicationPage( - array( - $crumbs, - $object_box, - ), - array( - 'title' => pht('Certificate Install Token'), - )); + $title = pht('Certificate Install Token'); + + $header = id(new PHUIHeaderView()) + ->setHeader($title); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter($object_box); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); } } diff --git a/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php b/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php index f411c208bf..b5cd52471b 100644 --- a/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php +++ b/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php @@ -151,6 +151,7 @@ EOTEXT return id(new PHUIObjectBoxView()) ->setHeaderText(pht('Builtin and Saved Queries')) ->setCollapsed(true) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($this->buildRemarkup($info)) ->appendChild($table); } @@ -236,6 +237,7 @@ EOTEXT return id(new PHUIObjectBoxView()) ->setHeaderText(pht('Custom Query Constraints')) ->setCollapsed(true) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($this->buildRemarkup($info)) ->appendChild($table); } @@ -342,6 +344,7 @@ EOTEXT return id(new PHUIObjectBoxView()) ->setHeaderText(pht('Result Ordering')) ->setCollapsed(true) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($this->buildRemarkup($orders_info)) ->appendChild($orders_table) ->appendChild($this->buildRemarkup($columns_info)) @@ -422,6 +425,7 @@ EOTEXT return id(new PHUIObjectBoxView()) ->setHeaderText(pht('Object Fields')) ->setCollapsed(true) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($this->buildRemarkup($info)) ->appendChild($table); } @@ -510,6 +514,7 @@ EOTEXT return id(new PHUIObjectBoxView()) ->setHeaderText(pht('Attachments')) ->setCollapsed(true) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($this->buildRemarkup($info)) ->appendChild($table); } @@ -580,6 +585,7 @@ EOTEXT return id(new PHUIObjectBoxView()) ->setHeaderText(pht('Paging and Limits')) ->setCollapsed(true) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($this->buildRemarkup($info)); } diff --git a/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php b/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php index 3cf04aeca8..b6f95ecd1b 100644 --- a/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php @@ -94,6 +94,7 @@ abstract class PhabricatorEditEngineAPIMethod $boxes[] = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Transaction Types')) ->setCollapsed(true) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($this->buildRemarkup($summary_info)) ->appendChild($summary_table); @@ -140,6 +141,7 @@ abstract class PhabricatorEditEngineAPIMethod $boxes[] = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Transaction Type: %s', $type->getEditType())) ->setCollapsed(true) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($this->buildRemarkup($section)) ->appendChild($type_table); } From 57f016b166de8d79aaf3d164268b034af96aae16 Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 4 Apr 2016 09:27:42 -0700 Subject: [PATCH 41/84] Convert OAuthServer to Transactions + EditEngine Summary: Ref T7303. This application is currently stone-age tech (no transactions, hard "delete" action). Bring it up to modern specs. Test Plan: - Created and edited an OAuth application. - Viewed transaction record. - Tried to create something with no name, invalid redirect URI, etc. Was gently rebuffed with detailed explanatory errors. Reviewers: chad Reviewed By: chad Maniphest Tasks: T7303 Differential Revision: https://secure.phabricator.com/D15609 --- .../autopatches/20160404.oauth.1.xaction.sql | 19 +++ src/__phutil_library_map__.php | 9 ++ .../oauthserver/PhabricatorOAuthServer.php | 47 ++++-- .../PhabricatorOAuthServerApplication.php | 4 +- .../PhabricatorOAuthClientEditController.php | 126 +--------------- .../PhabricatorOAuthClientListController.php | 30 +--- .../PhabricatorOAuthClientViewController.php | 11 +- .../PhabricatorOAuthServerEditEngine.php | 100 +++++++++++++ .../editor/PhabricatorOAuthServerEditor.php | 137 ++++++++++++++++++ ...PhabricatorOAuthServerTransactionQuery.php | 10 ++ .../storage/PhabricatorOAuthServerClient.php | 27 +++- .../PhabricatorOAuthServerTransaction.php | 52 +++++++ src/infrastructure/env/PhabricatorEnv.php | 10 +- 13 files changed, 416 insertions(+), 166 deletions(-) create mode 100644 resources/sql/autopatches/20160404.oauth.1.xaction.sql create mode 100644 src/applications/oauthserver/editor/PhabricatorOAuthServerEditEngine.php create mode 100644 src/applications/oauthserver/editor/PhabricatorOAuthServerEditor.php create mode 100644 src/applications/oauthserver/query/PhabricatorOAuthServerTransactionQuery.php create mode 100644 src/applications/oauthserver/storage/PhabricatorOAuthServerTransaction.php diff --git a/resources/sql/autopatches/20160404.oauth.1.xaction.sql b/resources/sql/autopatches/20160404.oauth.1.xaction.sql new file mode 100644 index 0000000000..70b7065ea2 --- /dev/null +++ b/resources/sql/autopatches/20160404.oauth.1.xaction.sql @@ -0,0 +1,19 @@ +CREATE TABLE {$NAMESPACE}_oauth_server.oauth_server_transaction ( + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, + phid VARBINARY(64) NOT NULL, + authorPHID VARBINARY(64) NOT NULL, + objectPHID VARBINARY(64) NOT NULL, + viewPolicy VARBINARY(64) NOT NULL, + editPolicy VARBINARY(64) NOT NULL, + commentPHID VARBINARY(64) DEFAULT NULL, + commentVersion INT UNSIGNED NOT NULL, + transactionType VARCHAR(32) COLLATE {$COLLATE_TEXT} NOT NULL, + oldValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, + newValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, + contentSource LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, + metadata LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, + dateCreated INT UNSIGNED NOT NULL, + dateModified INT UNSIGNED NOT NULL, + UNIQUE KEY `key_phid` (`phid`), + KEY `key_object` (`objectPHID`) +) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 1020bad54d..a28ea67874 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2723,9 +2723,13 @@ phutil_register_library_map(array( 'PhabricatorOAuthServerController' => 'applications/oauthserver/controller/PhabricatorOAuthServerController.php', 'PhabricatorOAuthServerCreateClientsCapability' => 'applications/oauthserver/capability/PhabricatorOAuthServerCreateClientsCapability.php', 'PhabricatorOAuthServerDAO' => 'applications/oauthserver/storage/PhabricatorOAuthServerDAO.php', + 'PhabricatorOAuthServerEditEngine' => 'applications/oauthserver/editor/PhabricatorOAuthServerEditEngine.php', + 'PhabricatorOAuthServerEditor' => 'applications/oauthserver/editor/PhabricatorOAuthServerEditor.php', 'PhabricatorOAuthServerScope' => 'applications/oauthserver/PhabricatorOAuthServerScope.php', 'PhabricatorOAuthServerTestCase' => 'applications/oauthserver/__tests__/PhabricatorOAuthServerTestCase.php', 'PhabricatorOAuthServerTokenController' => 'applications/oauthserver/controller/PhabricatorOAuthServerTokenController.php', + 'PhabricatorOAuthServerTransaction' => 'applications/oauthserver/storage/PhabricatorOAuthServerTransaction.php', + 'PhabricatorOAuthServerTransactionQuery' => 'applications/oauthserver/query/PhabricatorOAuthServerTransactionQuery.php', 'PhabricatorObjectHandle' => 'applications/phid/PhabricatorObjectHandle.php', 'PhabricatorObjectHasAsanaSubtaskEdgeType' => 'applications/doorkeeper/edge/PhabricatorObjectHasAsanaSubtaskEdgeType.php', 'PhabricatorObjectHasAsanaTaskEdgeType' => 'applications/doorkeeper/edge/PhabricatorObjectHasAsanaTaskEdgeType.php', @@ -7206,6 +7210,7 @@ phutil_register_library_map(array( 'PhabricatorOAuthServerClient' => array( 'PhabricatorOAuthServerDAO', 'PhabricatorPolicyInterface', + 'PhabricatorApplicationTransactionInterface', 'PhabricatorDestructibleInterface', ), 'PhabricatorOAuthServerClientAuthorizationPHIDType' => 'PhabricatorPHIDType', @@ -7215,9 +7220,13 @@ phutil_register_library_map(array( 'PhabricatorOAuthServerController' => 'PhabricatorController', 'PhabricatorOAuthServerCreateClientsCapability' => 'PhabricatorPolicyCapability', 'PhabricatorOAuthServerDAO' => 'PhabricatorLiskDAO', + 'PhabricatorOAuthServerEditEngine' => 'PhabricatorEditEngine', + 'PhabricatorOAuthServerEditor' => 'PhabricatorApplicationTransactionEditor', 'PhabricatorOAuthServerScope' => 'Phobject', 'PhabricatorOAuthServerTestCase' => 'PhabricatorTestCase', 'PhabricatorOAuthServerTokenController' => 'PhabricatorOAuthServerController', + 'PhabricatorOAuthServerTransaction' => 'PhabricatorApplicationTransaction', + 'PhabricatorOAuthServerTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'PhabricatorObjectHandle' => array( 'Phobject', 'PhabricatorPolicyInterface', diff --git a/src/applications/oauthserver/PhabricatorOAuthServer.php b/src/applications/oauthserver/PhabricatorOAuthServer.php index dc4a9ab2e9..edb93ac974 100644 --- a/src/applications/oauthserver/PhabricatorOAuthServer.php +++ b/src/applications/oauthserver/PhabricatorOAuthServer.php @@ -188,24 +188,49 @@ final class PhabricatorOAuthServer extends Phobject { return $authorization; } + public function validateRedirectURI($uri) { + try { + $this->assertValidRedirectURI($uri); + return true; + } catch (Exception $ex) { + return false; + } + } + /** * See http://tools.ietf.org/html/draft-ietf-oauth-v2-23#section-3.1.2 * for details on what makes a given redirect URI "valid". */ - public function validateRedirectURI(PhutilURI $uri) { - if (!PhabricatorEnv::isValidRemoteURIForLink($uri)) { - return false; + public function assertValidRedirectURI($raw_uri) { + // This covers basics like reasonable formatting and the existence of a + // protocol. + PhabricatorEnv::requireValidRemoteURIForLink($raw_uri); + + $uri = new PhutilURI($raw_uri); + + $fragment = $uri->getFragment(); + if (strlen($fragment)) { + throw new Exception( + pht( + 'OAuth application redirect URIs must not contain URI '. + 'fragments, but the URI "%s" has a fragment ("%s").', + $raw_uri, + $fragment)); } - if ($uri->getFragment()) { - return false; + $protocol = $uri->getProtocol(); + switch ($protocol) { + case 'http': + case 'https': + break; + default: + throw new Exception( + pht( + 'OAuth application redirect URIs must only use the "http" or '. + '"https" protocols, but the URI "%s" uses the "%s" protocol.', + $raw_uri, + $protocol)); } - - if (!$uri->getDomain()) { - return false; - } - - return true; } /** diff --git a/src/applications/oauthserver/application/PhabricatorOAuthServerApplication.php b/src/applications/oauthserver/application/PhabricatorOAuthServerApplication.php index eb953bd391..8fa4d11e10 100644 --- a/src/applications/oauthserver/application/PhabricatorOAuthServerApplication.php +++ b/src/applications/oauthserver/application/PhabricatorOAuthServerApplication.php @@ -51,10 +51,10 @@ final class PhabricatorOAuthServerApplication extends PhabricatorApplication { => 'PhabricatorOAuthClientListController', 'auth/' => 'PhabricatorOAuthServerAuthController', 'token/' => 'PhabricatorOAuthServerTokenController', + $this->getEditRoutePattern('edit/') => + 'PhabricatorOAuthClientEditController', 'client/' => array( - 'create/' => 'PhabricatorOAuthClientEditController', 'delete/(?P\d+)/' => 'PhabricatorOAuthClientDeleteController', - 'edit/(?P\d+)/' => 'PhabricatorOAuthClientEditController', 'view/(?P\d+)/' => 'PhabricatorOAuthClientViewController', 'secret/(?P\d+)/' => 'PhabricatorOAuthClientSecretController', 'test/(?P\d+)/' => 'PhabricatorOAuthClientTestController', diff --git a/src/applications/oauthserver/controller/client/PhabricatorOAuthClientEditController.php b/src/applications/oauthserver/controller/client/PhabricatorOAuthClientEditController.php index d6278345f6..178a2df35b 100644 --- a/src/applications/oauthserver/controller/client/PhabricatorOAuthClientEditController.php +++ b/src/applications/oauthserver/controller/client/PhabricatorOAuthClientEditController.php @@ -4,129 +4,9 @@ final class PhabricatorOAuthClientEditController extends PhabricatorOAuthClientController { public function handleRequest(AphrontRequest $request) { - $viewer = $this->getViewer(); - $id = $request->getURIData('id'); - - if ($id) { - $client = id(new PhabricatorOAuthServerClientQuery()) - ->setViewer($viewer) - ->withIDs(array($id)) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->executeOne(); - if (!$client) { - return new Aphront404Response(); - } - - $title = pht('Edit OAuth Application: %s', $client->getName()); - $submit_button = pht('Save Application'); - $crumb_text = pht('Edit'); - $cancel_uri = $client->getViewURI(); - $is_new = false; - } else { - $this->requireApplicationCapability( - PhabricatorOAuthServerCreateClientsCapability::CAPABILITY); - - $client = PhabricatorOAuthServerClient::initializeNewClient($viewer); - - $title = pht('Create OAuth Application'); - $submit_button = pht('Create Application'); - $crumb_text = pht('Create Application'); - $cancel_uri = $this->getApplicationURI(); - $is_new = true; - } - - $errors = array(); - $e_redirect = true; - $e_name = true; - if ($request->isFormPost()) { - $redirect_uri = $request->getStr('redirect_uri'); - $client->setName($request->getStr('name')); - $client->setRedirectURI($redirect_uri); - - if (!strlen($client->getName())) { - $errors[] = pht('You must choose a name for this OAuth application.'); - $e_name = pht('Required'); - } - - $server = new PhabricatorOAuthServer(); - $uri = new PhutilURI($redirect_uri); - if (!$server->validateRedirectURI($uri)) { - $errors[] = pht( - 'Redirect URI must be a fully qualified domain name '. - 'with no fragments. See %s for more information on the correct '. - 'format.', - 'http://tools.ietf.org/html/draft-ietf-oauth-v2-23#section-3.1.2'); - $e_redirect = pht('Invalid'); - } - - $client->setViewPolicy($request->getStr('viewPolicy')); - $client->setEditPolicy($request->getStr('editPolicy')); - if (!$errors) { - $client->save(); - $view_uri = $client->getViewURI(); - return id(new AphrontRedirectResponse())->setURI($view_uri); - } - } - - $policies = id(new PhabricatorPolicyQuery()) - ->setViewer($viewer) - ->setObject($client) - ->execute(); - - $form = id(new AphrontFormView()) - ->setUser($viewer) - ->appendChild( - id(new AphrontFormTextControl()) - ->setLabel(pht('Name')) - ->setName('name') - ->setValue($client->getName()) - ->setError($e_name)) - ->appendChild( - id(new AphrontFormTextControl()) - ->setLabel(pht('Redirect URI')) - ->setName('redirect_uri') - ->setValue($client->getRedirectURI()) - ->setError($e_redirect)) - ->appendChild( - id(new AphrontFormPolicyControl()) - ->setUser($viewer) - ->setCapability(PhabricatorPolicyCapability::CAN_VIEW) - ->setPolicyObject($client) - ->setPolicies($policies) - ->setName('viewPolicy')) - ->appendChild( - id(new AphrontFormPolicyControl()) - ->setUser($viewer) - ->setCapability(PhabricatorPolicyCapability::CAN_EDIT) - ->setPolicyObject($client) - ->setPolicies($policies) - ->setName('editPolicy')) - ->appendChild( - id(new AphrontFormSubmitControl()) - ->addCancelButton($cancel_uri) - ->setValue($submit_button)); - - $crumbs = $this->buildApplicationCrumbs(); - if (!$is_new) { - $crumbs->addTextCrumb( - $client->getName(), - $client->getViewURI()); - } - $crumbs->addTextCrumb($crumb_text); - - $box = id(new PHUIObjectBoxView()) - ->setHeaderText($title) - ->setFormErrors($errors) - ->setForm($form); - - return $this->newPage() - ->setCrumbs($crumbs) - ->setTitle($title) - ->appendChild($box); + return id(new PhabricatorOAuthServerEditEngine()) + ->setController($this) + ->buildResponse(); } } diff --git a/src/applications/oauthserver/controller/client/PhabricatorOAuthClientListController.php b/src/applications/oauthserver/controller/client/PhabricatorOAuthClientListController.php index 217fa1d7b1..513bed7bc8 100644 --- a/src/applications/oauthserver/controller/client/PhabricatorOAuthClientListController.php +++ b/src/applications/oauthserver/controller/client/PhabricatorOAuthClientListController.php @@ -3,38 +3,22 @@ final class PhabricatorOAuthClientListController extends PhabricatorOAuthClientController { - private $queryKey; - public function shouldAllowPublic() { return true; } - public function willProcessRequest(array $data) { - $this->queryKey = idx($data, 'queryKey'); - } - - public function processRequest() { - $controller = id(new PhabricatorApplicationSearchController()) - ->setQueryKey($this->queryKey) - ->setSearchEngine(new PhabricatorOAuthServerClientSearchEngine()) - ->setNavigation($this->buildSideNavView()); - - return $this->delegateToController($controller); + public function handleRequest(AphrontRequest $request) { + return id(new PhabricatorOAuthServerClientSearchEngine()) + ->setController($this) + ->buildResponse(); } protected function buildApplicationCrumbs() { $crumbs = parent::buildApplicationCrumbs(); - $can_create = $this->hasApplicationCapability( - PhabricatorOAuthServerCreateClientsCapability::CAPABILITY); - - $crumbs->addAction( - id(new PHUIListItemView()) - ->setHref($this->getApplicationURI('client/create/')) - ->setName(pht('Create Application')) - ->setDisabled(!$can_create) - ->setWorkflow(!$can_create) - ->setIcon('fa-plus-square')); + id(new PhabricatorOAuthServerEditEngine()) + ->setViewer($this->getViewer()) + ->addActionToCrumbs($crumbs); return $crumbs; } diff --git a/src/applications/oauthserver/controller/client/PhabricatorOAuthClientViewController.php b/src/applications/oauthserver/controller/client/PhabricatorOAuthClientViewController.php index c7667ef6ae..217667b0b9 100644 --- a/src/applications/oauthserver/controller/client/PhabricatorOAuthClientViewController.php +++ b/src/applications/oauthserver/controller/client/PhabricatorOAuthClientViewController.php @@ -22,6 +22,11 @@ final class PhabricatorOAuthClientViewController $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb($client->getName()); + $timeline = $this->buildTransactionTimeline( + $client, + new PhabricatorOAuthServerTransactionQuery()); + $timeline->setShouldTerminate(true); + $box = id(new PHUIObjectBoxView()) ->setHeader($header) ->addPropertyList($properties); @@ -31,7 +36,11 @@ final class PhabricatorOAuthClientViewController return $this->newPage() ->setCrumbs($crumbs) ->setTitle($title) - ->appendChild($box); + ->appendChild( + array( + $box, + $timeline, + )); } private function buildHeaderView(PhabricatorOAuthServerClient $client) { diff --git a/src/applications/oauthserver/editor/PhabricatorOAuthServerEditEngine.php b/src/applications/oauthserver/editor/PhabricatorOAuthServerEditEngine.php new file mode 100644 index 0000000000..ad47552c19 --- /dev/null +++ b/src/applications/oauthserver/editor/PhabricatorOAuthServerEditEngine.php @@ -0,0 +1,100 @@ +getViewer()); + } + + protected function newObjectQuery() { + return id(new PhabricatorOAuthServerClientQuery()); + } + + protected function getObjectCreateTitleText($object) { + return pht('Create OAuth Server'); + } + + protected function getObjectCreateButtonText($object) { + return pht('Create OAuth Server'); + } + + protected function getObjectEditTitleText($object) { + return pht('Edit OAuth Server: %s', $object->getName()); + } + + protected function getObjectEditShortText($object) { + return pht('Edit OAuth Server'); + } + + protected function getObjectCreateShortText() { + return pht('Create OAuth Server'); + } + + protected function getObjectName() { + return pht('OAuth Server'); + } + + protected function getObjectViewURI($object) { + return $object->getViewURI(); + } + + protected function getCreateNewObjectPolicy() { + return $this->getApplication()->getPolicy( + PhabricatorOAuthServerCreateClientsCapability::CAPABILITY); + } + + protected function buildCustomEditFields($object) { + return array( + id(new PhabricatorTextEditField()) + ->setKey('name') + ->setLabel(pht('Name')) + ->setIsRequired(true) + ->setTransactionType(PhabricatorOAuthServerTransaction::TYPE_NAME) + ->setDescription(pht('The name of the OAuth application.')) + ->setConduitDescription(pht('Rename the application.')) + ->setConduitTypeDescription(pht('New application name.')) + ->setValue($object->getName()), + id(new PhabricatorTextEditField()) + ->setKey('redirectURI') + ->setLabel(pht('Redirect URI')) + ->setIsRequired(true) + ->setTransactionType( + PhabricatorOAuthServerTransaction::TYPE_REDIRECT_URI) + ->setDescription( + pht('The redirect URI for OAuth handshakes.')) + ->setConduitDescription( + pht( + 'Change where this application redirects users to during OAuth '. + 'handshakes.')) + ->setConduitTypeDescription( + pht( + 'New OAuth application redirect URI.')) + ->setValue($object->getRedirectURI()), + ); + } + +} diff --git a/src/applications/oauthserver/editor/PhabricatorOAuthServerEditor.php b/src/applications/oauthserver/editor/PhabricatorOAuthServerEditor.php new file mode 100644 index 0000000000..4578ccbc6a --- /dev/null +++ b/src/applications/oauthserver/editor/PhabricatorOAuthServerEditor.php @@ -0,0 +1,137 @@ +getTransactionType()) { + case PhabricatorOAuthServerTransaction::TYPE_NAME: + return $object->getName(); + case PhabricatorOAuthServerTransaction::TYPE_REDIRECT_URI: + return $object->getRedirectURI(); + } + } + + protected function getCustomTransactionNewValue( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + switch ($xaction->getTransactionType()) { + case PhabricatorOAuthServerTransaction::TYPE_NAME: + case PhabricatorOAuthServerTransaction::TYPE_REDIRECT_URI: + return $xaction->getNewValue(); + } + } + + protected function applyCustomInternalTransaction( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + switch ($xaction->getTransactionType()) { + case PhabricatorOAuthServerTransaction::TYPE_NAME: + $object->setName($xaction->getNewValue()); + return; + case PhabricatorOAuthServerTransaction::TYPE_REDIRECT_URI: + $object->setRedirectURI($xaction->getNewValue()); + return; + } + + return parent::applyCustomInternalTransaction($object, $xaction); + } + + protected function applyCustomExternalTransaction( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + switch ($xaction->getTransactionType()) { + case PhabricatorOAuthServerTransaction::TYPE_NAME: + case PhabricatorOAuthServerTransaction::TYPE_REDIRECT_URI: + return; + } + + return parent::applyCustomExternalTransaction($object, $xaction); + } + + protected function validateTransaction( + PhabricatorLiskDAO $object, + $type, + array $xactions) { + + $errors = parent::validateTransaction($object, $type, $xactions); + + switch ($type) { + case PhabricatorOAuthServerTransaction::TYPE_NAME: + $missing = $this->validateIsEmptyTextField( + $object->getName(), + $xactions); + + if ($missing) { + $error = new PhabricatorApplicationTransactionValidationError( + $type, + pht('Required'), + pht('OAuth applications must have a name.'), + nonempty(last($xactions), null)); + + $error->setIsMissingFieldError(true); + $errors[] = $error; + } + break; + case PhabricatorOAuthServerTransaction::TYPE_REDIRECT_URI: + $missing = $this->validateIsEmptyTextField( + $object->getRedirectURI(), + $xactions); + if ($missing) { + $error = new PhabricatorApplicationTransactionValidationError( + $type, + pht('Required'), + pht('OAuth applications must have a valid redirect URI.'), + nonempty(last($xactions), null)); + + $error->setIsMissingFieldError(true); + $errors[] = $error; + } else { + foreach ($xactions as $xaction) { + $redirect_uri = $xaction->getNewValue(); + + try { + $server = new PhabricatorOAuthServer(); + $server->assertValidRedirectURI($redirect_uri); + } catch (Exception $ex) { + $errors[] = new PhabricatorApplicationTransactionValidationError( + $type, + pht('Invalid'), + $ex->getMessage(), + $xaction); + } + } + } + break; + } + + return $errors; + } + +} diff --git a/src/applications/oauthserver/query/PhabricatorOAuthServerTransactionQuery.php b/src/applications/oauthserver/query/PhabricatorOAuthServerTransactionQuery.php new file mode 100644 index 0000000000..4dd21e2609 --- /dev/null +++ b/src/applications/oauthserver/query/PhabricatorOAuthServerTransactionQuery.php @@ -0,0 +1,10 @@ +getID(); - return "/oauthserver/client/edit/{$id}/"; + return "/oauthserver/edit/{$id}/"; } public function getViewURI() { @@ -92,8 +93,32 @@ final class PhabricatorOAuthServerClient return null; } + +/* -( PhabricatorApplicationTransactionInterface )------------------------- */ + + + public function getApplicationTransactionEditor() { + return new PhabricatorOAuthServerEditor(); + } + + public function getApplicationTransactionObject() { + return $this; + } + + public function getApplicationTransactionTemplate() { + return new PhabricatorOAuthServerTransaction(); + } + + public function willRenderTimeline( + PhabricatorApplicationTransactionView $timeline, + AphrontRequest $request) { + return $timeline; + } + + /* -( PhabricatorDestructibleInterface )----------------------------------- */ + public function destroyObjectPermanently( PhabricatorDestructionEngine $engine) { diff --git a/src/applications/oauthserver/storage/PhabricatorOAuthServerTransaction.php b/src/applications/oauthserver/storage/PhabricatorOAuthServerTransaction.php new file mode 100644 index 0000000000..1d65087a07 --- /dev/null +++ b/src/applications/oauthserver/storage/PhabricatorOAuthServerTransaction.php @@ -0,0 +1,52 @@ +getAuthorPHID(); + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + switch ($this->getTransactionType()) { + case PhabricatorTransactions::TYPE_CREATE: + return pht( + '%s created this OAuth application.', + $this->renderHandleLink($author_phid)); + case self::TYPE_NAME: + return pht( + '%s renamed this application from "%s" to "%s".', + $this->renderHandleLink($author_phid), + $old, + $new); + case self::TYPE_REDIRECT_URI: + return pht( + '%s changed the application redirect URI from "%s" to "%s".', + $this->renderHandleLink($author_phid), + $old, + $new); + } + + return parent::getTitle(); + } + +} diff --git a/src/infrastructure/env/PhabricatorEnv.php b/src/infrastructure/env/PhabricatorEnv.php index 694c793e0a..421d7eca25 100644 --- a/src/infrastructure/env/PhabricatorEnv.php +++ b/src/infrastructure/env/PhabricatorEnv.php @@ -575,8 +575,8 @@ final class PhabricatorEnv extends Phobject { * @return void * @task uri */ - public static function requireValidRemoteURIForLink($uri) { - $uri = new PhutilURI($uri); + public static function requireValidRemoteURIForLink($raw_uri) { + $uri = new PhutilURI($raw_uri); $proto = $uri->getProtocol(); if (!strlen($proto)) { @@ -584,7 +584,7 @@ final class PhabricatorEnv extends Phobject { pht( 'URI "%s" is not a valid linkable resource. A valid linkable '. 'resource URI must specify a protocol.', - $uri)); + $raw_uri)); } $protocols = self::getEnvConfig('uri.allowed-protocols'); @@ -593,7 +593,7 @@ final class PhabricatorEnv extends Phobject { pht( 'URI "%s" is not a valid linkable resource. A valid linkable '. 'resource URI must use one of these protocols: %s.', - $uri, + $raw_uri, implode(', ', array_keys($protocols)))); } @@ -603,7 +603,7 @@ final class PhabricatorEnv extends Phobject { pht( 'URI "%s" is not a valid linkable resource. A valid linkable '. 'resource URI must specify a domain.', - $uri)); + $raw_uri)); } } From e965a59bca56ddee3efdb0b07ce5d5f0266c9b52 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Mon, 4 Apr 2016 20:34:23 -0700 Subject: [PATCH 42/84] Update Batch Edit and Report pages Summary: Moves these Maniphest pages over to modern UI, components Test Plan: Batch Edit Tasks, View some reports. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15614 --- resources/celerity/map.php | 6 ++--- .../ManiphestBatchEditController.php | 23 +++++++++++++------ .../controller/ManiphestReportController.php | 20 ++++++++-------- webroot/rsrc/css/phui/phui-box.css | 2 +- 4 files changed, 30 insertions(+), 21 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 930a2e5b41..64455ce942 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -7,7 +7,7 @@ */ return array( 'names' => array( - 'core.pkg.css' => 'a3016dac', + 'core.pkg.css' => '97b752c8', 'core.pkg.js' => 'e5484f37', 'darkconsole.pkg.js' => 'e7393ebb', 'differential.pkg.css' => '7ba78475', @@ -123,7 +123,7 @@ return array( 'rsrc/css/phui/phui-action-panel.css' => '91c7b835', 'rsrc/css/phui/phui-badge.css' => 'f25c3476', 'rsrc/css/phui/phui-big-info-view.css' => 'bd903741', - 'rsrc/css/phui/phui-box.css' => 'b2d49bae', + 'rsrc/css/phui/phui-box.css' => '9c9159a7', 'rsrc/css/phui/phui-button.css' => 'a64a8de6', 'rsrc/css/phui/phui-chart.css' => '6bf6f78e', 'rsrc/css/phui/phui-crumbs-view.css' => '79d536e5', @@ -805,7 +805,7 @@ return array( 'phui-action-panel-css' => '91c7b835', 'phui-badge-view-css' => 'f25c3476', 'phui-big-info-view-css' => 'bd903741', - 'phui-box-css' => 'b2d49bae', + 'phui-box-css' => '9c9159a7', 'phui-button-css' => 'a64a8de6', 'phui-calendar-css' => 'ccabe893', 'phui-calendar-day-css' => 'd1cf6f93', diff --git a/src/applications/maniphest/controller/ManiphestBatchEditController.php b/src/applications/maniphest/controller/ManiphestBatchEditController.php index d245817f20..90fcda28ec 100644 --- a/src/applications/maniphest/controller/ManiphestBatchEditController.php +++ b/src/applications/maniphest/controller/ManiphestBatchEditController.php @@ -194,24 +194,33 @@ final class ManiphestBatchEditController extends ManiphestController { $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb($title); + $crumbs->setBorder(true); + + $header = id(new PHUIHeaderView()) + ->setHeader(pht('Batch Editor')) + ->setHeaderIcon('fa-pencil-square-o'); $task_box = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Selected Tasks')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setObjectList($list); $form_box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Batch Editor')) + ->setHeaderText(pht('Actions')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setForm($form); - return $this->buildApplicationPage( - array( - $crumbs, + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter(array( $task_box, $form_box, - ), - array( - 'title' => $title, )); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); } } diff --git a/src/applications/maniphest/controller/ManiphestReportController.php b/src/applications/maniphest/controller/ManiphestReportController.php index c7f0cf2186..f16281691b 100644 --- a/src/applications/maniphest/controller/ManiphestReportController.php +++ b/src/applications/maniphest/controller/ManiphestReportController.php @@ -45,17 +45,17 @@ final class ManiphestReportController extends ManiphestController { return new Aphront404Response(); } - $nav->appendChild($core); - $nav->setCrumbs( - $this->buildApplicationCrumbs() - ->addTextCrumb(pht('Reports'))); + $crumbs = $this->buildApplicationCrumbs() + ->addTextCrumb(pht('Reports')); + + $nav->appendChild($core); + $title = pht('Maniphest Reports'); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->setNavigation($nav); - return $this->buildApplicationPage( - $nav, - array( - 'title' => pht('Maniphest Reports'), - 'device' => false, - )); } public function renderBurn() { diff --git a/webroot/rsrc/css/phui/phui-box.css b/webroot/rsrc/css/phui/phui-box.css index 35f5067fec..30328d0d38 100644 --- a/webroot/rsrc/css/phui/phui-box.css +++ b/webroot/rsrc/css/phui/phui-box.css @@ -97,7 +97,7 @@ color: {$bluetext}; } -.phui-box-blue-property .phui-object-item-list-view.phui-object-list-flush { +.phui-box-blue-property .phui-object-item-list-view { padding: 2px 8px; } From f2a38f52d7a18c88142eecc43f11d82e0346da8b Mon Sep 17 00:00:00 2001 From: Chad Little Date: Mon, 4 Apr 2016 20:40:14 -0700 Subject: [PATCH 43/84] Update Drydock remaining pages to new UI Summary: Updates Console and Operations page. Test Plan: Pull up Console, pull up status page Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15615 --- .../controller/DrydockConsoleController.php | 24 ++++++++++++------- ...ockRepositoryOperationStatusController.php | 15 ++++-------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/applications/drydock/controller/DrydockConsoleController.php b/src/applications/drydock/controller/DrydockConsoleController.php index c6a8e25fd6..1d79f52c14 100644 --- a/src/applications/drydock/controller/DrydockConsoleController.php +++ b/src/applications/drydock/controller/DrydockConsoleController.php @@ -62,19 +62,25 @@ final class DrydockConsoleController extends DrydockController { $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Console')); + $crumbs->setBorder(true); $box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Drydock Console')) ->setObjectList($menu); - return $this->buildApplicationPage( - array( - $crumbs, - $box, - ), - array( - 'title' => pht('Drydock Console'), - )); + $title = pht('Drydock Console'); + + $header = id(new PHUIHeaderView()) + ->setHeader($title) + ->setHeaderIcon('fa-truck'); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter($box); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); } } diff --git a/src/applications/drydock/controller/DrydockRepositoryOperationStatusController.php b/src/applications/drydock/controller/DrydockRepositoryOperationStatusController.php index c6d38791f3..5f35c91bb3 100644 --- a/src/applications/drydock/controller/DrydockRepositoryOperationStatusController.php +++ b/src/applications/drydock/controller/DrydockRepositoryOperationStatusController.php @@ -43,17 +43,10 @@ final class DrydockRepositoryOperationStatusController $this->getApplicationURI('operation/')); $crumbs->addTextCrumb($title); - return $this->buildApplicationPage( - array( - $crumbs, - $status_view, - ), - array( - 'title' => array( - $title, - pht('Status'), - ), - )); + return $this->newPage() + ->setTitle(pht('Status')) + ->setCrumbs($crumbs) + ->appendChild($status_view); } } From 4761dba0cd37c06eeb81064369e50f4cf679b6ef Mon Sep 17 00:00:00 2001 From: Chad Little Date: Mon, 4 Apr 2016 21:11:48 -0700 Subject: [PATCH 44/84] Update Search edit page for new UI Summary: Updates to use new UI Test Plan: Save a custom query, edit a custom query Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15618 --- .../PhabricatorSearchEditController.php | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/applications/search/controller/PhabricatorSearchEditController.php b/src/applications/search/controller/PhabricatorSearchEditController.php index da89919611..c7f6c860e9 100644 --- a/src/applications/search/controller/PhabricatorSearchEditController.php +++ b/src/applications/search/controller/PhabricatorSearchEditController.php @@ -73,26 +73,35 @@ final class PhabricatorSearchEditController if ($named_query->getID()) { $title = pht('Edit Saved Query'); + $header_icon = 'fa-pencil'; } else { $title = pht('Save Query'); + $header_icon = 'fa-search'; } $form_box = id(new PHUIObjectBoxView()) - ->setHeaderText($title) + ->setHeaderText(pht('Query')) ->setFormErrors($errors) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setForm($form); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb($title); + $crumbs->setBorder(true); + + $header = id(new PHUIHeaderView()) + ->setHeader($title) + ->setHeaderIcon($header_icon); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter($form_box); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); - return $this->buildApplicationPage( - array( - $crumbs, - $form_box, - ), - array( - 'title' => $title, - )); } } From 57ed6b749a31568570ea9b8a6d88e25a9e9e2a4f Mon Sep 17 00:00:00 2001 From: Chad Little Date: Mon, 4 Apr 2016 21:04:58 -0700 Subject: [PATCH 45/84] Update Help for newPage Summary: Swaps over to `newPage` and `newDialog` Test Plan: Unsure how to actually pull these up? Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15617 --- .../PhabricatorHelpDocumentationController.php | 12 ++++-------- .../PhabricatorHelpEditorProtocolController.php | 6 +----- .../PhabricatorHelpKeyboardShortcutController.php | 5 +---- 3 files changed, 6 insertions(+), 17 deletions(-) diff --git a/src/applications/help/controller/PhabricatorHelpDocumentationController.php b/src/applications/help/controller/PhabricatorHelpDocumentationController.php index ce4dc38f42..4983520bab 100644 --- a/src/applications/help/controller/PhabricatorHelpDocumentationController.php +++ b/src/applications/help/controller/PhabricatorHelpDocumentationController.php @@ -38,14 +38,10 @@ final class PhabricatorHelpDocumentationController $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb($title); - return $this->buildApplicationPage( - array( - $crumbs, - $list, - ), - array( - 'title' => $title, - )); + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($list); } diff --git a/src/applications/help/controller/PhabricatorHelpEditorProtocolController.php b/src/applications/help/controller/PhabricatorHelpEditorProtocolController.php index 010e41ffcd..22ad0f8d2d 100644 --- a/src/applications/help/controller/PhabricatorHelpEditorProtocolController.php +++ b/src/applications/help/controller/PhabricatorHelpEditorProtocolController.php @@ -10,8 +10,7 @@ final class PhabricatorHelpEditorProtocolController public function handleRequest(AphrontRequest $request) { $viewer = $request->getViewer(); - $dialog = id(new AphrontDialogView()) - ->setUser($viewer) + return $this->newDialog() ->setMethod('GET') ->setSubmitURI('/settings/panel/display/') ->setTitle(pht('Unsupported Editor Protocol')) @@ -24,9 +23,6 @@ final class PhabricatorHelpEditorProtocolController phutil_tag('tt', array(), 'uri.allowed-editor-protocols'))) ->addSubmitButton(pht('Change Settings')) ->addCancelButton('/'); - - return id(new AphrontDialogResponse()) - ->setDialog($dialog); } public static function hasAllowedProtocol($uri) { diff --git a/src/applications/help/controller/PhabricatorHelpKeyboardShortcutController.php b/src/applications/help/controller/PhabricatorHelpKeyboardShortcutController.php index b136265d7f..80bd259c48 100644 --- a/src/applications/help/controller/PhabricatorHelpKeyboardShortcutController.php +++ b/src/applications/help/controller/PhabricatorHelpKeyboardShortcutController.php @@ -57,14 +57,11 @@ final class PhabricatorHelpKeyboardShortcutController array('class' => 'keyboard-shortcut-help'), $rows); - $dialog = id(new AphrontDialogView()) - ->setUser($viewer) + return $this->newDialog() ->setTitle(pht('Keyboard Shortcuts')) ->appendChild($table) ->addCancelButton('#', pht('Close')); - return id(new AphrontDialogResponse()) - ->setDialog($dialog); } } From 4d9bbc539ecd0e7ead93669d248af14a36423f36 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Mon, 4 Apr 2016 20:48:36 -0700 Subject: [PATCH 46/84] Update MetaMTA to new UI Summary: Swaps over to new hotness Test Plan: Pull up mail view, see new UI Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15616 --- .../PhabricatorMetaMTAMailViewController.php | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/applications/metamta/controller/PhabricatorMetaMTAMailViewController.php b/src/applications/metamta/controller/PhabricatorMetaMTAMailViewController.php index ec43312e23..fda471b79f 100644 --- a/src/applications/metamta/controller/PhabricatorMetaMTAMailViewController.php +++ b/src/applications/metamta/controller/PhabricatorMetaMTAMailViewController.php @@ -23,7 +23,8 @@ final class PhabricatorMetaMTAMailViewController $header = id(new PHUIHeaderView()) ->setHeader($title) ->setUser($viewer) - ->setPolicyObject($mail); + ->setPolicyObject($mail) + ->setHeaderIcon('fa-envelope'); $status = $mail->getStatus(); $name = PhabricatorMailOutboundStatus::getStatusName($status); @@ -32,24 +33,26 @@ final class PhabricatorMetaMTAMailViewController $header->setStatus($icon, $color, $name); $crumbs = $this->buildApplicationCrumbs() - ->addTextCrumb(pht('Mail %d', $mail->getID())); + ->addTextCrumb(pht('Mail %d', $mail->getID())) + ->setBorder(true); $object_box = id(new PHUIObjectBoxView()) - ->setHeader($header) + ->setHeaderText(pht('Mail')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->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( - $crumbs, - $object_box, - ), - array( - 'title' => $title, - 'pageObjects' => array($mail->getPHID()), - )); + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter($object_box); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->setPageObjectPHIDs(array($mail->getPHID())) + ->appendChild($view); } private function buildMessageProperties(PhabricatorMetaMTAMail $mail) { From 1bd33ad759d18289f4ed61eb012fe2591c25ce54 Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 5 Apr 2016 01:58:03 -0700 Subject: [PATCH 47/84] Correct table documentation in Remarkup reference Summary: Ref T10570. Earlier work on that task made tables activate even if indented so they work in quoted blocks. However, the documentation doesn't explicitly mark them in code blocks, so it turned them into markup. Test Plan: Used `bin/diviner generate` to regenerate documentation, verified it now renders properly. {F1205818} Reviewers: chad Reviewed By: chad Maniphest Tasks: T10570 Differential Revision: https://secure.phabricator.com/D15619 --- src/docs/user/userguide/remarkup.diviner | 52 +++++++++++++----------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/src/docs/user/userguide/remarkup.diviner b/src/docs/user/userguide/remarkup.diviner index c56600ebdf..379c09e5c3 100644 --- a/src/docs/user/userguide/remarkup.diviner +++ b/src/docs/user/userguide/remarkup.diviner @@ -532,10 +532,12 @@ escape HTML and preserve line breaks). Remarkup supports simple table syntax. For example, this: - | Fruit | Color | Price | Peel? - | ----- | ----- | ----- | ----- - | Apple | red | `$0.93` | no - | Banana | yellow | `$0.19` | **YES** +``` +| Fruit | Color | Price | Peel? +| ----- | ----- | ----- | ----- +| Apple | red | `$0.93` | no +| Banana | yellow | `$0.19` | **YES** +``` ...produces this: @@ -546,26 +548,28 @@ Remarkup supports simple table syntax. For example, this: Remarkup also supports a simplified HTML table syntax. For example, this: - - - - - - - - - - - - - - - - - - - -
FruitColorPricePeel?
Applered`$0.93`no
Bananayellow`$0.19`**YES**
+``` + + + + + + + + + + + + + + + + + + + +
FruitColorPricePeel?
Applered`$0.93`no
Bananayellow`$0.19`**YES**
+``` ...produces this: From 00604dff451010b73c0cf9ddcfe03401975b69a5 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Tue, 5 Apr 2016 13:10:21 -0700 Subject: [PATCH 48/84] Update Releeph to new UI Summary: Runs through Releeph to move to new UI and `newPage` Test Plan: Ran through product, release, branch, everything seems to work. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15623 --- .../releeph/controller/ReleephController.php | 13 -------- .../branch/ReleephBranchCreateController.php | 31 ++++++++++++------- .../branch/ReleephBranchEditController.php | 30 +++++++++++------- .../branch/ReleephBranchHistoryController.php | 16 +++++----- .../ReleephProductCreateController.php | 28 +++++++++++------ .../product/ReleephProductEditController.php | 28 +++++++++++------ .../ReleephProductHistoryController.php | 14 ++++----- .../request/ReleephRequestEditController.php | 26 +++++++++++----- .../request/ReleephRequestViewController.php | 20 ++++++++---- 9 files changed, 121 insertions(+), 85 deletions(-) diff --git a/src/applications/releeph/controller/ReleephController.php b/src/applications/releeph/controller/ReleephController.php index 83c94debe9..f48d2858e8 100644 --- a/src/applications/releeph/controller/ReleephController.php +++ b/src/applications/releeph/controller/ReleephController.php @@ -2,19 +2,6 @@ abstract class ReleephController extends PhabricatorController { - public function buildStandardPageResponse($view, array $data) { - $page = $this->buildStandardPageView(); - - $page->setApplicationName(pht('Releeph')); - $page->setBaseURI('/releeph/'); - $page->setTitle(idx($data, 'title')); - $page->setGlyph("\xD3\x82"); - $page->appendChild($view); - - $response = new AphrontWebpageResponse(); - return $response->setContent($page->render()); - } - public function buildSideNavView($for_app = false) { $user = $this->getRequest()->getUser(); diff --git a/src/applications/releeph/controller/branch/ReleephBranchCreateController.php b/src/applications/releeph/controller/branch/ReleephBranchCreateController.php index d13383cc3b..e03e432d1f 100644 --- a/src/applications/releeph/controller/branch/ReleephBranchCreateController.php +++ b/src/applications/releeph/controller/branch/ReleephBranchCreateController.php @@ -105,20 +105,29 @@ final class ReleephBranchCreateController extends ReleephProductController { ->addCancelButton($product_uri)); $box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('New Branch')) + ->setHeaderText(pht('Branch')) ->setFormErrors($errors) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($form); - $crumbs = $this->buildApplicationCrumbs(); - $crumbs->addTextCrumb(pht('New Branch')); + $title = pht('New Branch'); + + $crumbs = $this->buildApplicationCrumbs(); + $crumbs->addTextCrumb($title); + $crumbs->setBorder(true); + + $header = id(new PHUIHeaderView()) + ->setHeader($title) + ->setHeaderIcon('fa-plus-square'); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter($box); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); - return $this->buildApplicationPage( - array( - $crumbs, - $box, - ), - array( - 'title' => pht('New Branch'), - )); } } diff --git a/src/applications/releeph/controller/branch/ReleephBranchEditController.php b/src/applications/releeph/controller/branch/ReleephBranchEditController.php index 6d66f5d9d5..9d34e78668 100644 --- a/src/applications/releeph/controller/branch/ReleephBranchEditController.php +++ b/src/applications/releeph/controller/branch/ReleephBranchEditController.php @@ -86,23 +86,29 @@ final class ReleephBranchEditController extends ReleephBranchController { ->setValue(pht('Save Branch'))); $title = pht( - 'Edit Branch %s', + 'Edit Branch: %s', $branch->getDisplayNameWithDetail()); + $box = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Branch')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->appendChild($form); + $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Edit')); + $crumbs->setBorder(true); - $box = id(new PHUIObjectBoxView()) - ->setHeaderText($title) - ->appendChild($form); + $header = id(new PHUIHeaderView()) + ->setHeader(pht('Edit Branch')) + ->setHeaderIcon('fa-pencil'); - return $this->buildApplicationPage( - array( - $crumbs, - $box, - ), - array( - 'title' => $title, - )); + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter($box); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); } } diff --git a/src/applications/releeph/controller/branch/ReleephBranchHistoryController.php b/src/applications/releeph/controller/branch/ReleephBranchHistoryController.php index a77cdf8fb3..5a07a5c879 100644 --- a/src/applications/releeph/controller/branch/ReleephBranchHistoryController.php +++ b/src/applications/releeph/controller/branch/ReleephBranchHistoryController.php @@ -27,15 +27,15 @@ final class ReleephBranchHistoryController extends ReleephBranchController { $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('History')); + $crumbs->setBorder(true); + + $title = pht('Branch History'); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($timeline); - return $this->buildApplicationPage( - array( - $crumbs, - $timeline, - ), - array( - 'title' => pht('Branch History'), - )); } } diff --git a/src/applications/releeph/controller/product/ReleephProductCreateController.php b/src/applications/releeph/controller/product/ReleephProductCreateController.php index 2aedf8cdf1..12da2ea3f4 100644 --- a/src/applications/releeph/controller/product/ReleephProductCreateController.php +++ b/src/applications/releeph/controller/product/ReleephProductCreateController.php @@ -91,22 +91,30 @@ final class ReleephProductCreateController extends ReleephProductController { ->addCancelButton('/releeph/project/') ->setValue(pht('Create Release Product'))); - $form_box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Create New Product')) + $box = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Product')) ->setFormErrors($errors) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setForm($form); + $title = pht('Create New Product'); + $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('New Product')); + $crumbs->setBorder(true); - return $this->buildApplicationPage( - array( - $crumbs, - $form_box, - ), - array( - 'title' => pht('Create New Product'), - )); + $header = id(new PHUIHeaderView()) + ->setHeader($title) + ->setHeaderIcon('fa-plus-square'); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter($box); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); } private function getRepositorySelectOptions() { diff --git a/src/applications/releeph/controller/product/ReleephProductEditController.php b/src/applications/releeph/controller/product/ReleephProductEditController.php index 6a58a39bd9..7938f0d930 100644 --- a/src/applications/releeph/controller/product/ReleephProductEditController.php +++ b/src/applications/releeph/controller/product/ReleephProductEditController.php @@ -195,22 +195,30 @@ final class ReleephProductEditController extends ReleephProductController { ->setValue(pht('Save'))); $box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Edit Releeph Product')) + ->setHeaderText(pht('Product')) ->setFormErrors($errors) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($form); + $title = pht('Edit Product'); + $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Edit Product')); + $crumbs->setBorder(true); + + $header = id(new PHUIHeaderView()) + ->setHeader($title) + ->setHeaderIcon('fa-pencil'); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter($box); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); - return $this->buildStandardPageResponse( - array( - $crumbs, - $box, - ), - array( - 'title' => pht('Edit Releeph Product'), - 'device' => true, - )); } private function getBranchHelpText() { diff --git a/src/applications/releeph/controller/product/ReleephProductHistoryController.php b/src/applications/releeph/controller/product/ReleephProductHistoryController.php index ebe9f15725..12d0d0b5c1 100644 --- a/src/applications/releeph/controller/product/ReleephProductHistoryController.php +++ b/src/applications/releeph/controller/product/ReleephProductHistoryController.php @@ -28,14 +28,12 @@ final class ReleephProductHistoryController extends ReleephProductController { $crumbs->addTextCrumb(pht('History')); $crumbs->setBorder(true); - return $this->buildApplicationPage( - array( - $crumbs, - $timeline, - ), - array( - 'title' => pht('Product History'), - )); + $title = pht('Product History'); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($timeline); } } diff --git a/src/applications/releeph/controller/request/ReleephRequestEditController.php b/src/applications/releeph/controller/request/ReleephRequestEditController.php index d5f5187349..af7adc2c83 100644 --- a/src/applications/releeph/controller/request/ReleephRequestEditController.php +++ b/src/applications/releeph/controller/request/ReleephRequestEditController.php @@ -275,12 +275,14 @@ final class ReleephRequestEditController extends ReleephBranchController { if ($is_edit) { $title = pht('Edit Pull Request'); $submit_name = pht('Save'); + $header_icon = 'fa-pencil'; $crumbs->addTextCrumb($pull->getMonogram(), '/'.$pull->getMonogram()); $crumbs->addTextCrumb(pht('Edit')); } else { $title = pht('Create Pull Request'); $submit_name = pht('Create Pull Request'); + $header_icon = 'fa-plus-square'; $crumbs->addTextCrumb(pht('New Pull Request')); } @@ -291,18 +293,28 @@ final class ReleephRequestEditController extends ReleephBranchController { ->setValue($submit_name)); $box = id(new PHUIObjectBoxView()) - ->setHeaderText($title) + ->setHeaderText(pht('Request')) ->setFormErrors($errors) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($form); - return $this->buildApplicationPage( - array( - $crumbs, + $crumbs->setBorder(true); + + $header = id(new PHUIHeaderView()) + ->setHeader($title) + ->setHeaderIcon($header_icon); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter(array( $notice_view, $box, - ), - array( - 'title' => $title, )); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); + } } diff --git a/src/applications/releeph/controller/request/ReleephRequestViewController.php b/src/applications/releeph/controller/request/ReleephRequestViewController.php index 694505cd20..c404e31579 100644 --- a/src/applications/releeph/controller/request/ReleephRequestViewController.php +++ b/src/applications/releeph/controller/request/ReleephRequestViewController.php @@ -76,17 +76,25 @@ final class ReleephRequestViewController $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb($pull->getMonogram(), '/'.$pull->getMonogram()); + $crumbs->setBorder(true); - return $this->buildStandardPageResponse( - array( - $crumbs, + $header = id(new PHUIHeaderView()) + ->setHeader($title) + ->setHeaderIcon('fa-flag-checkered'); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter(array( $pull_box, $timeline, $add_comment_form, - ), - array( - 'title' => $title, )); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); + } From c29bbbab19e4a4689a9a8e29d58ce06be2e73e47 Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 5 Apr 2016 13:17:17 -0700 Subject: [PATCH 49/84] Remove bogus '$this->resource' from SES error handling pathway Summary: Ref T10728. This property does not exist and is never referenced. The `'resource'` key is also never referenced, so I believe this can be safely removed. Test Plan: Will make @amckinley do my job for me. Reviewers: chad Reviewed By: chad Subscribers: amckinley Maniphest Tasks: T10728 Differential Revision: https://secure.phabricator.com/D15624 --- externals/amazon-ses/ses.php | 1 - 1 file changed, 1 deletion(-) diff --git a/externals/amazon-ses/ses.php b/externals/amazon-ses/ses.php index b0eebce690..266056ec8f 100644 --- a/externals/amazon-ses/ses.php +++ b/externals/amazon-ses/ses.php @@ -530,7 +530,6 @@ final class SimpleEmailServiceRequest 'curl' => true, 'code' => curl_errno($curl), 'message' => curl_error($curl), - 'resource' => $this->resource ); } From 8dfc7d4201fdebbc96bdf3306b0e227b65c6a7fa Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 5 Apr 2016 09:40:22 -0700 Subject: [PATCH 50/84] Allow OAuth applications to be disabled instead of destroyed Summary: Ref T7303. This interaction is very oldschool; modernize it to enable/disable instead of "nuke from orbit". Test Plan: - Enabled applications. - Disabled applications. - Viewed applications in list view. - Generated new tokens. - Tried to use a token from a disabled application (got rebuffed). - Tried to use a token from an enabled application (worked fine). Reviewers: chad Reviewed By: chad Maniphest Tasks: T7303 Differential Revision: https://secure.phabricator.com/D15620 --- .../autopatches/20160405.oauth.2.disable.sql | 2 + src/__phutil_library_map__.php | 4 +- .../oauthserver/PhabricatorOAuthServer.php | 5 ++ .../PhabricatorOAuthServerApplication.php | 4 +- .../PhabricatorOAuthServerAuthController.php | 9 +++ .../PhabricatorOAuthServerTokenController.php | 19 ++++-- ...PhabricatorOAuthClientDeleteController.php | 40 ----------- ...habricatorOAuthClientDisableController.php | 67 +++++++++++++++++++ .../PhabricatorOAuthClientTestController.php | 55 ++++++++------- .../PhabricatorOAuthClientViewController.php | 42 +++++++----- .../editor/PhabricatorOAuthServerEditor.php | 9 +++ ...abricatorOAuthServerClientSearchEngine.php | 6 +- .../storage/PhabricatorOAuthServerClient.php | 18 ++--- .../PhabricatorOAuthServerTransaction.php | 11 +++ 14 files changed, 189 insertions(+), 102 deletions(-) create mode 100644 resources/sql/autopatches/20160405.oauth.2.disable.sql delete mode 100644 src/applications/oauthserver/controller/client/PhabricatorOAuthClientDeleteController.php create mode 100644 src/applications/oauthserver/controller/client/PhabricatorOAuthClientDisableController.php diff --git a/resources/sql/autopatches/20160405.oauth.2.disable.sql b/resources/sql/autopatches/20160405.oauth.2.disable.sql new file mode 100644 index 0000000000..fd26ce8a6e --- /dev/null +++ b/resources/sql/autopatches/20160405.oauth.2.disable.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_oauth_server.oauth_server_oauthserverclient + ADD isDisabled BOOL NOT NULL; diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index a28ea67874..824293188a 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2702,7 +2702,7 @@ phutil_register_library_map(array( 'PhabricatorOAuthClientAuthorization' => 'applications/oauthserver/storage/PhabricatorOAuthClientAuthorization.php', 'PhabricatorOAuthClientAuthorizationQuery' => 'applications/oauthserver/query/PhabricatorOAuthClientAuthorizationQuery.php', 'PhabricatorOAuthClientController' => 'applications/oauthserver/controller/client/PhabricatorOAuthClientController.php', - 'PhabricatorOAuthClientDeleteController' => 'applications/oauthserver/controller/client/PhabricatorOAuthClientDeleteController.php', + 'PhabricatorOAuthClientDisableController' => 'applications/oauthserver/controller/client/PhabricatorOAuthClientDisableController.php', 'PhabricatorOAuthClientEditController' => 'applications/oauthserver/controller/client/PhabricatorOAuthClientEditController.php', 'PhabricatorOAuthClientListController' => 'applications/oauthserver/controller/client/PhabricatorOAuthClientListController.php', 'PhabricatorOAuthClientSecretController' => 'applications/oauthserver/controller/client/PhabricatorOAuthClientSecretController.php', @@ -7194,7 +7194,7 @@ phutil_register_library_map(array( ), 'PhabricatorOAuthClientAuthorizationQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorOAuthClientController' => 'PhabricatorOAuthServerController', - 'PhabricatorOAuthClientDeleteController' => 'PhabricatorOAuthClientController', + 'PhabricatorOAuthClientDisableController' => 'PhabricatorOAuthClientController', 'PhabricatorOAuthClientEditController' => 'PhabricatorOAuthClientController', 'PhabricatorOAuthClientListController' => 'PhabricatorOAuthClientController', 'PhabricatorOAuthClientSecretController' => 'PhabricatorOAuthClientController', diff --git a/src/applications/oauthserver/PhabricatorOAuthServer.php b/src/applications/oauthserver/PhabricatorOAuthServer.php index edb93ac974..123b09d6ba 100644 --- a/src/applications/oauthserver/PhabricatorOAuthServer.php +++ b/src/applications/oauthserver/PhabricatorOAuthServer.php @@ -172,6 +172,11 @@ final class PhabricatorOAuthServer extends Phobject { return null; } + $application = $authorization->getClient(); + if ($application->getIsDisabled()) { + return null; + } + // TODO: This should probably be reworked; expiration should be an // exclusive property of the token. For now, this logic reads: tokens for // authorizations with "offline_access" never expire. diff --git a/src/applications/oauthserver/application/PhabricatorOAuthServerApplication.php b/src/applications/oauthserver/application/PhabricatorOAuthServerApplication.php index 8fa4d11e10..024d9101dd 100644 --- a/src/applications/oauthserver/application/PhabricatorOAuthServerApplication.php +++ b/src/applications/oauthserver/application/PhabricatorOAuthServerApplication.php @@ -53,8 +53,8 @@ final class PhabricatorOAuthServerApplication extends PhabricatorApplication { 'token/' => 'PhabricatorOAuthServerTokenController', $this->getEditRoutePattern('edit/') => 'PhabricatorOAuthClientEditController', - 'client/' => array( - 'delete/(?P\d+)/' => 'PhabricatorOAuthClientDeleteController', + 'client/' => array( + 'disable/(?P\d+)/' => 'PhabricatorOAuthClientDisableController', 'view/(?P\d+)/' => 'PhabricatorOAuthClientViewController', 'secret/(?P\d+)/' => 'PhabricatorOAuthClientSecretController', 'test/(?P\d+)/' => 'PhabricatorOAuthClientTestController', diff --git a/src/applications/oauthserver/controller/PhabricatorOAuthServerAuthController.php b/src/applications/oauthserver/controller/PhabricatorOAuthServerAuthController.php index 675b88e6da..4bffd2de63 100644 --- a/src/applications/oauthserver/controller/PhabricatorOAuthServerAuthController.php +++ b/src/applications/oauthserver/controller/PhabricatorOAuthServerAuthController.php @@ -62,6 +62,15 @@ final class PhabricatorOAuthServerAuthController phutil_tag('strong', array(), 'client_id'))); } + if ($client->getIsDisabled()) { + return $this->buildErrorResponse( + 'invalid_request', + pht('Application Disabled'), + pht( + 'The %s OAuth application has been disabled.', + phutil_tag('strong', array(), 'client_id'))); + } + $name = $client->getName(); $server->setClient($client); if ($redirect_uri) { diff --git a/src/applications/oauthserver/controller/PhabricatorOAuthServerTokenController.php b/src/applications/oauthserver/controller/PhabricatorOAuthServerTokenController.php index 7197ef368d..0ce5d7c3b5 100644 --- a/src/applications/oauthserver/controller/PhabricatorOAuthServerTokenController.php +++ b/src/applications/oauthserver/controller/PhabricatorOAuthServerTokenController.php @@ -67,7 +67,7 @@ final class PhabricatorOAuthServerTokenController $response->setError('invalid_grant'); $response->setErrorDescription( pht( - 'Authorization code %d not found.', + 'Authorization code %s not found.', $code)); return $response; } @@ -102,11 +102,22 @@ final class PhabricatorOAuthServerTokenController $response->setError('invalid_client'); $response->setErrorDescription( pht( - 'Client with %s %d not found.', + 'Client with %s %s not found.', 'client_id', $client_phid)); return $response; } + + if ($client->getIsDisabled()) { + $response->setError('invalid_client'); + $response->setErrorDescription( + pht( + 'OAuth application "%s" has been disabled.', + $client->getName())); + + return $response; + } + $server->setClient($client); $user_phid = $auth_code->getUserPHID(); @@ -116,7 +127,7 @@ final class PhabricatorOAuthServerTokenController $response->setError('invalid_grant'); $response->setErrorDescription( pht( - 'User with PHID %d not found.', + 'User with PHID %s not found.', $user_phid)); return $response; } @@ -132,7 +143,7 @@ final class PhabricatorOAuthServerTokenController $response->setError('invalid_grant'); $response->setErrorDescription( pht( - 'Invalid authorization code %d.', + 'Invalid authorization code %s.', $code)); return $response; } diff --git a/src/applications/oauthserver/controller/client/PhabricatorOAuthClientDeleteController.php b/src/applications/oauthserver/controller/client/PhabricatorOAuthClientDeleteController.php deleted file mode 100644 index a3c47e1604..0000000000 --- a/src/applications/oauthserver/controller/client/PhabricatorOAuthClientDeleteController.php +++ /dev/null @@ -1,40 +0,0 @@ -getViewer(); - - $client = id(new PhabricatorOAuthServerClientQuery()) - ->setViewer($viewer) - ->withIDs(array($request->getURIData('id'))) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->executeOne(); - if (!$client) { - return new Aphront404Response(); - } - - // TODO: This should be "disable", not "delete"! - - if ($request->isFormPost()) { - $client->delete(); - $app_uri = $this->getApplicationURI(); - return id(new AphrontRedirectResponse())->setURI($app_uri); - } - - return $this->newDialog() - ->setTitle(pht('Delete OAuth Application?')) - ->appendParagraph( - pht( - 'Really delete the OAuth application %s?', - phutil_tag('strong', array(), $client->getName()))) - ->addCancelButton($client->getViewURI()) - ->addSubmitButton(pht('Delete Application')); - } - -} diff --git a/src/applications/oauthserver/controller/client/PhabricatorOAuthClientDisableController.php b/src/applications/oauthserver/controller/client/PhabricatorOAuthClientDisableController.php new file mode 100644 index 0000000000..2ea9955365 --- /dev/null +++ b/src/applications/oauthserver/controller/client/PhabricatorOAuthClientDisableController.php @@ -0,0 +1,67 @@ +getViewer(); + + $client = id(new PhabricatorOAuthServerClientQuery()) + ->setViewer($viewer) + ->withIDs(array($request->getURIData('id'))) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->executeOne(); + if (!$client) { + return new Aphront404Response(); + } + + $done_uri = $client->getViewURI(); + $is_disable = !$client->getIsDisabled(); + + if ($request->isFormPost()) { + $xactions = array(); + + $xactions[] = id(new PhabricatorOAuthServerTransaction()) + ->setTransactionType(PhabricatorOAuthServerTransaction::TYPE_DISABLED) + ->setNewValue((int)$is_disable); + + $editor = id(new PhabricatorOAuthServerEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true) + ->setContinueOnMissingFields(true) + ->applyTransactions($client, $xactions); + + return id(new AphrontRedirectResponse())->setURI($done_uri); + } + + if ($is_disable) { + $title = pht('Disable OAuth Application'); + $body = pht( + 'Really disable the %s OAuth application? Users will no longer be '. + 'able to authenticate against it, nor access Phabricator using '. + 'tokens generated by this application.', + phutil_tag('strong', array(), $client->getName())); + $button = pht('Disable Application'); + } else { + $title = pht('Enable OAuth Application'); + $body = pht( + 'Really enable the %s OAuth application? Users will be able to '. + 'authenticate against it, and existing tokens will become usable '. + 'again.', + phutil_tag('strong', array(), $client->getName())); + $button = pht('Enable Application'); + } + + return $this->newDialog() + ->setTitle($title) + ->appendParagraph($body) + ->addCancelButton($done_uri) + ->addSubmitButton($button); + } + +} diff --git a/src/applications/oauthserver/controller/client/PhabricatorOAuthClientTestController.php b/src/applications/oauthserver/controller/client/PhabricatorOAuthClientTestController.php index 9efcb608c2..427c1be2a1 100644 --- a/src/applications/oauthserver/controller/client/PhabricatorOAuthClientTestController.php +++ b/src/applications/oauthserver/controller/client/PhabricatorOAuthClientTestController.php @@ -15,36 +15,41 @@ final class PhabricatorOAuthClientTestController return new Aphront404Response(); } - $view_uri = $client->getViewURI(); - - // Look for an existing authorization. - $authorization = id(new PhabricatorOAuthClientAuthorizationQuery()) - ->setViewer($viewer) - ->withUserPHIDs(array($viewer->getPHID())) - ->withClientPHIDs(array($client->getPHID())) - ->executeOne(); - if ($authorization) { - return $this->newDialog() - ->setTitle(pht('Already Authorized')) - ->appendParagraph( - pht( - 'You have already authorized this application to access your '. - 'account.')) - ->addCancelButton($view_uri, pht('Close')); - } + $done_uri = $client->getViewURI(); if ($request->isFormPost()) { $server = id(new PhabricatorOAuthServer()) ->setUser($viewer) ->setClient($client); - $scope = array(); - $authorization = $server->authorizeClient($scope); + // Create an authorization if we don't already have one. + $authorization = id(new PhabricatorOAuthClientAuthorizationQuery()) + ->setViewer($viewer) + ->withUserPHIDs(array($viewer->getPHID())) + ->withClientPHIDs(array($client->getPHID())) + ->executeOne(); + if (!$authorization) { + $scope = array(); + $authorization = $server->authorizeClient($scope); + } - $id = $authorization->getID(); - $panel_uri = '/settings/panel/oauthorizations/?id='.$id; + $access_token = $server->generateAccessToken(); - return id(new AphrontRedirectResponse())->setURI($panel_uri); + $form = id(new AphrontFormView()) + ->setViewer($viewer) + ->appendInstructions( + pht( + 'Keep this token private, it allows any bearer to access '. + 'your account on behalf of this application.')) + ->appendChild( + id(new AphrontFormTextControl()) + ->setLabel(pht('Token')) + ->setValue($access_token->getToken())); + + return $this->newDialog() + ->setTitle(pht('OAuth Access Token')) + ->appendForm($form) + ->addCancelButton($done_uri, pht('Close')); } // TODO: It would be nice to put scope options in this dialog, maybe? @@ -53,10 +58,10 @@ final class PhabricatorOAuthClientTestController ->setTitle(pht('Authorize Application?')) ->appendParagraph( pht( - 'This will create an authorization, permitting %s to access '. - 'your account.', + 'This will create an authorization and OAuth token, permitting %s '. + 'to access your account.', phutil_tag('strong', array(), $client->getName()))) - ->addCancelButton($view_uri) + ->addCancelButton($done_uri) ->addSubmitButton(pht('Authorize Application')); } } diff --git a/src/applications/oauthserver/controller/client/PhabricatorOAuthClientViewController.php b/src/applications/oauthserver/controller/client/PhabricatorOAuthClientViewController.php index 217667b0b9..394ace52a4 100644 --- a/src/applications/oauthserver/controller/client/PhabricatorOAuthClientViewController.php +++ b/src/applications/oauthserver/controller/client/PhabricatorOAuthClientViewController.php @@ -51,6 +51,12 @@ final class PhabricatorOAuthClientViewController ->setHeader(pht('OAuth Application: %s', $client->getName())) ->setPolicyObject($client); + if ($client->getIsDisabled()) { + $header->setStatus('fa-ban', 'indigo', pht('Disabled')); + } else { + $header->setStatus('fa-check', 'green', pht('Enabled')); + } + return $header; } @@ -62,12 +68,6 @@ final class PhabricatorOAuthClientViewController $client, PhabricatorPolicyCapability::CAN_EDIT); - $authorization = id(new PhabricatorOAuthClientAuthorizationQuery()) - ->setViewer($viewer) - ->withUserPHIDs(array($viewer->getPHID())) - ->withClientPHIDs(array($client->getPHID())) - ->executeOne(); - $is_authorized = (bool)$authorization; $id = $client->getID(); $view = id(new PhabricatorActionListView()) @@ -89,20 +89,30 @@ final class PhabricatorOAuthClientViewController ->setDisabled(!$can_edit) ->setWorkflow(true)); - $view->addAction( - id(new PhabricatorActionView()) - ->setName(pht('Delete Application')) - ->setIcon('fa-times') - ->setWorkflow(true) - ->setDisabled(!$can_edit) - ->setHref($client->getDeleteURI())); + $is_disabled = $client->getIsDisabled(); + if ($is_disabled) { + $disable_text = pht('Enable Application'); + $disable_icon = 'fa-check'; + } else { + $disable_text = pht('Disable Application'); + $disable_icon = 'fa-ban'; + } + + $disable_uri = $this->getApplicationURI("client/disable/{$id}/"); $view->addAction( id(new PhabricatorActionView()) - ->setName(pht('Create Test Authorization')) - ->setIcon('fa-wrench') + ->setName($disable_text) + ->setIcon($disable_icon) + ->setWorkflow(true) + ->setDisabled(!$can_edit) + ->setHref($disable_uri)); + + $view->addAction( + id(new PhabricatorActionView()) + ->setName(pht('Generate Test Token')) + ->setIcon('fa-plus') ->setWorkflow(true) - ->setDisabled($is_authorized) ->setHref($this->getApplicationURI("client/test/{$id}/"))); return $view; diff --git a/src/applications/oauthserver/editor/PhabricatorOAuthServerEditor.php b/src/applications/oauthserver/editor/PhabricatorOAuthServerEditor.php index 4578ccbc6a..32b9d45054 100644 --- a/src/applications/oauthserver/editor/PhabricatorOAuthServerEditor.php +++ b/src/applications/oauthserver/editor/PhabricatorOAuthServerEditor.php @@ -16,6 +16,7 @@ final class PhabricatorOAuthServerEditor $types[] = PhabricatorOAuthServerTransaction::TYPE_NAME; $types[] = PhabricatorOAuthServerTransaction::TYPE_REDIRECT_URI; + $types[] = PhabricatorOAuthServerTransaction::TYPE_DISABLED; $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; @@ -32,6 +33,8 @@ final class PhabricatorOAuthServerEditor return $object->getName(); case PhabricatorOAuthServerTransaction::TYPE_REDIRECT_URI: return $object->getRedirectURI(); + case PhabricatorOAuthServerTransaction::TYPE_DISABLED: + return $object->getIsDisabled(); } } @@ -43,6 +46,8 @@ final class PhabricatorOAuthServerEditor case PhabricatorOAuthServerTransaction::TYPE_NAME: case PhabricatorOAuthServerTransaction::TYPE_REDIRECT_URI: return $xaction->getNewValue(); + case PhabricatorOAuthServerTransaction::TYPE_DISABLED: + return (int)$xaction->getNewValue(); } } @@ -57,6 +62,9 @@ final class PhabricatorOAuthServerEditor case PhabricatorOAuthServerTransaction::TYPE_REDIRECT_URI: $object->setRedirectURI($xaction->getNewValue()); return; + case PhabricatorOAuthServerTransaction::TYPE_DISABLED: + $object->setIsDisabled($xaction->getNewValue()); + return; } return parent::applyCustomInternalTransaction($object, $xaction); @@ -69,6 +77,7 @@ final class PhabricatorOAuthServerEditor switch ($xaction->getTransactionType()) { case PhabricatorOAuthServerTransaction::TYPE_NAME: case PhabricatorOAuthServerTransaction::TYPE_REDIRECT_URI: + case PhabricatorOAuthServerTransaction::TYPE_DISABLED: return; } diff --git a/src/applications/oauthserver/query/PhabricatorOAuthServerClientSearchEngine.php b/src/applications/oauthserver/query/PhabricatorOAuthServerClientSearchEngine.php index 6b51a880b4..3cb027fe4f 100644 --- a/src/applications/oauthserver/query/PhabricatorOAuthServerClientSearchEngine.php +++ b/src/applications/oauthserver/query/PhabricatorOAuthServerClientSearchEngine.php @@ -73,7 +73,7 @@ final class PhabricatorOAuthServerClientSearchEngine array $clients, PhabricatorSavedQuery $query, array $handles) { - assert_instances_of($clients, 'PhabricatorOauthServerClient'); + assert_instances_of($clients, 'PhabricatorOAuthServerClient'); $viewer = $this->requireViewer(); @@ -86,6 +86,10 @@ final class PhabricatorOAuthServerClientSearchEngine ->setHref($client->getViewURI()) ->setObject($client); + if ($client->getIsDisabled()) { + $item->setDisabled(true); + } + $list->addItem($item); } diff --git a/src/applications/oauthserver/storage/PhabricatorOAuthServerClient.php b/src/applications/oauthserver/storage/PhabricatorOAuthServerClient.php index bcd01132b1..4a4f48bfed 100644 --- a/src/applications/oauthserver/storage/PhabricatorOAuthServerClient.php +++ b/src/applications/oauthserver/storage/PhabricatorOAuthServerClient.php @@ -11,9 +11,10 @@ final class PhabricatorOAuthServerClient protected $name; protected $redirectURI; protected $creatorPHID; - protected $isTrusted = 0; + protected $isTrusted; protected $viewPolicy; protected $editPolicy; + protected $isDisabled; public function getEditURI() { $id = $this->getID(); @@ -25,17 +26,14 @@ final class PhabricatorOAuthServerClient return "/oauthserver/client/view/{$id}/"; } - public function getDeleteURI() { - $id = $this->getID(); - return "/oauthserver/client/delete/{$id}/"; - } - public static function initializeNewClient(PhabricatorUser $actor) { return id(new PhabricatorOAuthServerClient()) ->setCreatorPHID($actor->getPHID()) ->setSecret(Filesystem::readRandomCharacters(32)) ->setViewPolicy(PhabricatorPolicies::POLICY_USER) - ->setEditPolicy($actor->getPHID()); + ->setEditPolicy($actor->getPHID()) + ->setIsDisabled(0) + ->setIsTrusted(0); } protected function getConfiguration() { @@ -46,13 +44,9 @@ final class PhabricatorOAuthServerClient 'secret' => 'text32', 'redirectURI' => 'text255', 'isTrusted' => 'bool', + 'isDisabled' => 'bool', ), self::CONFIG_KEY_SCHEMA => array( - 'key_phid' => null, - 'phid' => array( - 'columns' => array('phid'), - 'unique' => true, - ), 'creatorPHID' => array( 'columns' => array('creatorPHID'), ), diff --git a/src/applications/oauthserver/storage/PhabricatorOAuthServerTransaction.php b/src/applications/oauthserver/storage/PhabricatorOAuthServerTransaction.php index 1d65087a07..b2624dd9a4 100644 --- a/src/applications/oauthserver/storage/PhabricatorOAuthServerTransaction.php +++ b/src/applications/oauthserver/storage/PhabricatorOAuthServerTransaction.php @@ -5,6 +5,7 @@ final class PhabricatorOAuthServerTransaction const TYPE_NAME = 'oauthserver.name'; const TYPE_REDIRECT_URI = 'oauthserver.redirect-uri'; + const TYPE_DISABLED = 'oauthserver.disabled'; public function getApplicationName() { return 'oauth_server'; @@ -44,6 +45,16 @@ final class PhabricatorOAuthServerTransaction $this->renderHandleLink($author_phid), $old, $new); + case self::TYPE_DISABLED: + if ($new) { + return pht( + '%s disabled this application.', + $this->renderHandleLink($author_phid)); + } else { + return pht( + '%s enabled this application.', + $this->renderHandleLink($author_phid)); + } } return parent::getTitle(); From c8995ad0fe8a051ab7056a0970fd810d2bc25edc Mon Sep 17 00:00:00 2001 From: Chad Little Date: Tue, 5 Apr 2016 13:43:38 -0700 Subject: [PATCH 51/84] Update phpast for new UI Summary: New UI for phpast Test Plan: Open page, doesn't crash Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15627 --- .../PhabricatorXHPASTViewRunController.php | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/applications/phpast/controller/PhabricatorXHPASTViewRunController.php b/src/applications/phpast/controller/PhabricatorXHPASTViewRunController.php index 290666e375..8a530075ae 100644 --- a/src/applications/phpast/controller/PhabricatorXHPASTViewRunController.php +++ b/src/applications/phpast/controller/PhabricatorXHPASTViewRunController.php @@ -47,13 +47,24 @@ final class PhabricatorXHPASTViewRunController $form_box = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Generate XHP AST')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setForm($form); - return $this->buildApplicationPage( - $form_box, - array( - 'title' => pht('XHPAST View'), + $title = pht('XHPAST View'); + $header = id(new PHUIHeaderView()) + ->setHeader($title) + ->setHeaderIcon('fa-ambulance'); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter(array( + $form_box, )); + + return $this->newPage() + ->setTitle($title) + ->appendChild($view); + } } From 960f8abdf1fc46522b0d5c6d6efd5067d9ca7277 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Tue, 5 Apr 2016 13:25:04 -0700 Subject: [PATCH 52/84] Update Settings for newPage Summary: Converts over to `newPage` Test Plan: Pull up Settings panel, test a few. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15625 --- .../PhabricatorSettingsMainController.php | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/applications/settings/controller/PhabricatorSettingsMainController.php b/src/applications/settings/controller/PhabricatorSettingsMainController.php index 728e2131ad..48683a154c 100644 --- a/src/applications/settings/controller/PhabricatorSettingsMainController.php +++ b/src/applications/settings/controller/PhabricatorSettingsMainController.php @@ -61,17 +61,15 @@ final class PhabricatorSettingsMainController '/p/'.$this->getUser()->getUsername().'/'); } $crumbs->addTextCrumb($panel->getPanelName()); - $nav->appendChild( - array( - $crumbs, - $response, - )); + $nav->appendChild($response); + + $title = $panel->getPanelName(); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->setNavigation($nav); - return $this->buildApplicationPage( - $nav, - array( - 'title' => $panel->getPanelName(), - )); } private function buildPanels() { From 5dec03af323fbe9af8a0c72a67b715f93e141e87 Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 5 Apr 2016 10:48:00 -0700 Subject: [PATCH 53/84] Make OAuth scope handling more flexible Summary: Ref T7303. Currently, our handling of "scope" is fairly rigid and adheres to the spec, but some of these behaviors don't make much sense in practice. Soften some behaviors and make them more flexible: **Soft Failure on Unknown Permissions**: If a client asks for a permission we don't know about, just warn that we don't recognize it instead of fataling. In particular, I plan to make `offline_access` and `whoami` implicit. Older clients that request these permissions will still work fine as long as we don't hard-fatal. **Move `user.whoami` to ALWAYS scope**: Make `whoami` a default permission. We've already done this, in effect; this just formalizes it. **Tokens no longer expire**: Make `offline_access` (infinite-duration tokens) a default permission. I think the OAuth model doesn't map well to reality. It is common for other providers to issue "temporary" tokens with a duration of multiple years, and the refesh workflow is sort of silly. We can add a "temporary" scope later if we need temporary tokens. This flow was potentially extra silly with the "log out of Phacility" use case, where we might need to have you log in again before we could log you out, which is bizarre and senseless. Avoid this nonsense. **Move away from granular permissions**: Users currently get to pick-and-choose which permissions they grant, but this likely rarely/never works in practice and is fairly hostile since applications can't communicate which permissions they need. Applications which can actually operate with only some subset of permissions can make separate requests (e.g., when you activate "cool feature X", it asks for X permission). I think applications that do this are rare; pretty much everything just asks for tons of permissions and everyone grants them. Making this all-or-nothing is better for well-behaved applications and better for users. It's also slightly better for overzealous applications that ask for more than they need, but whatever. Users can make an informed decision, hopefully, and I plan to let administrators force applications to a subset of permissions once we introduce meaningful scopes. Test Plan: - Generated tokens. - Used tokens. - Authorized an instance. - Faked some bogus scopes, got clean authorization. Reviewers: chad Reviewed By: chad Maniphest Tasks: T7303 Differential Revision: https://secure.phabricator.com/D15621 --- .../oauthserver/PhabricatorOAuthServer.php | 13 -- .../PhabricatorOAuthServerScope.php | 116 ++---------------- .../PhabricatorOAuthServerAuthController.php | 77 ++++++------ .../PhabricatorOAuthServerTokenController.php | 3 +- .../PhabricatorOAuthServerAccessToken.php | 14 --- .../conduit/UserWhoAmIConduitAPIMethod.php | 2 +- .../contributor/using_oauthserver.diviner | 10 +- 7 files changed, 55 insertions(+), 180 deletions(-) diff --git a/src/applications/oauthserver/PhabricatorOAuthServer.php b/src/applications/oauthserver/PhabricatorOAuthServer.php index 123b09d6ba..f5c074f4eb 100644 --- a/src/applications/oauthserver/PhabricatorOAuthServer.php +++ b/src/applications/oauthserver/PhabricatorOAuthServer.php @@ -177,19 +177,6 @@ final class PhabricatorOAuthServer extends Phobject { return null; } - // TODO: This should probably be reworked; expiration should be an - // exclusive property of the token. For now, this logic reads: tokens for - // authorizations with "offline_access" never expire. - - $is_expired = $token->isExpired(); - if ($is_expired) { - $offline_access = PhabricatorOAuthServerScope::SCOPE_OFFLINE_ACCESS; - $authorization_scope = $authorization->getScope(); - if (empty($authorization_scope[$offline_access])) { - return null; - } - } - return $authorization; } diff --git a/src/applications/oauthserver/PhabricatorOAuthServerScope.php b/src/applications/oauthserver/PhabricatorOAuthServerScope.php index 105c9cd33d..68aec848f6 100644 --- a/src/applications/oauthserver/PhabricatorOAuthServerScope.php +++ b/src/applications/oauthserver/PhabricatorOAuthServerScope.php @@ -2,120 +2,20 @@ final class PhabricatorOAuthServerScope extends Phobject { - const SCOPE_OFFLINE_ACCESS = 'offline_access'; - const SCOPE_WHOAMI = 'whoami'; - - public static function getScopesDict() { - return array( - self::SCOPE_OFFLINE_ACCESS => 1, - self::SCOPE_WHOAMI => 1, - ); + public static function getScopeMap() { + return array(); } - public static function getDefaultScope() { - return self::SCOPE_WHOAMI; - } + public static function filterScope(array $scope) { + $valid_scopes = self::getScopeMap(); - public static function getCheckboxControl( - array $current_scopes) { - - $have_options = false; - $scopes = self::getScopesDict(); - $scope_keys = array_keys($scopes); - sort($scope_keys); - $default_scope = self::getDefaultScope(); - - $checkboxes = new AphrontFormCheckboxControl(); - foreach ($scope_keys as $scope) { - if ($scope == $default_scope) { - continue; - } - if (!isset($current_scopes[$scope])) { - continue; - } - - $checkboxes->addCheckbox( - $name = $scope, - $value = 1, - $label = self::getCheckboxLabel($scope), - $checked = isset($current_scopes[$scope])); - $have_options = true; - } - - if ($have_options) { - $checkboxes->setLabel(pht('Scope')); - return $checkboxes; - } - - return null; - } - - private static function getCheckboxLabel($scope) { - $label = null; - switch ($scope) { - case self::SCOPE_OFFLINE_ACCESS: - $label = pht('Make access tokens granted to this client never expire.'); - break; - case self::SCOPE_WHOAMI: - $label = pht('Read access to Conduit method %s.', 'user.whoami'); - break; - } - - return $label; - } - - public static function getScopesFromRequest(AphrontRequest $request) { - $scopes = self::getScopesDict(); - $requested_scopes = array(); - foreach ($scopes as $scope => $bit) { - if ($request->getBool($scope)) { - $requested_scopes[$scope] = 1; + foreach ($scope as $key => $scope_item) { + if (!isset($valid_scopes[$scope_item])) { + unset($scope[$key]); } } - $requested_scopes[self::getDefaultScope()] = 1; - return $requested_scopes; - } - /** - * A scopes list is considered valid if each scope is a known scope - * and each scope is seen only once. Otherwise, the list is invalid. - */ - public static function validateScopesList($scope_list) { - $scopes = explode(' ', $scope_list); - $known_scopes = self::getScopesDict(); - $seen_scopes = array(); - foreach ($scopes as $scope) { - if (!isset($known_scopes[$scope])) { - return false; - } - if (isset($seen_scopes[$scope])) { - return false; - } - $seen_scopes[$scope] = 1; - } - - return true; - } - - /** - * A scopes dictionary is considered valid if each key is a known scope. - * Otherwise, the dictionary is invalid. - */ - public static function validateScopesDict($scope_dict) { - $known_scopes = self::getScopesDict(); - $unknown_scopes = array_diff_key($scope_dict, - $known_scopes); - return empty($unknown_scopes); - } - - /** - * Transforms a space-delimited scopes list into a scopes dict. The list - * should be validated by @{method:validateScopesList} before - * transformation. - */ - public static function scopesListToDict($scope_list) { - $scopes = explode(' ', $scope_list); - return array_fill_keys($scopes, 1); + return $scope; } } diff --git a/src/applications/oauthserver/controller/PhabricatorOAuthServerAuthController.php b/src/applications/oauthserver/controller/PhabricatorOAuthServerAuthController.php index 4bffd2de63..b9d916e82b 100644 --- a/src/applications/oauthserver/controller/PhabricatorOAuthServerAuthController.php +++ b/src/applications/oauthserver/controller/PhabricatorOAuthServerAuthController.php @@ -14,7 +14,6 @@ final class PhabricatorOAuthServerAuthController $server = new PhabricatorOAuthServer(); $client_phid = $request->getStr('client_id'); - $scope = $request->getStr('scope'); $redirect_uri = $request->getStr('redirect_uri'); $response_type = $request->getStr('response_type'); @@ -114,24 +113,11 @@ final class PhabricatorOAuthServerAuthController implode(', ', array('code')))); } - if ($scope) { - if (!PhabricatorOAuthServerScope::validateScopesList($scope)) { - return $this->buildErrorResponse( - 'invalid_scope', - pht('Invalid Scope'), - pht( - 'Request parameter %s specifies an unsupported scope.', - phutil_tag('strong', array(), 'scope'))); - } - $scope = PhabricatorOAuthServerScope::scopesListToDict($scope); - } else { - return $this->buildErrorResponse( - 'invalid_request', - pht('Malformed Request'), - pht( - 'Required parameter %s was not present in the request.', - phutil_tag('strong', array(), 'scope'))); - } + + $requested_scope = $request->getStrList('scope'); + $requested_scope = array_fuse($requested_scope); + + $scope = PhabricatorOAuthServerScope::filterScope($requested_scope); // NOTE: We're always requiring a confirmation dialog to redirect. // Partly this is a general defense against redirect attacks, and @@ -142,8 +128,6 @@ final class PhabricatorOAuthServerAuthController list($is_authorized, $authorization) = $auth_info; if ($request->isFormPost()) { - $scope = PhabricatorOAuthServerScope::getScopesFromRequest($request); - if ($authorization) { $authorization->setScope($scope)->save(); } else { @@ -212,16 +196,9 @@ final class PhabricatorOAuthServerAuthController // Here, we're confirming authorization for the application. if ($authorization) { - $desired_scopes = array_merge($scope, $authorization->getScope()); + $missing_scope = array_diff_key($scope, $authorization->getScope()); } else { - $desired_scopes = $scope; - } - - if (!PhabricatorOAuthServerScope::validateScopesDict($desired_scopes)) { - return $this->buildErrorResponse( - 'invalid_scope', - pht('Invalid Scope'), - pht('The requested scope is invalid, unknown, or malformed.')); + $missing_scope = $scope; } $form = id(new AphrontFormView()) @@ -230,9 +207,7 @@ final class PhabricatorOAuthServerAuthController ->addHiddenInput('response_type', $response_type) ->addHiddenInput('state', $state) ->addHiddenInput('scope', $request->getStr('scope')) - ->setUser($viewer) - ->appendChild( - PhabricatorOAuthServerScope::getCheckboxControl($desired_scopes)); + ->setUser($viewer); $cancel_msg = pht('The user declined to authorize this application.'); $cancel_uri = $this->addQueryParams( @@ -242,7 +217,7 @@ final class PhabricatorOAuthServerAuthController 'error_description' => $cancel_msg, )); - return $this->newDialog() + $dialog = $this->newDialog() ->setShortTitle(pht('Authorize Access')) ->setTitle(pht('Authorize "%s"?', $name)) ->setSubmitURI($request->getRequestURI()->getPath()) @@ -253,9 +228,41 @@ final class PhabricatorOAuthServerAuthController 'access your Phabricator account data, including your primary '. 'email address?', phutil_tag('strong', array(), $name))) - ->appendChild($form->buildLayoutView()) + ->appendForm($form) ->addSubmitButton(pht('Authorize Access')) ->addCancelButton((string)$cancel_uri, pht('Do Not Authorize')); + + if ($missing_scope) { + $dialog->appendParagraph( + pht( + 'This application has requested these additional permissions. '. + 'Authorizing it will grant it the permissions it requests:')); + foreach ($missing_scope as $scope_key => $ignored) { + // TODO: Once we introduce more scopes, explain them here. + } + } + + $unknown_scope = array_diff_key($requested_scope, $scope); + if ($unknown_scope) { + $dialog->appendParagraph( + pht( + 'This application also requested additional unrecognized '. + 'permissions. These permissions may have existed in an older '. + 'version of Phabricator, or may be from a future version of '. + 'Phabricator. They will not be granted.')); + + $unknown_form = id(new AphrontFormView()) + ->setViewer($viewer) + ->appendChild( + id(new AphrontFormTextControl()) + ->setLabel(pht('Unknown Scope')) + ->setValue(implode(', ', array_keys($unknown_scope))) + ->setDisabled(true)); + + $dialog->appendForm($unknown_form); + } + + return $dialog; } diff --git a/src/applications/oauthserver/controller/PhabricatorOAuthServerTokenController.php b/src/applications/oauthserver/controller/PhabricatorOAuthServerTokenController.php index 0ce5d7c3b5..3b5ff72575 100644 --- a/src/applications/oauthserver/controller/PhabricatorOAuthServerTokenController.php +++ b/src/applications/oauthserver/controller/PhabricatorOAuthServerTokenController.php @@ -154,8 +154,7 @@ final class PhabricatorOAuthServerTokenController unset($unguarded); $result = array( 'access_token' => $access_token->getToken(), - 'token_type' => 'Bearer', - 'expires_in' => $access_token->getExpiresDuration(), + 'token_type' => 'Bearer', ); return $response->setContent($result); } catch (Exception $e) { diff --git a/src/applications/oauthserver/storage/PhabricatorOAuthServerAccessToken.php b/src/applications/oauthserver/storage/PhabricatorOAuthServerAccessToken.php index c50cac93ea..68b21d23cb 100644 --- a/src/applications/oauthserver/storage/PhabricatorOAuthServerAccessToken.php +++ b/src/applications/oauthserver/storage/PhabricatorOAuthServerAccessToken.php @@ -22,18 +22,4 @@ final class PhabricatorOAuthServerAccessToken ) + parent::getConfiguration(); } - public function isExpired() { - $now = PhabricatorTime::getNow(); - $expires_epoch = $this->getExpiresEpoch(); - return ($now > $expires_epoch); - } - - public function getExpiresEpoch() { - return $this->getDateCreated() + 3600; - } - - public function getExpiresDuration() { - return PhabricatorTime::getNow() - $this->getExpiresEpoch(); - } - } diff --git a/src/applications/people/conduit/UserWhoAmIConduitAPIMethod.php b/src/applications/people/conduit/UserWhoAmIConduitAPIMethod.php index 13884e1e83..2911056f5e 100644 --- a/src/applications/people/conduit/UserWhoAmIConduitAPIMethod.php +++ b/src/applications/people/conduit/UserWhoAmIConduitAPIMethod.php @@ -19,7 +19,7 @@ final class UserWhoAmIConduitAPIMethod extends UserConduitAPIMethod { } public function getRequiredScope() { - return PhabricatorOAuthServerScope::SCOPE_WHOAMI; + return self::SCOPE_ALWAYS; } protected function execute(ConduitAPIRequest $request) { diff --git a/src/docs/contributor/using_oauthserver.diviner b/src/docs/contributor/using_oauthserver.diviner index b09f595a5a..b40496d7cf 100644 --- a/src/docs/contributor/using_oauthserver.diviner +++ b/src/docs/contributor/using_oauthserver.diviner @@ -110,11 +110,7 @@ the entire `Authorization Code Grant` flow. NOTE: See "Scopes" section below for more information on what data is currently exposed through the OAuth Server. -= Scopes = +Scopes +====== -There are only two scopes supported at this time. - -- **offline_access** - allows an access token to work indefinitely without - expiring. -- **whoami** - allows the client to access the results of Conduit.whoami on - behalf of the resource owner. +//This section has not been written yet.// From e6421b6ab311966b0e6bbccb53b26df3771df1bb Mon Sep 17 00:00:00 2001 From: Chad Little Date: Tue, 5 Apr 2016 13:32:06 -0700 Subject: [PATCH 54/84] Update Home for newPage Summary: Converts /home/ to `newPage` Test Plan: Pull up Quick Create page, home, mobile home. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15626 --- .../PhabricatorHomeMainController.php | 9 +++--- .../PhabricatorHomeQuickCreateController.php | 28 +++++++++++++------ 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/applications/home/controller/PhabricatorHomeMainController.php b/src/applications/home/controller/PhabricatorHomeMainController.php index e53c4fac8d..950944188b 100644 --- a/src/applications/home/controller/PhabricatorHomeMainController.php +++ b/src/applications/home/controller/PhabricatorHomeMainController.php @@ -51,11 +51,10 @@ final class PhabricatorHomeMainController extends PhabricatorHomeController { $content = $nav; } - return $this->buildApplicationPage( - $content, - array( - 'title' => 'Phabricator', - )); + return $this->newPage() + ->setTitle('Phabricator') + ->appendChild($content); + } private function buildMainResponse(array $projects) { diff --git a/src/applications/home/controller/PhabricatorHomeQuickCreateController.php b/src/applications/home/controller/PhabricatorHomeQuickCreateController.php index 40f9afeb8c..6429443ed2 100644 --- a/src/applications/home/controller/PhabricatorHomeQuickCreateController.php +++ b/src/applications/home/controller/PhabricatorHomeQuickCreateController.php @@ -19,17 +19,29 @@ final class PhabricatorHomeQuickCreateController ->setHref($item->getHref())); } + $title = pht('Quick Create'); + $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Quick Create')); + $crumbs->setBorder(true); + + $box = id(new PHUIObjectBoxView()) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->setObjectList($list); + + $header = id(new PHUIHeaderView()) + ->setHeader($title) + ->setHeaderIcon('fa-plus-square'); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter($box); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); - return $this->buildApplicationPage( - array( - $crumbs, - $list, - ), - array( - 'title' => pht('Quick Create'), - )); } } From 46881c4ce507b5f1eb8e57e5f12d52c39da9e46d Mon Sep 17 00:00:00 2001 From: epriestley Date: Sun, 3 Apr 2016 10:04:10 -0700 Subject: [PATCH 55/84] Add a session engine extension point Summary: Ref T7673. This is really just so I can force admin.phacility.com logout when you log out of an instance, but there are a few other things we could move here eventually, like the WILLREGISTERUSER event. Test Plan: Logged out of an instance, got logged out of parent (see next change). Reviewers: chad Reviewed By: chad Maniphest Tasks: T7673 Differential Revision: https://secure.phabricator.com/D15629 --- src/__phutil_library_map__.php | 4 ++ .../PhabricatorLogoutController.php | 11 ++-- .../engine/PhabricatorAuthSessionEngine.php | 18 +++++++ .../PhabricatorAuthSessionEngineExtension.php | 23 ++++++++ ...icatorAuthSessionEngineExtensionModule.php | 49 +++++++++++++++++ .../PhabricatorPhabricatorAuthProvider.php | 5 ++ .../query/PhabricatorExternalAccountQuery.php | 53 ++++++++----------- .../storage/PhabricatorExternalAccount.php | 8 ++- 8 files changed, 127 insertions(+), 44 deletions(-) create mode 100644 src/applications/auth/engine/PhabricatorAuthSessionEngineExtension.php create mode 100644 src/applications/auth/engine/PhabricatorAuthSessionEngineExtensionModule.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 824293188a..99c9c32b55 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1848,6 +1848,8 @@ phutil_register_library_map(array( 'PhabricatorAuthSSHPublicKey' => 'applications/auth/sshkey/PhabricatorAuthSSHPublicKey.php', 'PhabricatorAuthSession' => 'applications/auth/storage/PhabricatorAuthSession.php', 'PhabricatorAuthSessionEngine' => 'applications/auth/engine/PhabricatorAuthSessionEngine.php', + 'PhabricatorAuthSessionEngineExtension' => 'applications/auth/engine/PhabricatorAuthSessionEngineExtension.php', + 'PhabricatorAuthSessionEngineExtensionModule' => 'applications/auth/engine/PhabricatorAuthSessionEngineExtensionModule.php', 'PhabricatorAuthSessionGarbageCollector' => 'applications/auth/garbagecollector/PhabricatorAuthSessionGarbageCollector.php', 'PhabricatorAuthSessionQuery' => 'applications/auth/query/PhabricatorAuthSessionQuery.php', 'PhabricatorAuthSetupCheck' => 'applications/config/check/PhabricatorAuthSetupCheck.php', @@ -6206,6 +6208,8 @@ phutil_register_library_map(array( 'PhabricatorPolicyInterface', ), 'PhabricatorAuthSessionEngine' => 'Phobject', + 'PhabricatorAuthSessionEngineExtension' => 'Phobject', + 'PhabricatorAuthSessionEngineExtensionModule' => 'PhabricatorConfigModule', 'PhabricatorAuthSessionGarbageCollector' => 'PhabricatorGarbageCollector', 'PhabricatorAuthSessionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorAuthSetupCheck' => 'PhabricatorSetupCheck', diff --git a/src/applications/auth/controller/PhabricatorLogoutController.php b/src/applications/auth/controller/PhabricatorLogoutController.php index 2600a08313..f4b61f88a3 100644 --- a/src/applications/auth/controller/PhabricatorLogoutController.php +++ b/src/applications/auth/controller/PhabricatorLogoutController.php @@ -29,13 +29,6 @@ final class PhabricatorLogoutController $viewer = $this->getViewer(); if ($request->isFormPost()) { - - $log = PhabricatorUserLog::initializeNewLog( - $viewer, - $viewer->getPHID(), - PhabricatorUserLog::ACTION_LOGOUT); - $log->save(); - // Destroy the user's session in the database so logout works even if // their cookies have some issues. We'll detect cookie issues when they // try to login again and tell them to clear any junk. @@ -45,8 +38,10 @@ final class PhabricatorLogoutController ->setViewer($viewer) ->withSessionKeys(array($phsid)) ->executeOne(); + if ($session) { - $session->delete(); + $engine = new PhabricatorAuthSessionEngine(); + $engine->logoutSession($viewer, $session); } } $request->clearCookie(PhabricatorCookies::COOKIE_SESSION); diff --git a/src/applications/auth/engine/PhabricatorAuthSessionEngine.php b/src/applications/auth/engine/PhabricatorAuthSessionEngine.php index 98b6a63b5a..1547d6bea8 100644 --- a/src/applications/auth/engine/PhabricatorAuthSessionEngine.php +++ b/src/applications/auth/engine/PhabricatorAuthSessionEngine.php @@ -297,6 +297,24 @@ final class PhabricatorAuthSessionEngine extends Phobject { } } + public function logoutSession( + PhabricatorUser $user, + PhabricatorAuthSession $session) { + + $log = PhabricatorUserLog::initializeNewLog( + $user, + $user->getPHID(), + PhabricatorUserLog::ACTION_LOGOUT); + $log->save(); + + $extensions = PhabricatorAuthSessionEngineExtension::getAllExtensions(); + foreach ($extensions as $extension) { + $extension->didLogout($user, array($session)); + } + + $session->delete(); + } + /* -( High Security )------------------------------------------------------ */ diff --git a/src/applications/auth/engine/PhabricatorAuthSessionEngineExtension.php b/src/applications/auth/engine/PhabricatorAuthSessionEngineExtension.php new file mode 100644 index 0000000000..267f5be7b4 --- /dev/null +++ b/src/applications/auth/engine/PhabricatorAuthSessionEngineExtension.php @@ -0,0 +1,23 @@ +getPhobjectClassConstant('EXTENSIONKEY'); + } + + final public static function getAllExtensions() { + return id(new PhutilClassMapQuery()) + ->setAncestorClass(__CLASS__) + ->setUniqueMethod('getExtensionKey') + ->execute(); + } + + abstract public function getExtensionName(); + + public function didLogout(PhabricatorUser $user, array $sessions) { + return; + } + +} diff --git a/src/applications/auth/engine/PhabricatorAuthSessionEngineExtensionModule.php b/src/applications/auth/engine/PhabricatorAuthSessionEngineExtensionModule.php new file mode 100644 index 0000000000..9468321d2d --- /dev/null +++ b/src/applications/auth/engine/PhabricatorAuthSessionEngineExtensionModule.php @@ -0,0 +1,49 @@ +getViewer(); + + $extensions = PhabricatorAuthSessionEngineExtension::getAllExtensions(); + + $rows = array(); + foreach ($extensions as $extension) { + $rows[] = array( + get_class($extension), + $extension->getExtensionKey(), + $extension->getExtensionName(), + ); + } + + $table = id(new AphrontTableView($rows)) + ->setNoDataString( + pht('There are no registered session engine extensions.')) + ->setHeaders( + array( + pht('Class'), + pht('Key'), + pht('Name'), + )) + ->setColumnClasses( + array( + null, + null, + 'wide pri', + )); + + return id(new PHUIObjectBoxView()) + ->setHeaderText(pht('SessionEngine Extensions')) + ->setTable($table); + } + +} diff --git a/src/applications/auth/provider/PhabricatorPhabricatorAuthProvider.php b/src/applications/auth/provider/PhabricatorPhabricatorAuthProvider.php index 8ea5e71f43..8799c43c6f 100644 --- a/src/applications/auth/provider/PhabricatorPhabricatorAuthProvider.php +++ b/src/applications/auth/provider/PhabricatorPhabricatorAuthProvider.php @@ -201,4 +201,9 @@ final class PhabricatorPhabricatorAuthProvider return true; } + public function getPhabricatorURI() { + $config = $this->getProviderConfig(); + return $config->getProperty(self::PROPERTY_PHABRICATOR_URI); + } + } diff --git a/src/applications/auth/query/PhabricatorExternalAccountQuery.php b/src/applications/auth/query/PhabricatorExternalAccountQuery.php index 9bb611015d..b34199ce60 100644 --- a/src/applications/auth/query/PhabricatorExternalAccountQuery.php +++ b/src/applications/auth/query/PhabricatorExternalAccountQuery.php @@ -62,19 +62,12 @@ final class PhabricatorExternalAccountQuery return $this; } + public function newResultObject() { + return new PhabricatorExternalAccount(); + } + protected function loadPage() { - $table = new PhabricatorExternalAccount(); - $conn_r = $table->establishConnection('r'); - - $data = queryfx_all( - $conn_r, - 'SELECT * FROM %T %Q %Q %Q', - $table->getTableName(), - $this->buildWhereClause($conn_r), - $this->buildOrderClause($conn_r), - $this->buildLimitClause($conn_r)); - - return $table->loadAllFromArray($data); + return $this->loadStandardPage($this->newResultObject()); } protected function willFilterPage(array $accounts) { @@ -116,61 +109,59 @@ final class PhabricatorExternalAccountQuery return $accounts; } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { - $where = array(); + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { + $where = parent::buildWhereClauseParts($conn); - $where[] = $this->buildPagingClause($conn_r); - - if ($this->ids) { + if ($this->ids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'id IN (%Ld)', $this->ids); } - if ($this->phids) { + if ($this->phids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'phid IN (%Ls)', $this->phids); } - if ($this->accountTypes) { + if ($this->accountTypes !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'accountType IN (%Ls)', $this->accountTypes); } - if ($this->accountDomains) { + if ($this->accountDomains !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'accountDomain IN (%Ls)', $this->accountDomains); } - if ($this->accountIDs) { + if ($this->accountIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'accountID IN (%Ls)', $this->accountIDs); } - if ($this->userPHIDs) { + if ($this->userPHIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'userPHID IN (%Ls)', $this->userPHIDs); } - if ($this->accountSecrets) { + if ($this->accountSecrets !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'accountSecret IN (%Ls)', $this->accountSecrets); } - return $this->formatWhereClause($where); + return $where; } public function getQueryApplicationClass() { diff --git a/src/applications/people/storage/PhabricatorExternalAccount.php b/src/applications/people/storage/PhabricatorExternalAccount.php index 12d5f545a7..4bc0fcae98 100644 --- a/src/applications/people/storage/PhabricatorExternalAccount.php +++ b/src/applications/people/storage/PhabricatorExternalAccount.php @@ -54,15 +54,13 @@ final class PhabricatorExternalAccount extends PhabricatorUserDAO 'accountURI' => 'text255?', ), self::CONFIG_KEY_SCHEMA => array( - 'key_phid' => null, - 'phid' => array( - 'columns' => array('phid'), - 'unique' => true, - ), 'account_details' => array( 'columns' => array('accountType', 'accountDomain', 'accountID'), 'unique' => true, ), + 'key_user' => array( + 'columns' => array('userPHID'), + ), ), ) + parent::getConfiguration(); } From 1914ea28ebf27680df740fd7053feee4feacbd20 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Tue, 5 Apr 2016 15:55:04 -0700 Subject: [PATCH 56/84] Update Files to new UI Summary: Modernize Files a bit, use newPage Test Plan: New file, drag and drop file, view file, edit file Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15631 --- .../PhabricatorFileComposeController.php | 5 +- .../PhabricatorFileDeleteController.php | 17 +++--- .../PhabricatorFileEditController.php | 30 +++++++---- .../PhabricatorFileInfoController.php | 52 ++++++++++--------- ...PhabricatorFileTransformListController.php | 27 ++++++---- .../PhabricatorFileUploadController.php | 22 +++++--- .../PhabricatorFileUploadDialogController.php | 4 +- 7 files changed, 89 insertions(+), 68 deletions(-) diff --git a/src/applications/files/controller/PhabricatorFileComposeController.php b/src/applications/files/controller/PhabricatorFileComposeController.php index 1eaafdbad0..6a4536d94a 100644 --- a/src/applications/files/controller/PhabricatorFileComposeController.php +++ b/src/applications/files/controller/PhabricatorFileComposeController.php @@ -155,8 +155,7 @@ final class PhabricatorFileComposeController 'defaultIcon' => $value_icon, )); - $dialog = id(new AphrontDialogView()) - ->setUser($viewer) + return $this->newDialog() ->setFormID($dialog_id) ->setClass('compose-dialog') ->setTitle(pht('Compose Image')) @@ -188,8 +187,6 @@ final class PhabricatorFileComposeController ->appendChild($icon_input) ->addCancelButton('/') ->addSubmitButton(pht('Save Image')); - - return id(new AphrontDialogResponse())->setDialog($dialog); } private function getIconMap() { diff --git a/src/applications/files/controller/PhabricatorFileDeleteController.php b/src/applications/files/controller/PhabricatorFileDeleteController.php index a07fe2e91a..acca7c9b1c 100644 --- a/src/applications/files/controller/PhabricatorFileDeleteController.php +++ b/src/applications/files/controller/PhabricatorFileDeleteController.php @@ -29,17 +29,14 @@ final class PhabricatorFileDeleteController extends PhabricatorFileController { return id(new AphrontRedirectResponse())->setURI('/file/'); } - $dialog = new AphrontDialogView(); - $dialog->setUser($viewer); - $dialog->setTitle(pht('Really delete file?')); - $dialog->appendChild(hsprintf( + return $this->newDialog() + ->setTitle(pht('Really delete file?')) + ->appendChild(hsprintf( '

%s

', pht( - "Permanently delete '%s'? This action can not be undone.", - $file->getName()))); - $dialog->addSubmitButton(pht('Delete')); - $dialog->addCancelButton($file->getInfoURI()); - - return id(new AphrontDialogResponse())->setDialog($dialog); + 'Permanently delete "%s"? This action can not be undone.', + $file->getName()))) + ->addSubmitButton(pht('Delete')) + ->addCancelButton($file->getInfoURI()); } } diff --git a/src/applications/files/controller/PhabricatorFileEditController.php b/src/applications/files/controller/PhabricatorFileEditController.php index 9c416b588c..e1b34afd73 100644 --- a/src/applications/files/controller/PhabricatorFileEditController.php +++ b/src/applications/files/controller/PhabricatorFileEditController.php @@ -19,8 +19,9 @@ final class PhabricatorFileEditController extends PhabricatorFileController { return new Aphront404Response(); } - $title = pht('Edit %s', $file->getName()); + $title = pht('Edit File: %s', $file->getName()); $file_name = $file->getName(); + $header_icon = 'fa-pencil'; $view_uri = '/'.$file->getMonogram(); $error_name = true; $validation_exception = null; @@ -86,21 +87,28 @@ final class PhabricatorFileEditController extends PhabricatorFileController { $crumbs = $this->buildApplicationCrumbs() ->addTextCrumb($file->getMonogram(), $view_uri) - ->addTextCrumb(pht('Edit')); + ->addTextCrumb(pht('Edit')) + ->setBorder(true); - $object_box = id(new PHUIObjectBoxView()) + $box = id(new PHUIObjectBoxView()) ->setHeaderText($title) ->setValidationException($validation_exception) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($form); - return $this->buildApplicationPage( - array( - $crumbs, - $object_box, - ), - array( - 'title' => $title, - )); + $header = id(new PHUIHeaderView()) + ->setHeader($title) + ->setHeaderIcon($header_icon); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter($box); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); + } } diff --git a/src/applications/files/controller/PhabricatorFileInfoController.php b/src/applications/files/controller/PhabricatorFileInfoController.php index 2731b7f4bb..66050b697e 100644 --- a/src/applications/files/controller/PhabricatorFileInfoController.php +++ b/src/applications/files/controller/PhabricatorFileInfoController.php @@ -35,7 +35,8 @@ final class PhabricatorFileInfoController extends PhabricatorFileController { $header = id(new PHUIHeaderView()) ->setUser($viewer) ->setPolicyObject($file) - ->setHeader($file->getName()); + ->setHeader($file->getName()) + ->setHeaderIcon('fa-file-o'); $ttl = $file->getTTL(); if ($ttl !== null) { @@ -55,28 +56,35 @@ final class PhabricatorFileInfoController extends PhabricatorFileController { $header->addTag($partial_tag); } - $actions = $this->buildActionView($file); + $curtain = $this->buildCurtainView($file); $timeline = $this->buildTransactionView($file); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb( 'F'.$file->getID(), $this->getApplicationURI("/info/{$phid}/")); + $crumbs->setBorder(true); $object_box = id(new PHUIObjectBoxView()) - ->setHeader($header); + ->setHeaderText(pht('File')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY); - $this->buildPropertyViews($object_box, $file, $actions); + $this->buildPropertyViews($object_box, $file); + $title = $file->getName(); - return $this->buildApplicationPage( - array( - $crumbs, + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setCurtain($curtain) + ->setMainColumn(array( $object_box, $timeline, - ), - array( - 'title' => $file->getName(), - 'pageObjects' => array($file->getPHID()), )); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->setPageObjectPHIDs(array($file->getPHID())) + ->appendChild($view); + } private function buildTransactionView(PhabricatorFile $file) { @@ -108,7 +116,7 @@ final class PhabricatorFileInfoController extends PhabricatorFileController { ); } - private function buildActionView(PhabricatorFile $file) { + private function buildCurtainView(PhabricatorFile $file) { $viewer = $this->getViewer(); $id = $file->getID(); @@ -118,14 +126,12 @@ final class PhabricatorFileInfoController extends PhabricatorFileController { $file, PhabricatorPolicyCapability::CAN_EDIT); - $view = id(new PhabricatorActionListView()) - ->setUser($viewer) - ->setObject($file); + $curtain = $this->newCurtainView($file); $can_download = !$file->getIsPartial(); if ($file->isViewableInBrowser()) { - $view->addAction( + $curtain->addAction( id(new PhabricatorActionView()) ->setName(pht('View File')) ->setIcon('fa-file-o') @@ -133,7 +139,7 @@ final class PhabricatorFileInfoController extends PhabricatorFileController { ->setDisabled(!$can_download) ->setWorkflow(!$can_download)); } else { - $view->addAction( + $curtain->addAction( id(new PhabricatorActionView()) ->setUser($viewer) ->setRenderAsForm($can_download) @@ -145,7 +151,7 @@ final class PhabricatorFileInfoController extends PhabricatorFileController { ->setWorkflow(!$can_download)); } - $view->addAction( + $curtain->addAction( id(new PhabricatorActionView()) ->setName(pht('Edit File')) ->setIcon('fa-pencil') @@ -153,7 +159,7 @@ final class PhabricatorFileInfoController extends PhabricatorFileController { ->setWorkflow(!$can_edit) ->setDisabled(!$can_edit)); - $view->addAction( + $curtain->addAction( id(new PhabricatorActionView()) ->setName(pht('Delete File')) ->setIcon('fa-times') @@ -161,24 +167,22 @@ final class PhabricatorFileInfoController extends PhabricatorFileController { ->setWorkflow(true) ->setDisabled(!$can_edit)); - $view->addAction( + $curtain->addAction( id(new PhabricatorActionView()) ->setName(pht('View Transforms')) ->setIcon('fa-crop') ->setHref($this->getApplicationURI("/transforms/{$id}/"))); - return $view; + return $curtain; } private function buildPropertyViews( PHUIObjectBoxView $box, - PhabricatorFile $file, - PhabricatorActionListView $actions) { + PhabricatorFile $file) { $request = $this->getRequest(); $viewer = $request->getUser(); $properties = id(new PHUIPropertyListView()); - $properties->setActionList($actions); $box->addPropertyList($properties, pht('Details')); if ($file->getAuthorPHID()) { diff --git a/src/applications/files/controller/PhabricatorFileTransformListController.php b/src/applications/files/controller/PhabricatorFileTransformListController.php index 767254ad3a..026f60320e 100644 --- a/src/applications/files/controller/PhabricatorFileTransformListController.php +++ b/src/applications/files/controller/PhabricatorFileTransformListController.php @@ -113,26 +113,35 @@ final class PhabricatorFileTransformListController $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb($monogram, '/'.$monogram); $crumbs->addTextCrumb(pht('Transforms')); + $crumbs->setBorder(true); $dst_box = id(new PHUIObjectBoxView()) ->setHeaderText(pht('File Sources')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setTable($dst_table); $src_box = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Available Transforms')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setTable($src_table); - return $this->buildApplicationPage( - array( - $crumbs, + $title = pht('%s Transforms', $file->getName()); + + $header = id(new PHUIHeaderView()) + ->setHeader($title) + ->setHeaderIcon('fa-arrows-alt'); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter(array( $dst_box, $src_box, - ), - array( - 'title' => array( - pht('%s %s', $monogram, $file->getName()), - pht('Tranforms'), - ), )); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); + } } diff --git a/src/applications/files/controller/PhabricatorFileUploadController.php b/src/applications/files/controller/PhabricatorFileUploadController.php index a02b298f25..3be0ca3968 100644 --- a/src/applications/files/controller/PhabricatorFileUploadController.php +++ b/src/applications/files/controller/PhabricatorFileUploadController.php @@ -81,6 +81,7 @@ final class PhabricatorFileUploadController extends PhabricatorFileController { $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Upload'), $request->getRequestURI()); + $crumbs->setBorder(true); $title = pht('Upload File'); @@ -89,19 +90,26 @@ final class PhabricatorFileUploadController extends PhabricatorFileController { ->setShowIfSupportedID($support_id); $form_box = id(new PHUIObjectBoxView()) - ->setHeaderText($title) + ->setHeaderText(pht('File')) ->setFormErrors($errors) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setForm($form); - return $this->buildApplicationPage( - array( - $crumbs, + $header = id(new PHUIHeaderView()) + ->setHeader($title) + ->setHeaderIcon('fa-upload'); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter(array( $form_box, $global_upload, - ), - array( - 'title' => $title, )); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->appendChild($view); } } diff --git a/src/applications/files/controller/PhabricatorFileUploadDialogController.php b/src/applications/files/controller/PhabricatorFileUploadDialogController.php index dd22caa74a..cf13f4d694 100644 --- a/src/applications/files/controller/PhabricatorFileUploadDialogController.php +++ b/src/applications/files/controller/PhabricatorFileUploadDialogController.php @@ -6,14 +6,12 @@ final class PhabricatorFileUploadDialogController public function handleRequest(AphrontRequest $request) { $viewer = $request->getViewer(); - $dialog = id(new AphrontDialogView()) - ->setUser($viewer) + return $this->newDialog() ->setTitle(pht('Upload File')) ->appendChild(pht( 'To add files, drag and drop them into the comment text area.')) ->addCancelButton('/', pht('Close')); - return id(new AphrontDialogResponse())->setDialog($dialog); } } From 9518a1a9a6420eb2bf9dd0f26ff439cd73e7a6a5 Mon Sep 17 00:00:00 2001 From: lkassianik Date: Tue, 5 Apr 2016 11:21:44 -0700 Subject: [PATCH 57/84] Add `badges.edit` and `badges.search` to Conduit API Summary: Ref T10671 Test Plan: Open Conduit application, open `badges.edit` or `badges.search`, create, edit, or query for a badge. Reviewers: epriestley, #blessed_reviewers Reviewed By: epriestley, #blessed_reviewers Subscribers: Korvin Maniphest Tasks: T10671 Differential Revision: https://secure.phabricator.com/D15622 --- src/__phutil_library_map__.php | 5 +++ .../PhabricatorBadgesEditConduitAPIMethod.php | 19 ++++++++++ ...habricatorBadgesSearchConduitAPIMethod.php | 18 +++++++++ .../editor/PhabricatorBadgesEditEngine.php | 5 +++ .../badges/storage/PhabricatorBadgesBadge.php | 37 ++++++++++++++++++- .../paste/storage/PhabricatorPaste.php | 2 +- 6 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 src/applications/badges/conduit/PhabricatorBadgesEditConduitAPIMethod.php create mode 100644 src/applications/badges/conduit/PhabricatorBadgesSearchConduitAPIMethod.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 99c9c32b55..13e4b98341 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1878,6 +1878,7 @@ phutil_register_library_map(array( 'PhabricatorBadgesCreateCapability' => 'applications/badges/capability/PhabricatorBadgesCreateCapability.php', 'PhabricatorBadgesDAO' => 'applications/badges/storage/PhabricatorBadgesDAO.php', 'PhabricatorBadgesDefaultEditCapability' => 'applications/badges/capability/PhabricatorBadgesDefaultEditCapability.php', + 'PhabricatorBadgesEditConduitAPIMethod' => 'applications/badges/conduit/PhabricatorBadgesEditConduitAPIMethod.php', 'PhabricatorBadgesEditController' => 'applications/badges/controller/PhabricatorBadgesEditController.php', 'PhabricatorBadgesEditEngine' => 'applications/badges/editor/PhabricatorBadgesEditEngine.php', 'PhabricatorBadgesEditRecipientsController' => 'applications/badges/controller/PhabricatorBadgesEditRecipientsController.php', @@ -1892,6 +1893,7 @@ phutil_register_library_map(array( 'PhabricatorBadgesRemoveRecipientsController' => 'applications/badges/controller/PhabricatorBadgesRemoveRecipientsController.php', 'PhabricatorBadgesReplyHandler' => 'applications/badges/mail/PhabricatorBadgesReplyHandler.php', 'PhabricatorBadgesSchemaSpec' => 'applications/badges/storage/PhabricatorBadgesSchemaSpec.php', + 'PhabricatorBadgesSearchConduitAPIMethod' => 'applications/badges/conduit/PhabricatorBadgesSearchConduitAPIMethod.php', 'PhabricatorBadgesSearchEngine' => 'applications/badges/query/PhabricatorBadgesSearchEngine.php', 'PhabricatorBadgesTransaction' => 'applications/badges/storage/PhabricatorBadgesTransaction.php', 'PhabricatorBadgesTransactionComment' => 'applications/badges/storage/PhabricatorBadgesTransactionComment.php', @@ -6247,12 +6249,14 @@ phutil_register_library_map(array( 'PhabricatorTokenReceiverInterface', 'PhabricatorFlaggableInterface', 'PhabricatorDestructibleInterface', + 'PhabricatorConduitResultInterface', ), 'PhabricatorBadgesCommentController' => 'PhabricatorBadgesController', 'PhabricatorBadgesController' => 'PhabricatorController', 'PhabricatorBadgesCreateCapability' => 'PhabricatorPolicyCapability', 'PhabricatorBadgesDAO' => 'PhabricatorLiskDAO', 'PhabricatorBadgesDefaultEditCapability' => 'PhabricatorPolicyCapability', + 'PhabricatorBadgesEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod', 'PhabricatorBadgesEditController' => 'PhabricatorBadgesController', 'PhabricatorBadgesEditEngine' => 'PhabricatorEditEngine', 'PhabricatorBadgesEditRecipientsController' => 'PhabricatorBadgesController', @@ -6267,6 +6271,7 @@ phutil_register_library_map(array( 'PhabricatorBadgesRemoveRecipientsController' => 'PhabricatorBadgesController', 'PhabricatorBadgesReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler', 'PhabricatorBadgesSchemaSpec' => 'PhabricatorConfigSchemaSpec', + 'PhabricatorBadgesSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod', 'PhabricatorBadgesSearchEngine' => 'PhabricatorApplicationSearchEngine', 'PhabricatorBadgesTransaction' => 'PhabricatorApplicationTransaction', 'PhabricatorBadgesTransactionComment' => 'PhabricatorApplicationTransactionComment', diff --git a/src/applications/badges/conduit/PhabricatorBadgesEditConduitAPIMethod.php b/src/applications/badges/conduit/PhabricatorBadgesEditConduitAPIMethod.php new file mode 100644 index 0000000000..42be0c2ba3 --- /dev/null +++ b/src/applications/badges/conduit/PhabricatorBadgesEditConduitAPIMethod.php @@ -0,0 +1,19 @@ +setKey('name') ->setLabel(pht('Name')) ->setDescription(pht('Badge name.')) + ->setConduitTypeDescription(pht('New badge name.')) ->setTransactionType(PhabricatorBadgesTransaction::TYPE_NAME) ->setValue($object->getName()), id(new PhabricatorTextEditField()) ->setKey('flavor') ->setLabel(pht('Flavor text')) ->setDescription(pht('Short description of the badge.')) + ->setConduitTypeDescription(pht('New badge flavor.')) ->setValue($object->getFlavor()) ->setTransactionType(PhabricatorBadgesTransaction::TYPE_FLAVOR), id(new PhabricatorIconSetEditField()) @@ -100,6 +102,8 @@ final class PhabricatorBadgesEditEngine id(new PhabricatorSelectEditField()) ->setKey('quality') ->setLabel(pht('Quality')) + ->setDescription(pht('Color and rarity of the badge.')) + ->setConduitTypeDescription(pht('New badge quality.')) ->setValue($object->getQuality()) ->setTransactionType(PhabricatorBadgesTransaction::TYPE_QUALITY) ->setOptions(PhabricatorBadgesQuality::getDropdownQualityMap()), @@ -107,6 +111,7 @@ final class PhabricatorBadgesEditEngine ->setKey('description') ->setLabel(pht('Description')) ->setDescription(pht('Badge long description.')) + ->setConduitTypeDescription(pht('New badge description.')) ->setTransactionType(PhabricatorBadgesTransaction::TYPE_DESCRIPTION) ->setValue($object->getDescription()), ); diff --git a/src/applications/badges/storage/PhabricatorBadgesBadge.php b/src/applications/badges/storage/PhabricatorBadgesBadge.php index c5b29df09d..95ceb7a9e8 100644 --- a/src/applications/badges/storage/PhabricatorBadgesBadge.php +++ b/src/applications/badges/storage/PhabricatorBadgesBadge.php @@ -7,7 +7,8 @@ final class PhabricatorBadgesBadge extends PhabricatorBadgesDAO PhabricatorSubscribableInterface, PhabricatorTokenReceiverInterface, PhabricatorFlaggableInterface, - PhabricatorDestructibleInterface { + PhabricatorDestructibleInterface, + PhabricatorConduitResultInterface { protected $name; protected $flavor; @@ -49,6 +50,8 @@ final class PhabricatorBadgesBadge extends PhabricatorBadgesDAO ->setQuality(PhabricatorBadgesQuality::DEFAULT_QUALITY) ->setCreatorPHID($actor->getPHID()) ->setEditPolicy($edit_policy) + ->setFlavor('') + ->setDescription('') ->setStatus(self::STATUS_ACTIVE); } @@ -190,4 +193,36 @@ final class PhabricatorBadgesBadge extends PhabricatorBadgesDAO $this->saveTransaction(); } +/* -( PhabricatorConduitResultInterface )---------------------------------- */ + + + public function getFieldSpecificationsForConduit() { + return array( + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('name') + ->setType('string') + ->setDescription(pht('The name of the badge.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('creatorPHID') + ->setType('phid') + ->setDescription(pht('User PHID of the creator.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('status') + ->setType('string') + ->setDescription(pht('Active or archived status of the badge.')), + ); + } + + public function getFieldValuesForConduit() { + return array( + 'name' => $this->getName(), + 'creatorPHID' => $this->getCreatorPHID(), + 'status' => $this->getStatus(), + ); + } + + public function getConduitSearchAttachments() { + return array(); + } + } diff --git a/src/applications/paste/storage/PhabricatorPaste.php b/src/applications/paste/storage/PhabricatorPaste.php index bc0909cd45..0e8f497936 100644 --- a/src/applications/paste/storage/PhabricatorPaste.php +++ b/src/applications/paste/storage/PhabricatorPaste.php @@ -264,7 +264,7 @@ final class PhabricatorPaste extends PhabricatorPasteDAO id(new PhabricatorConduitSearchFieldSpecification()) ->setKey('status') ->setType('string') - ->setDescription(pht('Active or arhived status of the paste.')), + ->setDescription(pht('Active or archived status of the paste.')), ); } From ee814923a22e20f2c64c2fba9105aef167e81766 Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 5 Apr 2016 16:59:25 -0700 Subject: [PATCH 58/84] Improve Amazon SES code error handling behavior Summary: Fixes T10728. Fixes T10476. SES uses third-party code with unique, creative ideas about error handling. - Make the error handling behavior more correct, so it doesn't try to use undefined variables. - Simplify the error handling behavior (throw exceptions sooner, remove redundant code). - Explicitly test for `-smtp` misconfigurations. These can arise if you read the wrong column out of the table in the AWS docs, as in T10728. - Explicitly test for SimpleXML, to catch T10476 before it does damage. Test Plan: - Configured SES to use a bogus SMTP endpoint. - Faked past the SMTP check, hit sane error on the connection. - Undid faking, hit immediate hard stop on the STMP check. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10476, T10728 Differential Revision: https://secure.phabricator.com/D15632 --- externals/amazon-ses/ses.php | 94 ++++++++++++++---------------------- 1 file changed, 37 insertions(+), 57 deletions(-) diff --git a/externals/amazon-ses/ses.php b/externals/amazon-ses/ses.php index 266056ec8f..9968e33ac9 100644 --- a/externals/amazon-ses/ses.php +++ b/externals/amazon-ses/ses.php @@ -80,9 +80,30 @@ class SimpleEmailService * @return void */ public function __construct($accessKey = null, $secretKey = null, $host = 'email.us-east-1.amazonaws.com') { + if (!function_exists('simplexml_load_string')) { + throw new Exception( + pht( + 'The PHP SimpleXML extension is not available, but this '. + 'extension is required to send mail via Amazon SES, because '. + 'Amazon SES returns API responses in XML format. Install or '. + 'enable the SimpleXML extension.')); + } + + // Catch mistakes with reading the wrong column out of the SES + // documentation. See T10728. + if (preg_match('(-smtp)', $host)) { + throw new Exception( + pht( + 'Amazon SES is not configured correctly: the configured SES '. + 'endpoint ("%s") is an SMTP endpoint. Instead, use an API (HTTPS) '. + 'endpoint.', + $host)); + } + if ($accessKey !== null && $secretKey !== null) { $this->setAuth($accessKey, $secretKey); } + $this->__host = $host; } @@ -108,13 +129,6 @@ class SimpleEmailService $rest->setParameter('Action', 'ListVerifiedEmailAddresses'); $rest = $rest->getResponse(); - if($rest->error === false && $rest->code !== 200) { - $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); - } - if($rest->error !== false) { - $this->__triggerError('listVerifiedEmailAddresses', $rest->error); - return false; - } $response = array(); if(!isset($rest->body)) { @@ -148,13 +162,6 @@ class SimpleEmailService $rest->setParameter('EmailAddress', $email); $rest = $rest->getResponse(); - if($rest->error === false && $rest->code !== 200) { - $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); - } - if($rest->error !== false) { - $this->__triggerError('verifyEmailAddress', $rest->error); - return false; - } $response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId; return $response; @@ -172,13 +179,6 @@ class SimpleEmailService $rest->setParameter('EmailAddress', $email); $rest = $rest->getResponse(); - if($rest->error === false && $rest->code !== 200) { - $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); - } - if($rest->error !== false) { - $this->__triggerError('deleteVerifiedEmailAddress', $rest->error); - return false; - } $response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId; return $response; @@ -195,13 +195,6 @@ class SimpleEmailService $rest->setParameter('Action', 'GetSendQuota'); $rest = $rest->getResponse(); - if($rest->error === false && $rest->code !== 200) { - $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); - } - if($rest->error !== false) { - $this->__triggerError('getSendQuota', $rest->error); - return false; - } $response = array(); if(!isset($rest->body)) { @@ -227,13 +220,6 @@ class SimpleEmailService $rest->setParameter('Action', 'GetSendStatistics'); $rest = $rest->getResponse(); - if($rest->error === false && $rest->code !== 200) { - $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); - } - if($rest->error !== false) { - $this->__triggerError('getSendStatistics', $rest->error); - return false; - } $response = array(); if(!isset($rest->body)) { @@ -265,13 +251,6 @@ class SimpleEmailService $rest->setParameter('RawMessage.Data', base64_encode($raw)); $rest = $rest->getResponse(); - if($rest->error === false && $rest->code !== 200) { - $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); - } - if($rest->error !== false) { - $this->__triggerError('sendRawEmail', $rest->error); - return false; - } $response['MessageId'] = (string)$rest->body->SendEmailResult->MessageId; $response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId; @@ -351,13 +330,6 @@ class SimpleEmailService } $rest = $rest->getResponse(); - if($rest->error === false && $rest->code !== 200) { - $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); - } - if($rest->error !== false) { - $this->__triggerError('sendEmail', $rest->error); - return false; - } $response['MessageId'] = (string)$rest->body->SendEmailResult->MessageId; $response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId; @@ -523,14 +495,22 @@ final class SimpleEmailServiceRequest curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); // Execute, grab errors - if (curl_exec($curl)) { - $this->response->code = curl_getinfo($curl, CURLINFO_HTTP_CODE); - } else { - $this->response->error = array( - 'curl' => true, - 'code' => curl_errno($curl), - 'message' => curl_error($curl), - ); + if (!curl_exec($curl)) { + throw new SimpleEmailServiceException( + pht( + 'Encountered an error while making an HTTP request to Amazon SES '. + '(cURL Error #%d): %s', + curl_errno($curl), + curl_error($curl))); + } + + $this->response->code = curl_getinfo($curl, CURLINFO_HTTP_CODE); + if ($this->response->code != 200) { + throw new SimpleEmailServiceException( + pht( + 'Unexpected HTTP status while making request to Amazon SES: '. + 'expected 200, got %s.', + $this->response->code)); } @curl_close($curl); From 86b08514abea4798917f596a1ef85f71687a6556 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 6 Apr 2016 02:52:17 -0700 Subject: [PATCH 59/84] Merge TYPE_PROJECT_COLUMNS and TYPE_COLUMN transactions into a more general TYPE_COLUMNS transaction Summary: Ref T6027. We currently have two different transaction types: - `TYPE_PROJECT_COLUMNS` does most of the work, but has a sort of weird structure and isn't really suitable for API use. - `TYPE_COLUMN` is this weird, junk transaction which mostly just creates the other transaction. Merge them into a single higher-level `TYPE_COLUMNS` transaction which works properly and has a sensible structure and comprehensive error checking. Remaining work here: - I've removed the old rendering logic, but not yet added new logic. I need to migrate the old transaction types and add new rendering logic. - Although the internal representation is now //suitable// for use in the API, it isn't properly exposed yet. Test Plan: - Created tasks into a column. - Ran unit tests. - Moved tasks between columns. - Will perform additional testing in followups. Reviewers: chad Reviewed By: chad Maniphest Tasks: T6027 Differential Revision: https://secure.phabricator.com/D15634 --- .../maniphest/editor/ManiphestEditEngine.php | 12 +- .../editor/ManiphestTransactionEditor.php | 519 +++++++++++------- .../storage/ManiphestTransaction.php | 44 +- .../PhabricatorProjectCoreTestCase.php | 17 +- .../PhabricatorProjectMoveController.php | 24 +- .../constants/PhabricatorTransactions.php | 1 + 6 files changed, 344 insertions(+), 273 deletions(-) diff --git a/src/applications/maniphest/editor/ManiphestEditEngine.php b/src/applications/maniphest/editor/ManiphestEditEngine.php index 273c0c5472..6c7d26ca12 100644 --- a/src/applications/maniphest/editor/ManiphestEditEngine.php +++ b/src/applications/maniphest/editor/ManiphestEditEngine.php @@ -98,11 +98,13 @@ final class ManiphestEditEngine id(new PhabricatorHandlesEditField()) ->setKey('column') ->setLabel(pht('Column')) - ->setDescription(pht('Workboard column to create this task into.')) - ->setConduitDescription(pht('Create into a workboard column.')) - ->setConduitTypeDescription(pht('PHID of workboard column.')) - ->setAliases(array('columnPHID')) - ->setTransactionType(ManiphestTransaction::TYPE_COLUMN) + ->setDescription(pht('Create a task in a workboard column.')) + ->setConduitDescription( + pht('Move a task to one or more workboard columns.')) + ->setConduitTypeDescription( + pht('PHID or PHIDs of workboard columns.')) + ->setAliases(array('columnPHID', 'columns', 'columnPHIDs')) + ->setTransactionType(PhabricatorTransactions::TYPE_COLUMNS) ->setSingleValue(null) ->setIsInvisible(true) ->setIsReorderable(false) diff --git a/src/applications/maniphest/editor/ManiphestTransactionEditor.php b/src/applications/maniphest/editor/ManiphestTransactionEditor.php index b88c5a5923..6c9a7d0089 100644 --- a/src/applications/maniphest/editor/ManiphestTransactionEditor.php +++ b/src/applications/maniphest/editor/ManiphestTransactionEditor.php @@ -3,6 +3,8 @@ final class ManiphestTransactionEditor extends PhabricatorApplicationTransactionEditor { + private $moreValidationErrors = array(); + public function getEditorApplicationClass() { return 'PhabricatorManiphestApplication'; } @@ -22,14 +24,13 @@ final class ManiphestTransactionEditor $types[] = ManiphestTransaction::TYPE_DESCRIPTION; $types[] = ManiphestTransaction::TYPE_OWNER; $types[] = ManiphestTransaction::TYPE_SUBPRIORITY; - $types[] = ManiphestTransaction::TYPE_PROJECT_COLUMN; $types[] = ManiphestTransaction::TYPE_MERGED_INTO; $types[] = ManiphestTransaction::TYPE_MERGED_FROM; $types[] = ManiphestTransaction::TYPE_UNBLOCK; $types[] = ManiphestTransaction::TYPE_PARENT; - $types[] = ManiphestTransaction::TYPE_COLUMN; $types[] = ManiphestTransaction::TYPE_COVER_IMAGE; $types[] = ManiphestTransaction::TYPE_POINTS; + $types[] = PhabricatorTransactions::TYPE_COLUMNS; $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; @@ -63,9 +64,6 @@ final class ManiphestTransactionEditor return $object->getDescription(); case ManiphestTransaction::TYPE_OWNER: return nonempty($object->getOwnerPHID(), null); - case ManiphestTransaction::TYPE_PROJECT_COLUMN: - // These are pre-populated. - return $xaction->getOldValue(); case ManiphestTransaction::TYPE_SUBPRIORITY: return $object->getSubpriority(); case ManiphestTransaction::TYPE_COVER_IMAGE: @@ -80,7 +78,7 @@ final class ManiphestTransactionEditor case ManiphestTransaction::TYPE_MERGED_FROM: return null; case ManiphestTransaction::TYPE_PARENT: - case ManiphestTransaction::TYPE_COLUMN: + case PhabricatorTransactions::TYPE_COLUMNS: return null; } } @@ -98,14 +96,13 @@ final class ManiphestTransactionEditor case ManiphestTransaction::TYPE_TITLE: case ManiphestTransaction::TYPE_DESCRIPTION: case ManiphestTransaction::TYPE_SUBPRIORITY: - case ManiphestTransaction::TYPE_PROJECT_COLUMN: case ManiphestTransaction::TYPE_MERGED_INTO: case ManiphestTransaction::TYPE_MERGED_FROM: case ManiphestTransaction::TYPE_UNBLOCK: case ManiphestTransaction::TYPE_COVER_IMAGE: return $xaction->getNewValue(); case ManiphestTransaction::TYPE_PARENT: - case ManiphestTransaction::TYPE_COLUMN: + case PhabricatorTransactions::TYPE_COLUMNS: return $xaction->getNewValue(); case ManiphestTransaction::TYPE_POINTS: $value = $xaction->getNewValue(); @@ -127,12 +124,8 @@ final class ManiphestTransactionEditor $new = $xaction->getNewValue(); switch ($xaction->getTransactionType()) { - case ManiphestTransaction::TYPE_PROJECT_COLUMN: - $new_column_phids = $new['columnPHIDs']; - $old_column_phids = $old['columnPHIDs']; - sort($new_column_phids); - sort($old_column_phids); - return ($old !== $new); + case PhabricatorTransactions::TYPE_COLUMNS: + return (bool)$new; } return parent::transactionHasEffect($object, $xaction); @@ -175,9 +168,6 @@ final class ManiphestTransactionEditor case ManiphestTransaction::TYPE_SUBPRIORITY: $object->setSubpriority($xaction->getNewValue()); return; - case ManiphestTransaction::TYPE_PROJECT_COLUMN: - // these do external (edge) updates - return; case ManiphestTransaction::TYPE_MERGED_INTO: $object->setStatus(ManiphestTaskStatus::getDuplicateStatus()); return; @@ -212,7 +202,7 @@ final class ManiphestTransactionEditor return; case ManiphestTransaction::TYPE_MERGED_FROM: case ManiphestTransaction::TYPE_PARENT: - case ManiphestTransaction::TYPE_COLUMN: + case PhabricatorTransactions::TYPE_COLUMNS: return; } } @@ -231,125 +221,10 @@ final class ManiphestTransactionEditor ->addEdge($parent_phid, $parent_type, $task_phid) ->save(); break; - case ManiphestTransaction::TYPE_PROJECT_COLUMN: - $board_phid = idx($xaction->getNewValue(), 'projectPHID'); - if (!$board_phid) { - throw new Exception( - pht( - "Expected '%s' in column transaction.", - 'projectPHID')); + case PhabricatorTransactions::TYPE_COLUMNS: + foreach ($xaction->getNewValue() as $move) { + $this->applyBoardMove($object, $move); } - - $old_phids = idx($xaction->getOldValue(), 'columnPHIDs', array()); - $new_phids = idx($xaction->getNewValue(), 'columnPHIDs', array()); - if (count($new_phids) !== 1) { - throw new Exception( - pht( - "Expected exactly one '%s' in column transaction.", - 'columnPHIDs')); - } - - $before_phid = idx($xaction->getNewValue(), 'beforePHID'); - $after_phid = idx($xaction->getNewValue(), 'afterPHID'); - - if (!$before_phid && !$after_phid && ($old_phids == $new_phids)) { - // If we are not moving the object between columns and also not - // reordering the position, this is a move on some other order - // (like priority). We can leave the positions untouched and just - // bail, there's no work to be done. - return; - } - - // Otherwise, we're either moving between columns or adjusting the - // object's position in the "natural" ordering, so we do need to update - // some rows. - - $object_phid = $object->getPHID(); - - // We're doing layout with the ominpotent viewer to make sure we don't - // remove positions in columns that exist, but which the actual actor - // can't see. - $omnipotent_viewer = PhabricatorUser::getOmnipotentUser(); - - $select_phids = array($board_phid); - - $descendants = id(new PhabricatorProjectQuery()) - ->setViewer($omnipotent_viewer) - ->withAncestorProjectPHIDs($select_phids) - ->execute(); - foreach ($descendants as $descendant) { - $select_phids[] = $descendant->getPHID(); - } - - $board_tasks = id(new ManiphestTaskQuery()) - ->setViewer($omnipotent_viewer) - ->withEdgeLogicPHIDs( - PhabricatorProjectObjectHasProjectEdgeType::EDGECONST, - PhabricatorQueryConstraint::OPERATOR_ANCESTOR, - array($select_phids)) - ->execute(); - - $board_tasks = mpull($board_tasks, null, 'getPHID'); - $board_tasks[$object_phid] = $object; - - // Make sure tasks are sorted by ID, so we lay out new positions in - // a consistent way. - $board_tasks = msort($board_tasks, 'getID'); - - $object_phids = array_keys($board_tasks); - - $engine = id(new PhabricatorBoardLayoutEngine()) - ->setViewer($omnipotent_viewer) - ->setBoardPHIDs(array($board_phid)) - ->setObjectPHIDs($object_phids) - ->executeLayout(); - - // TODO: This logic needs to be revised if we legitimately support - // multiple column positions. - - // NOTE: When a task is newly created, it's implicitly added to the - // backlog but we don't currently record that in the "$old_phids". Just - // clean it up for now. - $columns = $engine->getObjectColumns($board_phid, $object_phid); - foreach ($columns as $column) { - $engine->queueRemovePosition( - $board_phid, - $column->getPHID(), - $object_phid); - } - - // Remove all existing column positions on the board. - foreach ($old_phids as $column_phid) { - $engine->queueRemovePosition( - $board_phid, - $column_phid, - $object_phid); - } - - // Add new positions. - foreach ($new_phids as $column_phid) { - if ($before_phid) { - $engine->queueAddPositionBefore( - $board_phid, - $column_phid, - $object_phid, - $before_phid); - } else if ($after_phid) { - $engine->queueAddPositionAfter( - $board_phid, - $column_phid, - $object_phid, - $after_phid); - } else { - $engine->queueAddPosition( - $board_phid, - $column_phid, - $object_phid); - } - } - - $engine->applyPositionUpdates(); - break; default: break; @@ -492,13 +367,12 @@ final class ManiphestTransactionEditor $board_phids = array(); - $type_column = ManiphestTransaction::TYPE_PROJECT_COLUMN; + $type_columns = PhabricatorTransactions::TYPE_COLUMNS; foreach ($xactions as $xaction) { - if ($xaction->getTransactionType() == $type_column) { - $new = $xaction->getNewValue(); - $project_phid = idx($new, 'projectPHID'); - if ($project_phid) { - $board_phids[] = $project_phid; + if ($xaction->getTransactionType() == $type_columns) { + $moves = $xaction->getNewValue(); + foreach ($moves as $move) { + $board_phids[] = $move['boardPHID']; } } } @@ -815,38 +689,6 @@ final class ManiphestTransactionEditor last($with_effect)); } break; - case ManiphestTransaction::TYPE_COLUMN: - $with_effect = array(); - foreach ($xactions as $xaction) { - $column_phid = $xaction->getNewValue(); - if (!$column_phid) { - continue; - } - - $with_effect[] = $xaction; - - $column = $this->loadProjectColumn($column_phid); - if (!$column) { - $errors[] = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Invalid'), - pht( - 'Column PHID "%s" does not identify a visible column.', - $column_phid), - $xaction); - } - } - - if ($with_effect && !$this->getIsNewObject()) { - $errors[] = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Invalid'), - pht( - 'You can only put a task into an initial column during task '. - 'creation.'), - last($with_effect)); - } - break; case ManiphestTransaction::TYPE_OWNER: foreach ($xactions as $xaction) { $old = $xaction->getOldValue(); @@ -938,6 +780,19 @@ final class ManiphestTransactionEditor return $errors; } + protected function validateAllTransactions( + PhabricatorLiskDAO $object, + array $xactions) { + + $errors = parent::validateAllTransactions($object, $xactions); + + if ($this->moreValidationErrors) { + $errors = array_merge($errors, $this->moreValidationErrors); + } + + return $errors; + } + protected function expandTransactions( PhabricatorLiskDAO $object, array $xactions) { @@ -1009,28 +864,36 @@ final class ManiphestTransactionEditor $results = parent::expandTransaction($object, $xaction); - switch ($xaction->getTransactionType()) { - case ManiphestTransaction::TYPE_COLUMN: - $column_phid = $xaction->getNewValue(); - if (!$column_phid) { - break; - } + $type = $xaction->getTransactionType(); + switch ($type) { + case PhabricatorTransactions::TYPE_COLUMNS: + try { + $this->buildMoveTransaction($object, $xaction); - // When a task is created into a column, we also generate a transaction - // to actually put it in that column. - $column = $this->loadProjectColumn($column_phid); - $results[] = id(new ManiphestTransaction()) - ->setTransactionType(ManiphestTransaction::TYPE_PROJECT_COLUMN) - ->setOldValue( - array( - 'projectPHID' => $column->getProjectPHID(), - 'columnPHIDs' => array(), - )) - ->setNewValue( - array( - 'projectPHID' => $column->getProjectPHID(), - 'columnPHIDs' => array($column->getPHID()), - )); + // Implicilty add the task to any boards that we're moving it + // on, since moves on a board the task isn't part of are not + // meaningful. + $board_phids = ipull($xaction->getNewValue(), 'boardPHID'); + if ($board_phids) { + $results[] = id(new ManiphestTransaction()) + ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) + ->setMetadataValue( + 'edge:type', + PhabricatorProjectObjectHasProjectEdgeType::EDGECONST) + ->setIgnoreOnNoEffect(true) + ->setNewValue( + array( + '+' => array_fuse($board_phids), + )); + } + } catch (Exception $ex) { + $error = new PhabricatorApplicationTransactionValidationError( + $type, + pht('Invalid'), + $ex->getMessage(), + $xaction); + $this->moreValidationErrors[] = $error; + } break; case ManiphestTransaction::TYPE_OWNER: // If this is a no-op update, don't expand it. @@ -1057,13 +920,6 @@ final class ManiphestTransactionEditor return $results; } - private function loadProjectColumn($column_phid) { - return id(new PhabricatorProjectColumnQuery()) - ->setViewer($this->getActor()) - ->withPHIDs(array($column_phid)) - ->executeOne(); - } - protected function extractFilePHIDsFromCustomTransaction( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { @@ -1078,5 +934,264 @@ final class ManiphestTransactionEditor return $phids; } + private function buildMoveTransaction( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + $new = $xaction->getNewValue(); + if (!is_array($new)) { + $this->validateColumnPHID($new); + $new = array($new); + } + + $nearby_phids = array(); + foreach ($new as $key => $value) { + if (!is_array($value)) { + $this->validateColumnPHID($value); + $value = array( + 'columnPHID' => $value, + ); + } + + PhutilTypeSpec::checkMap( + $value, + array( + 'columnPHID' => 'string', + 'beforePHID' => 'optional string', + 'afterPHID' => 'optional string', + )); + + $new[$key] = $value; + + if (!empty($value['beforePHID'])) { + $nearby_phids[] = $value['beforePHID']; + } + + if (!empty($value['afterPHID'])) { + $nearby_phids[] = $value['afterPHID']; + } + } + + if ($nearby_phids) { + $nearby_objects = id(new PhabricatorObjectQuery()) + ->setViewer($this->getActor()) + ->withPHIDs($nearby_phids) + ->execute(); + $nearby_objects = mpull($nearby_objects, null, 'getPHID'); + } else { + $nearby_objects = array(); + } + + $column_phids = ipull($new, 'columnPHID'); + if ($column_phids) { + $columns = id(new PhabricatorProjectColumnQuery()) + ->setViewer($this->getActor()) + ->withPHIDs($column_phids) + ->execute(); + $columns = mpull($columns, null, 'getPHID'); + } else { + $columns = array(); + } + + $board_phids = mpull($columns, 'getProjectPHID'); + $object_phid = $object->getPHID(); + + $object_phids = $nearby_phids; + + // Note that we may not have an object PHID if we're creating a new + // object. + if ($object_phid) { + $object_phids[] = $object_phid; + } + + if ($object_phids) { + $layout_engine = id(new PhabricatorBoardLayoutEngine()) + ->setViewer($this->getActor()) + ->setBoardPHIDs($board_phids) + ->setObjectPHIDs($object_phids) + ->setFetchAllBoards(true) + ->executeLayout(); + } + + foreach ($new as $key => $spec) { + $column_phid = $spec['columnPHID']; + $column = idx($columns, $column_phid); + if (!$column) { + throw new Exception( + pht( + 'Column move transaction specifies column PHID "%s", but there '. + 'is no corresponding column with this PHID.', + $column_phid)); + } + + $board_phid = $column->getProjectPHID(); + + $nearby = array(); + + if (!empty($spec['beforePHID'])) { + $nearby['beforePHID'] = $spec['beforePHID']; + } + + if (!empty($spec['afterPHID'])) { + $nearby['afterPHID'] = $spec['afterPHID']; + } + + if (count($nearby) > 1) { + throw new Exception( + pht( + 'Column move transaction moves object to multiple positions. '. + 'Specify only "beforePHID" or "afterPHID", not both.')); + } + + foreach ($nearby as $where => $nearby_phid) { + if (empty($nearby_objects[$nearby_phid])) { + throw new Exception( + pht( + 'Column move transaction specifies object "%s" as "%s", but '. + 'there is no corresponding object with this PHID.', + $object_phid, + $where)); + } + + $nearby_columns = $layout_engine->getObjectColumns( + $board_phid, + $nearby_phid); + $nearby_columns = mpull($nearby_columns, null, 'getPHID'); + + if (empty($nearby_columns[$column_phid])) { + throw new Exception( + pht( + 'Column move transaction specifies object "%s" as "%s" in '. + 'column "%s", but this object is not in that column!', + $nearby_phid, + $where, + $column_phid)); + } + } + + if ($object_phid) { + $old_columns = $layout_engine->getObjectColumns( + $board_phid, + $object_phid); + $old_column_phids = mpull($old_columns, 'getPHID'); + } else { + $old_column_phids = array(); + } + + $spec += array( + 'boardPHID' => $board_phid, + 'fromColumnPHIDs' => $old_column_phids, + ); + + // Check if the object is already in this column, and isn't being moved. + // We can just drop this column change if it has no effect. + $from_map = array_fuse($spec['fromColumnPHIDs']); + $already_here = isset($from_map[$column_phid]); + $is_reordering = (bool)$nearby; + + if ($already_here && !$is_reordering) { + unset($new[$key]); + } else { + $new[$key] = $spec; + } + } + + $new = array_values($new); + $xaction->setNewValue($new); + } + + private function applyBoardMove($object, array $move) { + $board_phid = $move['boardPHID']; + $column_phid = $move['columnPHID']; + $before_phid = idx($move, 'beforePHID'); + $after_phid = idx($move, 'afterPHID'); + + $object_phid = $object->getPHID(); + + // We're doing layout with the ominpotent viewer to make sure we don't + // remove positions in columns that exist, but which the actual actor + // can't see. + $omnipotent_viewer = PhabricatorUser::getOmnipotentUser(); + + $select_phids = array($board_phid); + + $descendants = id(new PhabricatorProjectQuery()) + ->setViewer($omnipotent_viewer) + ->withAncestorProjectPHIDs($select_phids) + ->execute(); + foreach ($descendants as $descendant) { + $select_phids[] = $descendant->getPHID(); + } + + $board_tasks = id(new ManiphestTaskQuery()) + ->setViewer($omnipotent_viewer) + ->withEdgeLogicPHIDs( + PhabricatorProjectObjectHasProjectEdgeType::EDGECONST, + PhabricatorQueryConstraint::OPERATOR_ANCESTOR, + array($select_phids)) + ->execute(); + + $board_tasks = mpull($board_tasks, null, 'getPHID'); + $board_tasks[$object_phid] = $object; + + // Make sure tasks are sorted by ID, so we lay out new positions in + // a consistent way. + $board_tasks = msort($board_tasks, 'getID'); + + $object_phids = array_keys($board_tasks); + + $engine = id(new PhabricatorBoardLayoutEngine()) + ->setViewer($omnipotent_viewer) + ->setBoardPHIDs(array($board_phid)) + ->setObjectPHIDs($object_phids) + ->executeLayout(); + + // TODO: This logic needs to be revised when we legitimately support + // multiple column positions. + $columns = $engine->getObjectColumns($board_phid, $object_phid); + foreach ($columns as $column) { + $engine->queueRemovePosition( + $board_phid, + $column->getPHID(), + $object_phid); + } + + if ($before_phid) { + $engine->queueAddPositionBefore( + $board_phid, + $column_phid, + $object_phid, + $before_phid); + } else if ($after_phid) { + $engine->queueAddPositionAfter( + $board_phid, + $column_phid, + $object_phid, + $after_phid); + } else { + $engine->queueAddPosition( + $board_phid, + $column_phid, + $object_phid); + } + + $engine->applyPositionUpdates(); + } + + + private function validateColumnPHID($value) { + if (phid_get_type($value) == PhabricatorProjectColumnPHIDType::TYPECONST) { + return; + } + + throw new Exception( + pht( + 'When moving objects between columns on a board, columns must '. + 'be identified by PHIDs. This transaction uses "%s" to identify '. + 'a column, but that is not a valid column PHID.', + $value)); + } + + } diff --git a/src/applications/maniphest/storage/ManiphestTransaction.php b/src/applications/maniphest/storage/ManiphestTransaction.php index caf57a3f71..79901d7ae6 100644 --- a/src/applications/maniphest/storage/ManiphestTransaction.php +++ b/src/applications/maniphest/storage/ManiphestTransaction.php @@ -10,12 +10,10 @@ final class ManiphestTransaction const TYPE_PRIORITY = 'priority'; const TYPE_EDGE = 'edge'; const TYPE_SUBPRIORITY = 'subpriority'; - const TYPE_PROJECT_COLUMN = 'projectcolumn'; const TYPE_MERGED_INTO = 'mergedinto'; const TYPE_MERGED_FROM = 'mergedfrom'; const TYPE_UNBLOCK = 'unblock'; const TYPE_PARENT = 'parent'; - const TYPE_COLUMN = 'column'; const TYPE_COVER_IMAGE = 'cover-image'; const TYPE_POINTS = 'points'; @@ -48,7 +46,6 @@ final class ManiphestTransaction public function shouldGenerateOldValue() { switch ($this->getTransactionType()) { - case self::TYPE_PROJECT_COLUMN: case self::TYPE_EDGE: case self::TYPE_UNBLOCK: return false; @@ -85,10 +82,6 @@ final class ManiphestTransaction $phids[] = $old; } break; - case self::TYPE_PROJECT_COLUMN: - $phids[] = $new['projectPHID']; - $phids[] = head($new['columnPHIDs']); - break; case self::TYPE_MERGED_INTO: $phids[] = $new; break; @@ -152,18 +145,7 @@ final class ManiphestTransaction break; case self::TYPE_SUBPRIORITY: case self::TYPE_PARENT: - case self::TYPE_COLUMN: return true; - case self::TYPE_PROJECT_COLUMN: - $old_cols = idx($this->getOldValue(), 'columnPHIDs'); - $new_cols = idx($this->getNewValue(), 'columnPHIDs'); - - $old_cols = array_values($old_cols); - $new_cols = array_values($new_cols); - sort($old_cols); - sort($new_cols); - - return ($old_cols === $new_cols); case self::TYPE_COVER_IMAGE: // At least for now, don't show these. return true; @@ -308,7 +290,7 @@ final class ManiphestTransaction return pht('Reassigned'); } - case self::TYPE_PROJECT_COLUMN: + case PhabricatorTransactions::TYPE_COLUMNS: return pht('Changed Project Column'); case self::TYPE_PRIORITY: @@ -378,7 +360,7 @@ final class ManiphestTransaction case self::TYPE_DESCRIPTION: return 'fa-pencil'; - case self::TYPE_PROJECT_COLUMN: + case PhabricatorTransactions::TYPE_COLUMNS: return 'fa-columns'; case self::TYPE_MERGED_INTO: @@ -616,16 +598,6 @@ final class ManiphestTransaction $this->renderHandleList($removed)); } - case self::TYPE_PROJECT_COLUMN: - $project_phid = $new['projectPHID']; - $column_phid = head($new['columnPHIDs']); - return pht( - '%s moved this task to %s on the %s workboard.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($column_phid), - $this->renderHandleLink($project_phid)); - break; - case self::TYPE_MERGED_INTO: return pht( '%s closed this task as a duplicate of %s.', @@ -887,16 +859,6 @@ final class ManiphestTransaction $this->renderHandleList($removed)); } - case self::TYPE_PROJECT_COLUMN: - $project_phid = $new['projectPHID']; - $column_phid = head($new['columnPHIDs']); - return pht( - '%s moved %s to %s on the %s workboard.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid), - $this->renderHandleLink($column_phid), - $this->renderHandleLink($project_phid)); - case self::TYPE_MERGED_INTO: return pht( '%s merged task %s into %s.', @@ -961,7 +923,7 @@ final class ManiphestTransaction case self::TYPE_UNBLOCK: $tags[] = self::MAILTAG_UNBLOCK; break; - case self::TYPE_PROJECT_COLUMN: + case PhabricatorTransactions::TYPE_COLUMNS: $tags[] = self::MAILTAG_COLUMN; break; case PhabricatorTransactions::TYPE_COMMENT: diff --git a/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php b/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php index e2b2f9688d..5ead896035 100644 --- a/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php +++ b/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php @@ -1072,18 +1072,13 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase { $options = array(); } + $value = array( + 'columnPHID' => $dst->getPHID(), + ) + $options; + $xactions[] = id(new ManiphestTransaction()) - ->setTransactionType(ManiphestTransaction::TYPE_PROJECT_COLUMN) - ->setOldValue( - array( - 'projectPHID' => $board->getPHID(), - 'columnPHIDs' => array($src->getPHID()), - )) - ->setNewValue( - array( - 'projectPHID' => $board->getPHID(), - 'columnPHIDs' => array($dst->getPHID()), - ) + $options); + ->setTransactionType(PhabricatorTransactions::TYPE_COLUMNS) + ->setNewValue(array($value)); $editor = id(new ManiphestTransactionEditor()) ->setActor($viewer) diff --git a/src/applications/project/controller/PhabricatorProjectMoveController.php b/src/applications/project/controller/PhabricatorProjectMoveController.php index d3540a1781..e529fab61e 100644 --- a/src/applications/project/controller/PhabricatorProjectMoveController.php +++ b/src/applications/project/controller/PhabricatorProjectMoveController.php @@ -68,26 +68,22 @@ final class PhabricatorProjectMoveController $xactions = array(); + $order_params = array(); if ($order == PhabricatorProjectColumn::ORDER_NATURAL) { - $order_params = array( - 'afterPHID' => $after_phid, - 'beforePHID' => $before_phid, - ); - } else { - $order_params = array(); + if ($after_phid) { + $order_params['afterPHID'] = $after_phid; + } else if ($before_phid) { + $order_params['beforePHID'] = $before_phid; + } } $xactions[] = id(new ManiphestTransaction()) - ->setTransactionType(ManiphestTransaction::TYPE_PROJECT_COLUMN) + ->setTransactionType(PhabricatorTransactions::TYPE_COLUMNS) ->setNewValue( array( - 'columnPHIDs' => array($column->getPHID()), - 'projectPHID' => $column->getProjectPHID(), - ) + $order_params) - ->setOldValue( - array( - 'columnPHIDs' => $old_column_phids, - 'projectPHID' => $column->getProjectPHID(), + array( + 'columnPHID' => $column->getPHID(), + ) + $order_params, )); if ($order == PhabricatorProjectColumn::ORDER_PRIORITY) { diff --git a/src/applications/transactions/constants/PhabricatorTransactions.php b/src/applications/transactions/constants/PhabricatorTransactions.php index 172f80ba31..7d8e8afb1a 100644 --- a/src/applications/transactions/constants/PhabricatorTransactions.php +++ b/src/applications/transactions/constants/PhabricatorTransactions.php @@ -14,6 +14,7 @@ final class PhabricatorTransactions extends Phobject { const TYPE_INLINESTATE = 'core:inlinestate'; const TYPE_SPACE = 'core:space'; const TYPE_CREATE = 'core:create'; + const TYPE_COLUMNS = 'core:columns'; const COLOR_RED = 'red'; const COLOR_ORANGE = 'orange'; From 222cf6862bfd4779485d6829c19c4d70bd9d5364 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 6 Apr 2016 03:33:06 -0700 Subject: [PATCH 60/84] Render new more-general move transactions in a human-readable way Summary: Ref T6027. This adds human-readable rendering for the new `TYPE_COLUMNS` core transactions. Test Plan: {F1207784} Reviewers: chad Reviewed By: chad Maniphest Tasks: T6027 Differential Revision: https://secure.phabricator.com/D15635 --- .../PhabricatorApplicationTransaction.php | 108 +++++++++++++++++- 1 file changed, 107 insertions(+), 1 deletion(-) diff --git a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php index ee8a7a47a3..071b94a933 100644 --- a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php +++ b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php @@ -256,6 +256,15 @@ abstract class PhabricatorApplicationTransaction $phids[] = ipull($old, 'dst'); $phids[] = ipull($new, 'dst'); break; + case PhabricatorTransactions::TYPE_COLUMNS: + foreach ($new as $move) { + $phids[] = array( + $move['columnPHID'], + $move['boardPHID'], + ); + $phids[] = $move['fromColumnPHIDs']; + } + break; case PhabricatorTransactions::TYPE_EDIT_POLICY: case PhabricatorTransactions::TYPE_VIEW_POLICY: case PhabricatorTransactions::TYPE_JOIN_POLICY: @@ -407,6 +416,8 @@ abstract class PhabricatorApplicationTransaction return 'fa-trophy'; case PhabricatorTransactions::TYPE_SPACE: return 'fa-th-large'; + case PhabricatorTransactions::TYPE_COLUMNS: + return 'fa-columns'; } return 'fa-pencil'; @@ -493,6 +504,7 @@ abstract class PhabricatorApplicationTransaction case PhabricatorTransactions::TYPE_EDIT_POLICY: case PhabricatorTransactions::TYPE_JOIN_POLICY: case PhabricatorTransactions::TYPE_SPACE: + case PhabricatorTransactions::TYPE_COLUMNS: break; default: $old = $this->getOldValue(); @@ -501,9 +513,10 @@ abstract class PhabricatorApplicationTransaction return true; } - if (!strlen($old)) { + if (!is_array($old) && !strlen($old)) { return true; } + break; } } @@ -549,6 +562,8 @@ abstract class PhabricatorApplicationTransaction if ($field) { return $field->shouldHideInApplicationTransactions($this); } + case PhabricatorTransactions::TYPE_COLUMNS: + return !$this->getInterestingMoves($this->getNewValue()); case PhabricatorTransactions::TYPE_EDGE: $edge_type = $this->getMetadataValue('edge:type'); switch ($edge_type) { @@ -930,6 +945,44 @@ abstract class PhabricatorApplicationTransaction } break; + case PhabricatorTransactions::TYPE_COLUMNS: + $moves = $this->getInterestingMoves($new); + if (count($moves) == 1) { + $move = head($moves); + $from_columns = $move['fromColumnPHIDs']; + $to_column = $move['columnPHID']; + $board_phid = $move['boardPHID']; + if (count($from_columns) == 1) { + return pht( + '%s moved this task from %s to %s on the %s board.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink(head($from_columns)), + $this->renderHandleLink($to_column), + $this->renderHandleLink($board_phid)); + } else { + return pht( + '%s moved this task to %s on the %s board.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($to_column), + $this->renderHandleLink($board_phid)); + } + } else { + $fragments = array(); + foreach ($moves as $move) { + $fragments[] = pht( + '%s (%s)', + $this->renderHandleLink($board_phid), + $this->renderHandleLink($to_column)); + } + + return pht( + '%s moved this task on %s board(s): %s.', + $this->renderHandleLink($author_phid), + phutil_count($moves), + phutil_implode_html(', ', $fragments)); + } + break; + default: return pht( '%s edited this %s.', @@ -1058,6 +1111,47 @@ abstract class PhabricatorApplicationTransaction return null; } + case PhabricatorTransactions::TYPE_COLUMNS: + $moves = $this->getInterestingMoves($new); + if (count($moves) == 1) { + $move = head($moves); + $from_columns = $move['fromColumnPHIDs']; + $to_column = $move['columnPHID']; + $board_phid = $move['boardPHID']; + if (count($from_columns) == 1) { + return pht( + '%s moved %s from %s to %s on the %s board.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($object_phid), + $this->renderHandleLink(head($from_columns)), + $this->renderHandleLink($to_column), + $this->renderHandleLink($board_phid)); + } else { + return pht( + '%s moved %s to %s on the %s board.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($object_phid), + $this->renderHandleLink($to_column), + $this->renderHandleLink($board_phid)); + } + } else { + $fragments = array(); + foreach ($moves as $move) { + $fragments[] = pht( + '%s (%s)', + $this->renderHandleLink($board_phid), + $this->renderHandleLink($to_column)); + } + + return pht( + '%s moved %s on %s board(s): %s.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($object_phid), + phutil_count($moves), + phutil_implode_html(', ', $fragments)); + } + break; + } return $this->getTitle(); @@ -1364,6 +1458,18 @@ abstract class PhabricatorApplicationTransaction return true; } + private function getInterestingMoves(array $moves) { + // Remove moves which only shift the position of a task within a column. + foreach ($moves as $key => $move) { + $from_phids = array_fuse($move['fromColumnPHIDs']); + if (isset($from_phids[$move['columnPHID']])) { + unset($moves[$key]); + } + } + + return $moves; + } + /* -( PhabricatorPolicyInterface Implementation )-------------------------- */ From ecd4dd4e0b08f5b8934fd2cde426e7d1059681c6 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 6 Apr 2016 04:37:01 -0700 Subject: [PATCH 61/84] Expose column positions via maniphest.edit Summary: Ref T5214. Fixes T10486. Ref T6027. This exposes the `TYPE_COLUMNS` transaction in a usable way via API, and fixes the interactions via prefilling. Test Plan: - Created tasks directly into columns via API. - Moved tasks between columns via API. - Used `?column=...` to try to create a template task with valid and bogus column PHIDs. Reviewers: chad Reviewed By: chad Subscribers: AmyLewis Maniphest Tasks: T5214, T6027, T10486 Differential Revision: https://secure.phabricator.com/D15636 --- src/__phutil_library_map__.php | 4 ++ .../ConduitColumnsParameterType.php | 38 ++++++++++++ .../maniphest/editor/ManiphestEditEngine.php | 58 +++++++++++++++++-- .../editfield/PhabricatorColumnsEditField.php | 21 +++++++ .../PhabricatorApplicationTransaction.php | 1 - 5 files changed, 117 insertions(+), 5 deletions(-) create mode 100644 src/applications/conduit/parametertype/ConduitColumnsParameterType.php create mode 100644 src/applications/transactions/editfield/PhabricatorColumnsEditField.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 13e4b98341..b1e36442c3 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -253,6 +253,7 @@ phutil_register_library_map(array( 'ConduitBoolParameterType' => 'applications/conduit/parametertype/ConduitBoolParameterType.php', 'ConduitCall' => 'applications/conduit/call/ConduitCall.php', 'ConduitCallTestCase' => 'applications/conduit/call/__tests__/ConduitCallTestCase.php', + 'ConduitColumnsParameterType' => 'applications/conduit/parametertype/ConduitColumnsParameterType.php', 'ConduitConnectConduitAPIMethod' => 'applications/conduit/method/ConduitConnectConduitAPIMethod.php', 'ConduitEpochParameterType' => 'applications/conduit/parametertype/ConduitEpochParameterType.php', 'ConduitException' => 'applications/conduit/protocol/exception/ConduitException.php', @@ -1984,6 +1985,7 @@ phutil_register_library_map(array( 'PhabricatorChunkedFileStorageEngine' => 'applications/files/engine/PhabricatorChunkedFileStorageEngine.php', 'PhabricatorClusterConfigOptions' => 'applications/config/option/PhabricatorClusterConfigOptions.php', 'PhabricatorColumnProxyInterface' => 'applications/project/interface/PhabricatorColumnProxyInterface.php', + 'PhabricatorColumnsEditField' => 'applications/transactions/editfield/PhabricatorColumnsEditField.php', 'PhabricatorCommentEditEngineExtension' => 'applications/transactions/engineextension/PhabricatorCommentEditEngineExtension.php', 'PhabricatorCommentEditField' => 'applications/transactions/editfield/PhabricatorCommentEditField.php', 'PhabricatorCommentEditType' => 'applications/transactions/edittype/PhabricatorCommentEditType.php', @@ -4380,6 +4382,7 @@ phutil_register_library_map(array( 'ConduitBoolParameterType' => 'ConduitListParameterType', 'ConduitCall' => 'Phobject', 'ConduitCallTestCase' => 'PhabricatorTestCase', + 'ConduitColumnsParameterType' => 'ConduitParameterType', 'ConduitConnectConduitAPIMethod' => 'ConduitAPIMethod', 'ConduitEpochParameterType' => 'ConduitListParameterType', 'ConduitException' => 'Exception', @@ -6383,6 +6386,7 @@ phutil_register_library_map(array( 'PhabricatorChatLogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorChunkedFileStorageEngine' => 'PhabricatorFileStorageEngine', 'PhabricatorClusterConfigOptions' => 'PhabricatorApplicationConfigOptions', + 'PhabricatorColumnsEditField' => 'PhabricatorPHIDListEditField', 'PhabricatorCommentEditEngineExtension' => 'PhabricatorEditEngineExtension', 'PhabricatorCommentEditField' => 'PhabricatorEditField', 'PhabricatorCommentEditType' => 'PhabricatorEditType', diff --git a/src/applications/conduit/parametertype/ConduitColumnsParameterType.php b/src/applications/conduit/parametertype/ConduitColumnsParameterType.php new file mode 100644 index 0000000000..c6669fae06 --- /dev/null +++ b/src/applications/conduit/parametertype/ConduitColumnsParameterType.php @@ -0,0 +1,38 @@ +getViewer()->getPHID()); } + $column_documentation = pht(<<setKey('parent') @@ -95,18 +146,17 @@ final class ManiphestEditEngine ->setIsReorderable(false) ->setIsDefaultable(false) ->setIsLockable(false), - id(new PhabricatorHandlesEditField()) + id(new PhabricatorColumnsEditField()) ->setKey('column') ->setLabel(pht('Column')) ->setDescription(pht('Create a task in a workboard column.')) ->setConduitDescription( pht('Move a task to one or more workboard columns.')) ->setConduitTypeDescription( - pht('PHID or PHIDs of workboard columns.')) + pht('List of columns to move the task to.')) + ->setConduitDocumentation($column_documentation) ->setAliases(array('columnPHID', 'columns', 'columnPHIDs')) ->setTransactionType(PhabricatorTransactions::TYPE_COLUMNS) - ->setSingleValue(null) - ->setIsInvisible(true) ->setIsReorderable(false) ->setIsDefaultable(false) ->setIsLockable(false), diff --git a/src/applications/transactions/editfield/PhabricatorColumnsEditField.php b/src/applications/transactions/editfield/PhabricatorColumnsEditField.php new file mode 100644 index 0000000000..79fb25b86a --- /dev/null +++ b/src/applications/transactions/editfield/PhabricatorColumnsEditField.php @@ -0,0 +1,21 @@ +setIsInvisible(true); + + return $control; + } + + protected function newHTTPParameterType() { + return new AphrontPHIDListHTTPParameterType(); + } + + protected function newConduitParameterType() { + return new ConduitColumnsParameterType(); + } + +} diff --git a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php index 071b94a933..0e533cfa40 100644 --- a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php +++ b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php @@ -504,7 +504,6 @@ abstract class PhabricatorApplicationTransaction case PhabricatorTransactions::TYPE_EDIT_POLICY: case PhabricatorTransactions::TYPE_JOIN_POLICY: case PhabricatorTransactions::TYPE_SPACE: - case PhabricatorTransactions::TYPE_COLUMNS: break; default: $old = $this->getOldValue(); From 8bca296ac1412380d12b8f0f4d8b4131a8792e3b Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 6 Apr 2016 05:13:28 -0700 Subject: [PATCH 62/84] Migrate old task transactions to use new display code Summary: Ref T6027. This converts the old transaction records to the new format so we don't have to keep legacy code around. Test Plan: Migrated tasks, browsed around, looked at transaction records, didn't see any issues. Reviewers: chad Reviewed By: chad Maniphest Tasks: T6027 Differential Revision: https://secure.phabricator.com/D15637 --- .../sql/autopatches/20160406.columns.1.php | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 resources/sql/autopatches/20160406.columns.1.php diff --git a/resources/sql/autopatches/20160406.columns.1.php b/resources/sql/autopatches/20160406.columns.1.php new file mode 100644 index 0000000000..8be75ebbed --- /dev/null +++ b/resources/sql/autopatches/20160406.columns.1.php @@ -0,0 +1,84 @@ +establishConnection('w'); + +foreach (new LiskMigrationIterator($table) as $xaction) { + $type = $xaction->getTransactionType(); + $id = $xaction->getID(); + + // This is an old ManiphestTransaction::TYPE_COLUMN. It did not do anything + // on its own and was hidden from the UI, so we're just going to remove it. + if ($type == 'column') { + queryfx( + $conn_w, + 'DELETE FROM %T WHERE id = %d', + $table->getTableName(), + $id); + continue; + } + + // This is an old ManiphestTransaction::TYPE_PROJECT_COLUMN. It moved + // tasks between board columns; we're going to replace it with a modern + // PhabricatorTransactions::TYPE_COLUMNS transaction. + if ($type == 'projectcolumn') { + try { + $new = $xaction->getNewValue(); + if (!$new || !is_array($new)) { + continue; + } + + $column_phids = idx($new, 'columnPHIDs'); + if (!is_array($column_phids) || !$column_phids) { + continue; + } + + $column_phid = head($column_phids); + if (!$column_phid) { + continue; + } + + $board_phid = idx($new, 'projectPHID'); + if (!$board_phid) { + continue; + } + + $before_phid = idx($new, 'beforePHID'); + $after_phid = idx($new, 'afterPHID'); + + $old = $xaction->getOldValue(); + if ($old && is_array($old)) { + $from_phids = idx($old, 'columnPHIDs'); + $from_phids = array_values($from_phids); + } else { + $from_phids = array(); + } + + $replacement = array( + 'columnPHID' => $column_phid, + 'boardPHID' => $board_phid, + 'fromColumnPHIDs' => $from_phids, + ); + + if ($before_phid) { + $replacement['beforePHID'] = $before_phid; + } else if ($after_phid) { + $replacement['afterPHID'] = $after_phid; + } + + queryfx( + $conn_w, + 'UPDATE %T SET transactionType = %s, oldValue = %s, newValue = %s + WHERE id = %d', + $table->getTableName(), + PhabricatorTransactions::TYPE_COLUMNS, + 'null', + phutil_json_encode(array($replacement)), + $id); + } catch (Exception $ex) { + // If anything went awry, just move on. + } + } + + +} From 67629aab14b3c7b027bcc56f4e8b81d069541b43 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 6 Apr 2016 05:59:09 -0700 Subject: [PATCH 63/84] Implement a rough optgroup-based "Move on Workboard" stacked action Summary: Ref T6027. Try this out and see how it feels? Clear issues: - This definitely shouldn't be at the top. - You should probably be able to select it multiple times? - Some of the "which columns show up" rules might need adjustment? - Diamond marker maybe not great? Not sure I love this but it doesn't feel //terrible//... Test Plan: {F1207891} Reviewers: chad Reviewed By: chad Maniphest Tasks: T6027 Differential Revision: https://secure.phabricator.com/D15638 --- resources/celerity/map.php | 12 +-- src/__phutil_library_map__.php | 2 + .../maniphest/editor/ManiphestEditEngine.php | 81 ++++++++++++++++++- ...bricatorEditEngineColumnsCommentAction.php | 27 +++++++ .../editfield/PhabricatorColumnsEditField.php | 21 +++++ webroot/rsrc/js/phuix/PHUIXFormControl.js | 35 ++++++++ 6 files changed, 171 insertions(+), 7 deletions(-) create mode 100644 src/applications/transactions/commentaction/PhabricatorEditEngineColumnsCommentAction.php diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 64455ce942..1c4d121db1 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -515,7 +515,7 @@ return array( 'rsrc/js/phuix/PHUIXActionView.js' => '8cf6d262', 'rsrc/js/phuix/PHUIXAutocomplete.js' => '9196fb06', 'rsrc/js/phuix/PHUIXDropdownMenu.js' => 'bd4c8dca', - 'rsrc/js/phuix/PHUIXFormControl.js' => 'a7763e11', + 'rsrc/js/phuix/PHUIXFormControl.js' => 'e15869a8', 'rsrc/js/phuix/PHUIXIconView.js' => 'bff6884b', ), 'symbols' => array( @@ -855,7 +855,7 @@ return array( 'phuix-action-view' => '8cf6d262', 'phuix-autocomplete' => '9196fb06', 'phuix-dropdown-menu' => 'bd4c8dca', - 'phuix-form-control-view' => 'a7763e11', + 'phuix-form-control-view' => 'e15869a8', 'phuix-icon-view' => 'bff6884b', 'policy-css' => '957ea14c', 'policy-edit-css' => '815c66f7', @@ -1659,10 +1659,6 @@ return array( 'javelin-uri', 'phabricator-notification', ), - 'a7763e11' => array( - 'javelin-install', - 'javelin-dom', - ), 'a80d0378' => array( 'javelin-behavior', 'javelin-stratcom', @@ -1964,6 +1960,10 @@ return array( 'javelin-dom', 'phabricator-prefab', ), + 'e15869a8' => array( + 'javelin-install', + 'javelin-dom', + ), 'e1d25dfb' => array( 'javelin-behavior', 'javelin-stratcom', diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index b1e36442c3..671a8e8e26 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2278,6 +2278,7 @@ phutil_register_library_map(array( 'PhabricatorEdgesDestructionEngineExtension' => 'infrastructure/edges/engineextension/PhabricatorEdgesDestructionEngineExtension.php', 'PhabricatorEditEngine' => 'applications/transactions/editengine/PhabricatorEditEngine.php', 'PhabricatorEditEngineAPIMethod' => 'applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php', + 'PhabricatorEditEngineColumnsCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineColumnsCommentAction.php', 'PhabricatorEditEngineCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineCommentAction.php', 'PhabricatorEditEngineConfiguration' => 'applications/transactions/storage/PhabricatorEditEngineConfiguration.php', 'PhabricatorEditEngineConfigurationDefaultCreateController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationDefaultCreateController.php', @@ -6723,6 +6724,7 @@ phutil_register_library_map(array( 'PhabricatorPolicyInterface', ), 'PhabricatorEditEngineAPIMethod' => 'ConduitAPIMethod', + 'PhabricatorEditEngineColumnsCommentAction' => 'PhabricatorEditEngineCommentAction', 'PhabricatorEditEngineCommentAction' => 'Phobject', 'PhabricatorEditEngineConfiguration' => array( 'PhabricatorSearchDAO', diff --git a/src/applications/maniphest/editor/ManiphestEditEngine.php b/src/applications/maniphest/editor/ManiphestEditEngine.php index 42822868f3..d49851cdfd 100644 --- a/src/applications/maniphest/editor/ManiphestEditEngine.php +++ b/src/applications/maniphest/editor/ManiphestEditEngine.php @@ -132,6 +132,8 @@ priority-sorted boards. EODOCS ); + $column_map = $this->getColumnMap($object); + $fields = array( id(new PhabricatorHandlesEditField()) ->setKey('parent') @@ -159,7 +161,9 @@ EODOCS ->setTransactionType(PhabricatorTransactions::TYPE_COLUMNS) ->setIsReorderable(false) ->setIsDefaultable(false) - ->setIsLockable(false), + ->setIsLockable(false) + ->setCommentActionLabel(pht('Move on Workboard')) + ->setColumnMap($column_map), id(new PhabricatorTextEditField()) ->setKey('title') ->setLabel(pht('Title')) @@ -370,5 +374,80 @@ EODOCS ->buildResponse(); } + private function getColumnMap(ManiphestTask $task) { + $phid = $task->getPHID(); + if (!$phid) { + return array(); + } + + $board_phids = PhabricatorEdgeQuery::loadDestinationPHIDs( + $phid, + PhabricatorProjectObjectHasProjectEdgeType::EDGECONST); + if (!$board_phids) { + return array(); + } + + $viewer = $this->getViewer(); + + $layout_engine = id(new PhabricatorBoardLayoutEngine()) + ->setViewer($viewer) + ->setBoardPHIDs($board_phids) + ->setObjectPHIDs(array($task->getPHID())) + ->setFetchAllBoards(true) + ->executeLayout(); + + $map = array(); + foreach ($board_phids as $board_phid) { + $in_columns = $layout_engine->getObjectColumns($board_phid, $phid); + $in_columns = mpull($in_columns, null, 'getPHID'); + + $all_columns = $layout_engine->getColumns($board_phid); + $options = array(); + foreach ($all_columns as $column) { + $name = $column->getDisplayName(); + + $is_hidden = $column->isHidden(); + $is_selected = isset($in_columns[$column->getPHID()]); + + // Don't show hidden, subproject or milestone columns in this map + // unless the object is currently in the column. + $skip_column = ($is_hidden || $column->getProxyPHID()); + if ($skip_column) { + if (!$is_selected) { + continue; + } + } + + if ($is_hidden) { + $name = pht('(%s)', $name); + } + + if ($is_selected) { + $name = pht("\xE2\x97\x8F %s", $name); + } else { + $name = pht("\xE2\x97\x8B %s", $name); + } + + $option = array( + 'key' => $column->getPHID(), + 'label' => $name, + 'selected' => (bool)$is_selected, + ); + + $options[] = $option; + } + + $map[] = array( + 'label' => head($all_columns)->getProject()->getDisplayName(), + 'options' => $options, + ); + } + + $map = isort($map, 'label'); + $map = array_values($map); + + return $map; + } + } diff --git a/src/applications/transactions/commentaction/PhabricatorEditEngineColumnsCommentAction.php b/src/applications/transactions/commentaction/PhabricatorEditEngineColumnsCommentAction.php new file mode 100644 index 0000000000..bc68b7b642 --- /dev/null +++ b/src/applications/transactions/commentaction/PhabricatorEditEngineColumnsCommentAction.php @@ -0,0 +1,27 @@ +columnMap = $column_map; + return $this; + } + + public function getColumnMap() { + return $this->columnMap; + } + + public function getPHUIXControlType() { + return 'optgroups'; + } + + public function getPHUIXControlSpecification() { + return array( + 'groups' => $this->getColumnMap(), + ); + } + +} diff --git a/src/applications/transactions/editfield/PhabricatorColumnsEditField.php b/src/applications/transactions/editfield/PhabricatorColumnsEditField.php index 79fb25b86a..fef51e2864 100644 --- a/src/applications/transactions/editfield/PhabricatorColumnsEditField.php +++ b/src/applications/transactions/editfield/PhabricatorColumnsEditField.php @@ -3,6 +3,17 @@ final class PhabricatorColumnsEditField extends PhabricatorPHIDListEditField { + private $columnMap; + + public function setColumnMap(array $column_map) { + $this->columnMap = $column_map; + return $this; + } + + public function getColumnMap() { + return $this->columnMap; + } + protected function newControl() { $control = id(new AphrontFormHandlesControl()); $control->setIsInvisible(true); @@ -18,4 +29,14 @@ final class PhabricatorColumnsEditField return new ConduitColumnsParameterType(); } + protected function newCommentAction() { + $column_map = $this->getColumnMap(); + if (!$column_map) { + return null; + } + + return id(new PhabricatorEditEngineColumnsCommentAction()) + ->setColumnMap($this->getColumnMap()); + } + } diff --git a/webroot/rsrc/js/phuix/PHUIXFormControl.js b/webroot/rsrc/js/phuix/PHUIXFormControl.js index cd004b0e8f..5a53bafd7b 100644 --- a/webroot/rsrc/js/phuix/PHUIXFormControl.js +++ b/webroot/rsrc/js/phuix/PHUIXFormControl.js @@ -38,6 +38,9 @@ JX.install('PHUIXFormControl', { case 'points': input = this._newPoints(spec); break; + case 'optgroups': + input = this._newOptgroups(spec); + break; default: // TODO: Default or better error? JX.$E('Bad Input Type'); @@ -171,6 +174,38 @@ JX.install('PHUIXFormControl', { var node = JX.$N('input', attrs); + return { + node: node, + get: function() { + return node.value; + }, + set: function(value) { + node.value = value; + } + }; + }, + + _newOptgroups: function(spec) { + var value = spec.value || null; + + var optgroups = []; + for (var ii = 0; ii < spec.groups.length; ii++) { + var group = spec.groups[ii]; + var options = []; + for (var jj = 0; jj < group.options.length; jj++) { + var option = group.options[jj]; + options.push(JX.$N('option', {value: option.key}, option.label)); + + if (option.selected && (value === null)) { + value = option.key; + } + } + optgroups.push(JX.$N('optgroup', {label: group.label}, options)); + } + + var node = JX.$N('select', {}, optgroups); + node.value = value; + return { node: node, get: function() { From 4d32c990ab3e5dcecc34a06b7f87aef1eb16603e Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 6 Apr 2016 06:22:44 -0700 Subject: [PATCH 64/84] Allow stacked comment actions to be explicitly ordered Summary: Ref T6027. Normally, actions use the same order as the form, but in some cases (like moving stuff on workboards) it makes sense to reorder them explicitly. Pin "Move on board" near the bottom, and "projects/subscribers" at the bottom. I think these are generally reasonable rules in all cases. Test Plan: Opened menu, saw slightly better action order. Reviewers: chad Reviewed By: chad Maniphest Tasks: T6027 Differential Revision: https://secure.phabricator.com/D15639 --- .../maniphest/editor/ManiphestEditEngine.php | 1 + .../PhabricatorProjectsEditEngineExtension.php | 1 + ...habricatorSubscriptionsEditEngineExtension.php | 1 + .../PhabricatorEditEngineCommentAction.php | 15 +++++++++++++++ .../editengine/PhabricatorEditEngine.php | 2 ++ .../editfield/PhabricatorEditField.php | 13 ++++++++++++- 6 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/applications/maniphest/editor/ManiphestEditEngine.php b/src/applications/maniphest/editor/ManiphestEditEngine.php index d49851cdfd..58606538ca 100644 --- a/src/applications/maniphest/editor/ManiphestEditEngine.php +++ b/src/applications/maniphest/editor/ManiphestEditEngine.php @@ -163,6 +163,7 @@ EODOCS ->setIsDefaultable(false) ->setIsLockable(false) ->setCommentActionLabel(pht('Move on Workboard')) + ->setCommentActionOrder(2000) ->setColumnMap($column_map), id(new PhabricatorTextEditField()) ->setKey('title') diff --git a/src/applications/project/engineextension/PhabricatorProjectsEditEngineExtension.php b/src/applications/project/engineextension/PhabricatorProjectsEditEngineExtension.php index cd481946b2..ce9fbf332c 100644 --- a/src/applications/project/engineextension/PhabricatorProjectsEditEngineExtension.php +++ b/src/applications/project/engineextension/PhabricatorProjectsEditEngineExtension.php @@ -50,6 +50,7 @@ final class PhabricatorProjectsEditEngineExtension ->setIsCopyable(true) ->setUseEdgeTransactions(true) ->setCommentActionLabel(pht('Change Project Tags')) + ->setCommentActionOrder(8000) ->setDescription(pht('Select project tags for the object.')) ->setTransactionType($edge_type) ->setMetadataValue('edge:type', $project_edge_type) diff --git a/src/applications/subscriptions/engineextension/PhabricatorSubscriptionsEditEngineExtension.php b/src/applications/subscriptions/engineextension/PhabricatorSubscriptionsEditEngineExtension.php index 19b9281d02..a0a6cbf7be 100644 --- a/src/applications/subscriptions/engineextension/PhabricatorSubscriptionsEditEngineExtension.php +++ b/src/applications/subscriptions/engineextension/PhabricatorSubscriptionsEditEngineExtension.php @@ -45,6 +45,7 @@ final class PhabricatorSubscriptionsEditEngineExtension ->setIsCopyable(true) ->setUseEdgeTransactions(true) ->setCommentActionLabel(pht('Change Subscribers')) + ->setCommentActionOrder(9000) ->setDescription(pht('Choose subscribers.')) ->setTransactionType($subscribers_type) ->setValue($sub_phids); diff --git a/src/applications/transactions/commentaction/PhabricatorEditEngineCommentAction.php b/src/applications/transactions/commentaction/PhabricatorEditEngineCommentAction.php index 548039ce15..dc676630ba 100644 --- a/src/applications/transactions/commentaction/PhabricatorEditEngineCommentAction.php +++ b/src/applications/transactions/commentaction/PhabricatorEditEngineCommentAction.php @@ -6,6 +6,7 @@ abstract class PhabricatorEditEngineCommentAction extends Phobject { private $label; private $value; private $initialValue; + private $order; abstract public function getPHUIXControlType(); abstract public function getPHUIXControlSpecification(); @@ -37,6 +38,20 @@ abstract class PhabricatorEditEngineCommentAction extends Phobject { return $this->value; } + public function setOrder($order) { + $this->order = $order; + return $this; + } + + public function getOrder() { + return $this->order; + } + + public function getSortVector() { + return id(new PhutilSortVector()) + ->addInt($this->getOrder()); + } + public function setInitialValue($initial_value) { $this->initialValue = $initial_value; return $this; diff --git a/src/applications/transactions/editengine/PhabricatorEditEngine.php b/src/applications/transactions/editengine/PhabricatorEditEngine.php index 9655a83e65..185ac13a5a 100644 --- a/src/applications/transactions/editengine/PhabricatorEditEngine.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngine.php @@ -1382,6 +1382,8 @@ abstract class PhabricatorEditEngine $comment_actions[$key] = $comment_action; } + $comment_actions = msortv($comment_actions, 'getSortVector'); + $view->setCommentActions($comment_actions); return $view; diff --git a/src/applications/transactions/editfield/PhabricatorEditField.php b/src/applications/transactions/editfield/PhabricatorEditField.php index 1216370819..3b4c4e264b 100644 --- a/src/applications/transactions/editfield/PhabricatorEditField.php +++ b/src/applications/transactions/editfield/PhabricatorEditField.php @@ -24,6 +24,7 @@ abstract class PhabricatorEditField extends Phobject { private $commentActionLabel; private $commentActionValue; + private $commentActionOrder = 1000; private $hasCommentActionValue; private $isLocked; @@ -243,6 +244,15 @@ abstract class PhabricatorEditField extends Phobject { return $this->commentActionLabel; } + public function setCommentActionOrder($order) { + $this->commentActionOrder = $order; + return $this; + } + + public function getCommentActionOrder() { + return $this->commentActionOrder; + } + public function setCommentActionValue($comment_action_value) { $this->hasCommentActionValue = true; $this->commentActionValue = $comment_action_value; @@ -686,7 +696,8 @@ abstract class PhabricatorEditField extends Phobject { $action ->setKey($this->getKey()) ->setLabel($label) - ->setValue($this->getValueForCommentAction($value)); + ->setValue($this->getValueForCommentAction($value)) + ->setOrder($this->getCommentActionOrder()); return $action; } From 2ae8e57cf110557c9355419a698a04b4dc46ab32 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 6 Apr 2016 10:05:07 -0700 Subject: [PATCH 65/84] Fix some issue with "Move on Workboard" and workboard-less or invisible projects Summary: Ref T6027. Fixes T10734. - If one of the projects a task is tagged with isn't visible to the user or doesn't have a board, it won't have columns. - Don't show options for projects with disabled boards. Test Plan: - Viewed task with project with no columns; no fatal. - Viewed task with project with disabled board; no options to move on that board. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10734, T6027 Differential Revision: https://secure.phabricator.com/D15640 --- .../maniphest/editor/ManiphestEditEngine.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/applications/maniphest/editor/ManiphestEditEngine.php b/src/applications/maniphest/editor/ManiphestEditEngine.php index 58606538ca..8e5893a9b8 100644 --- a/src/applications/maniphest/editor/ManiphestEditEngine.php +++ b/src/applications/maniphest/editor/ManiphestEditEngine.php @@ -394,7 +394,6 @@ EODOCS ->setViewer($viewer) ->setBoardPHIDs($board_phids) ->setObjectPHIDs(array($task->getPHID())) - ->setFetchAllBoards(true) ->executeLayout(); $map = array(); @@ -403,6 +402,14 @@ EODOCS $in_columns = mpull($in_columns, null, 'getPHID'); $all_columns = $layout_engine->getColumns($board_phid); + if (!$all_columns) { + // This could be a project with no workboard, or a project the viewer + // does not have permission to see. + continue; + } + + $board = head($all_columns)->getProject(); + $options = array(); foreach ($all_columns as $column) { $name = $column->getDisplayName(); @@ -439,7 +446,7 @@ EODOCS } $map[] = array( - 'label' => head($all_columns)->getProject()->getDisplayName(), + 'label' => $board->getDisplayName(), 'options' => $options, ); } From ded034474597767635cb6796afc8f4a48010df2f Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 6 Apr 2016 13:56:40 -0700 Subject: [PATCH 66/84] Have milestones inherit colors from parent projects Summary: Fixes T10737. I agree that this is a better behavior than always making them blue (boring). Test Plan: what {F1208333} wut Reviewers: hach-que, chad Reviewed By: chad Maniphest Tasks: T10737 Differential Revision: https://secure.phabricator.com/D15643 --- src/applications/project/storage/PhabricatorProject.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/project/storage/PhabricatorProject.php b/src/applications/project/storage/PhabricatorProject.php index 061886f6f0..2172156e38 100644 --- a/src/applications/project/storage/PhabricatorProject.php +++ b/src/applications/project/storage/PhabricatorProject.php @@ -573,7 +573,7 @@ final class PhabricatorProject extends PhabricatorProjectDAO public function getDisplayColor() { if ($this->isMilestone()) { - return PhabricatorProjectIconSet::getDefaultColorKey(); + return $this->getParentProject()->getColor(); } return $this->getColor(); From 9b3c09d248ec77fd7ce2c611f4533744111307a9 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 6 Apr 2016 14:01:25 -0700 Subject: [PATCH 67/84] Put older milestones back on the left Summary: This reverts commit 3f50ba90f1d0d30008ea13457dd90f8ea83f25b7. Fixes T10412. Everyone seems to hate this and I don't feel strongly about it. It's definitely a little weird. Test Plan: Straight revert. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10412 Differential Revision: https://secure.phabricator.com/D15644 --- .../project/storage/PhabricatorProjectColumn.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/applications/project/storage/PhabricatorProjectColumn.php b/src/applications/project/storage/PhabricatorProjectColumn.php index 28aed0f5a8..660aafcc1f 100644 --- a/src/applications/project/storage/PhabricatorProjectColumn.php +++ b/src/applications/project/storage/PhabricatorProjectColumn.php @@ -170,16 +170,14 @@ final class PhabricatorProjectColumn // Normal columns and subproject columns go first, in a user-controlled // order. - // All the milestone columns go last, in reverse order (newest on the - // left) so that you don't have to scroll across older milestones to get - // to the newest ones. + // All the milestone columns go last, in their sequential order. if (!$proxy || !$proxy->isMilestone()) { $group = 'A'; $sequence = $this->getSequence(); } else { $group = 'B'; - $sequence = (10000000 - $proxy->getMilestoneNumber()); + $sequence = $proxy->getMilestoneNumber(); } return sprintf('%s%012d', $group, $sequence); From f9836cb646f8bbc8f88927bba23a830aebf308ec Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 6 Apr 2016 12:10:33 -0700 Subject: [PATCH 68/84] Scramble file secrets when related objects change policies Summary: Ref T10262. Files have an internal secret key which is partially used to control access to them, and determines part of the URL you need to access them. Scramble (regenerate) the secret when: - the view policy for the file itself changes (and the new policy is not "public" or "all users"); or - the view policy or space for an object the file is attached to changes (and the file policy is not "public" or "all users"). This basically means that when you change the visibility of a task, any old URLs for attached files stop working and new ones are implicitly generated. Test Plan: - Attached a file to a task, used `SELECT * FROM file WHERE id = ...` to inspect the secret. - Set view policy to public, same secret. - Set view policy to me, new secret. - Changed task view policy, new secret. - Changed task space, new secret. - Changed task title, same old secret. - Added and ran unit tests which cover this behavior. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10262 Differential Revision: https://secure.phabricator.com/D15641 --- .../files/storage/PhabricatorFile.php | 4 + .../__tests__/PhabricatorFileTestCase.php | 127 ++++++++++++++++++ ...habricatorApplicationTransactionEditor.php | 65 ++++++++- 3 files changed, 195 insertions(+), 1 deletion(-) diff --git a/src/applications/files/storage/PhabricatorFile.php b/src/applications/files/storage/PhabricatorFile.php index f1d4c11bd7..81b02f21c5 100644 --- a/src/applications/files/storage/PhabricatorFile.php +++ b/src/applications/files/storage/PhabricatorFile.php @@ -139,6 +139,10 @@ final class PhabricatorFile extends PhabricatorFileDAO return 'F'.$this->getID(); } + public function scrambleSecret() { + return $this->setSecretKey($this->generateSecretKey()); + } + public static function readUploadedFileData($spec) { if (!$spec) { throw new Exception(pht('No file was uploaded!')); diff --git a/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php b/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php index f6199c9a03..8aa22c6578 100644 --- a/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php +++ b/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php @@ -8,6 +8,133 @@ final class PhabricatorFileTestCase extends PhabricatorTestCase { ); } + public function testFileDirectScramble() { + // Changes to a file's view policy should scramble the file secret. + + $engine = new PhabricatorTestStorageEngine(); + $data = Filesystem::readRandomCharacters(64); + + $author = $this->generateNewTestUser(); + + $params = array( + 'name' => 'test.dat', + 'viewPolicy' => PhabricatorPolicies::POLICY_USER, + 'authorPHID' => $author->getPHID(), + 'storageEngines' => array( + $engine, + ), + ); + + $file = PhabricatorFile::newFromFileData($data, $params); + + $secret1 = $file->getSecretKey(); + + // First, change the name: this should not scramble the secret. + $xactions = array(); + $xactions[] = id(new PhabricatorFileTransaction()) + ->setTransactionType(PhabricatorFileTransaction::TYPE_NAME) + ->setNewValue('test.dat2'); + + $engine = id(new PhabricatorFileEditor()) + ->setActor($author) + ->setContentSource($this->newContentSource()) + ->applyTransactions($file, $xactions); + + $file = $file->reload(); + + $secret2 = $file->getSecretKey(); + + $this->assertEqual( + $secret1, + $secret2, + pht('No secret scramble on non-policy edit.')); + + // Now, change the view policy. This should scramble the secret. + $xactions = array(); + $xactions[] = id(new PhabricatorFileTransaction()) + ->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY) + ->setNewValue($author->getPHID()); + + $engine = id(new PhabricatorFileEditor()) + ->setActor($author) + ->setContentSource($this->newContentSource()) + ->applyTransactions($file, $xactions); + + $file = $file->reload(); + $secret3 = $file->getSecretKey(); + + $this->assertTrue( + ($secret1 !== $secret3), + pht('Changing file view policy should scramble secret.')); + } + + public function testFileIndirectScramble() { + // When a file is attached to an object like a task and the task view + // policy changes, the file secret should be scrambled. This invalidates + // old URIs if tasks get locked down. + + $engine = new PhabricatorTestStorageEngine(); + $data = Filesystem::readRandomCharacters(64); + + $author = $this->generateNewTestUser(); + + $params = array( + 'name' => 'test.dat', + 'viewPolicy' => $author->getPHID(), + 'authorPHID' => $author->getPHID(), + 'storageEngines' => array( + $engine, + ), + ); + + $file = PhabricatorFile::newFromFileData($data, $params); + $secret1 = $file->getSecretKey(); + + $task = ManiphestTask::initializeNewTask($author); + + $xactions = array(); + $xactions[] = id(new ManiphestTransaction()) + ->setTransactionType(ManiphestTransaction::TYPE_TITLE) + ->setNewValue(pht('File Scramble Test Task')); + + $xactions[] = id(new ManiphestTransaction()) + ->setTransactionType(ManiphestTransaction::TYPE_DESCRIPTION) + ->setNewValue('{'.$file->getMonogram().'}'); + + id(new ManiphestTransactionEditor()) + ->setActor($author) + ->setContentSource($this->newContentSource()) + ->applyTransactions($task, $xactions); + + $file = $file->reload(); + $secret2 = $file->getSecretKey(); + + $this->assertEqual( + $secret1, + $secret2, + pht( + 'File policy should not scramble when attached to '. + 'newly created object.')); + + $xactions = array(); + $xactions[] = id(new ManiphestTransaction()) + ->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY) + ->setNewValue($author->getPHID()); + + id(new ManiphestTransactionEditor()) + ->setActor($author) + ->setContentSource($this->newContentSource()) + ->applyTransactions($task, $xactions); + + $file = $file->reload(); + $secret3 = $file->getSecretKey(); + + $this->assertTrue( + ($secret1 !== $secret3), + pht('Changing attached object view policy should scramble secret.')); + } + + public function testFileVisibility() { $engine = new PhabricatorTestStorageEngine(); $data = Filesystem::readRandomCharacters(64); diff --git a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php index d7c0bbf5f6..3f51ca7296 100644 --- a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php +++ b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php @@ -678,6 +678,10 @@ abstract class PhabricatorApplicationTransactionEditor $editor->save(); break; + case PhabricatorTransactions::TYPE_VIEW_POLICY: + case PhabricatorTransactions::TYPE_SPACE: + $this->scrambleFileSecrets($object); + break; } } @@ -914,7 +918,6 @@ abstract class PhabricatorApplicationTransactionEditor } $xactions = $this->applyFinalEffects($object, $xactions); - if ($read_locking) { $object->endReadLocking(); $read_locking = false; @@ -3471,4 +3474,64 @@ abstract class PhabricatorApplicationTransactionEditor return $phids; } + /** + * When the view policy for an object is changed, scramble the secret keys + * for attached files to invalidate existing URIs. + */ + private function scrambleFileSecrets($object) { + // If this is a newly created object, we don't need to scramble anything + // since it couldn't have been previously published. + if ($this->getIsNewObject()) { + return; + } + + // If the object is a file itself, scramble it. + if ($object instanceof PhabricatorFile) { + if ($this->shouldScramblePolicy($object->getViewPolicy())) { + $object->scrambleSecret(); + $object->save(); + } + } + + $phid = $object->getPHID(); + + $attached_phids = PhabricatorEdgeQuery::loadDestinationPHIDs( + $phid, + PhabricatorObjectHasFileEdgeType::EDGECONST); + if (!$attached_phids) { + return; + } + + $omnipotent_viewer = PhabricatorUser::getOmnipotentUser(); + + $files = id(new PhabricatorFileQuery()) + ->setViewer($omnipotent_viewer) + ->withPHIDs($attached_phids) + ->execute(); + foreach ($files as $file) { + $view_policy = $file->getViewPolicy(); + if ($this->shouldScramblePolicy($view_policy)) { + $file->scrambleSecret(); + $file->save(); + } + } + } + + + /** + * Check if a policy is strong enough to justify scrambling. Objects which + * are set to very open policies don't need to scramble their files, and + * files with very open policies don't need to be scrambled when associated + * objects change. + */ + private function shouldScramblePolicy($policy) { + switch ($policy) { + case PhabricatorPolicies::POLICY_PUBLIC: + case PhabricatorPolicies::POLICY_USER: + return false; + } + + return true; + } + } From 439821c7b2cd80c78b0c5da7ef8545710abf0856 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 6 Apr 2016 13:06:34 -0700 Subject: [PATCH 69/84] Don't require one-time tokens to view file resources Summary: Ref T10262. This removes one-time tokens and makes file data responses always-cacheable (for 30 days). The URI will stop working once any attached object changes its view policy, or the file view policy itself changes. Files with `canCDN` (totally public data like profile images, CSS, JS, etc) use "cache-control: public" so they can be CDN'd. Files without `canCDN` use "cache-control: private" so they won't be cached by the CDN. They could still be cached by a misbehaving local cache, but if you don't want your users seeing one anothers' secret files you should configure your local network properly. Our "Cache-Control" headers were also from 1999 or something, update them to be more modern/sane. I can't find any evidence that any browser has done the wrong thing with this simpler ruleset in the last ~10 years. Test Plan: - Configured alternate file domain. - Viewed site: stuff worked. - Accessed a file on primary domain, got redirected to alternate domain. - Verified proper cache headers for `canCDN` (public) and non-`canCDN` (private) files. - Uploaded a file to a task, edited task policy, verified it scrambled the old URI. - Reloaded task, new URI generated transparently. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10262 Differential Revision: https://secure.phabricator.com/D15642 --- src/__phutil_library_map__.php | 2 - src/aphront/response/AphrontFileResponse.php | 21 --- src/aphront/response/AphrontProxyResponse.php | 5 + src/aphront/response/AphrontResponse.php | 24 +++- .../controller/CelerityResourceController.php | 1 + .../controller/DiffusionServeController.php | 2 +- .../PhabricatorFileDataController.php | 129 ++++-------------- .../files/storage/PhabricatorFile.php | 52 +------ ...habricatorFileAccessTemporaryTokenType.php | 17 --- .../PhabricatorRobotsController.php | 3 +- 10 files changed, 55 insertions(+), 201 deletions(-) delete mode 100644 src/applications/files/temporarytoken/PhabricatorFileAccessTemporaryTokenType.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 671a8e8e26..9d0150ebee 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2378,7 +2378,6 @@ phutil_register_library_map(array( 'PhabricatorFeedStoryPublisher' => 'applications/feed/PhabricatorFeedStoryPublisher.php', 'PhabricatorFeedStoryReference' => 'applications/feed/storage/PhabricatorFeedStoryReference.php', 'PhabricatorFile' => 'applications/files/storage/PhabricatorFile.php', - 'PhabricatorFileAccessTemporaryTokenType' => 'applications/files/temporarytoken/PhabricatorFileAccessTemporaryTokenType.php', 'PhabricatorFileBundleLoader' => 'applications/files/query/PhabricatorFileBundleLoader.php', 'PhabricatorFileChunk' => 'applications/files/storage/PhabricatorFileChunk.php', 'PhabricatorFileChunkIterator' => 'applications/files/engine/PhabricatorFileChunkIterator.php', @@ -6842,7 +6841,6 @@ phutil_register_library_map(array( 'PhabricatorPolicyInterface', 'PhabricatorDestructibleInterface', ), - 'PhabricatorFileAccessTemporaryTokenType' => 'PhabricatorAuthTemporaryTokenType', 'PhabricatorFileBundleLoader' => 'Phobject', 'PhabricatorFileChunk' => array( 'PhabricatorFileDAO', diff --git a/src/aphront/response/AphrontFileResponse.php b/src/aphront/response/AphrontFileResponse.php index bf58421c68..4688e3bc8f 100644 --- a/src/aphront/response/AphrontFileResponse.php +++ b/src/aphront/response/AphrontFileResponse.php @@ -11,7 +11,6 @@ final class AphrontFileResponse extends AphrontResponse { private $rangeMin; private $rangeMax; private $allowOrigins = array(); - private $fileToken; public function addAllowOrigin($origin) { $this->allowOrigins[] = $origin; @@ -76,15 +75,6 @@ final class AphrontFileResponse extends AphrontResponse { return $this; } - public function setTemporaryFileToken(PhabricatorAuthTemporaryToken $token) { - $this->fileToken = $token; - return $this; - } - - public function getTemporaryFileToken() { - return $this->fileToken; - } - public function getHeaders() { $headers = array( array('Content-Type', $this->getMimeType()), @@ -128,15 +118,4 @@ final class AphrontFileResponse extends AphrontResponse { return $headers; } - public function didCompleteWrite($aborted) { - if (!$aborted) { - $token = $this->getTemporaryFileToken(); - if ($token) { - $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); - $token->delete(); - unset($unguarded); - } - } - } - } diff --git a/src/aphront/response/AphrontProxyResponse.php b/src/aphront/response/AphrontProxyResponse.php index 772c4adaab..eda9e9b719 100644 --- a/src/aphront/response/AphrontProxyResponse.php +++ b/src/aphront/response/AphrontProxyResponse.php @@ -39,6 +39,11 @@ abstract class AphrontProxyResponse return $this; } + public function setCanCDN($can_cdn) { + $this->getProxy()->setCanCDN($can_cdn); + return $this; + } + public function setLastModified($epoch_timestamp) { $this->getProxy()->setLastModified($epoch_timestamp); return $this; diff --git a/src/aphront/response/AphrontResponse.php b/src/aphront/response/AphrontResponse.php index 72dacf977e..729bc5cc42 100644 --- a/src/aphront/response/AphrontResponse.php +++ b/src/aphront/response/AphrontResponse.php @@ -4,6 +4,7 @@ abstract class AphrontResponse extends Phobject { private $request; private $cacheable = false; + private $canCDN; private $responseCode = 200; private $lastModified = null; @@ -66,6 +67,11 @@ abstract class AphrontResponse extends Phobject { return $this; } + public function setCanCDN($can_cdn) { + $this->canCDN = $can_cdn; + return $this; + } + public function setLastModified($epoch_timestamp) { $this->lastModified = $epoch_timestamp; return $this; @@ -186,6 +192,18 @@ abstract class AphrontResponse extends Phobject { public function getCacheHeaders() { $headers = array(); if ($this->cacheable) { + if ($this->canCDN) { + $headers[] = array( + 'Cache-Control', + 'public', + ); + } else { + $headers[] = array( + 'Cache-Control', + 'private', + ); + } + $headers[] = array( 'Expires', $this->formatEpochTimestampForHTTPHeader(time() + $this->cacheable), @@ -193,11 +211,7 @@ abstract class AphrontResponse extends Phobject { } else { $headers[] = array( 'Cache-Control', - 'private, no-cache, no-store, must-revalidate', - ); - $headers[] = array( - 'Pragma', - 'no-cache', + 'no-store', ); $headers[] = array( 'Expires', diff --git a/src/applications/celerity/controller/CelerityResourceController.php b/src/applications/celerity/controller/CelerityResourceController.php index 01f6424382..4e6feeac6c 100644 --- a/src/applications/celerity/controller/CelerityResourceController.php +++ b/src/applications/celerity/controller/CelerityResourceController.php @@ -140,6 +140,7 @@ abstract class CelerityResourceController extends PhabricatorController { private function makeResponseCacheable(AphrontResponse $response) { $response->setCacheDurationInSeconds(60 * 60 * 24 * 30); $response->setLastModified(time()); + $response->setCanCDN(true); return $response; } diff --git a/src/applications/diffusion/controller/DiffusionServeController.php b/src/applications/diffusion/controller/DiffusionServeController.php index 80fb224163..fab2bcca96 100644 --- a/src/applications/diffusion/controller/DiffusionServeController.php +++ b/src/applications/diffusion/controller/DiffusionServeController.php @@ -991,7 +991,7 @@ final class DiffusionServeController extends DiffusionController { // $no_authorization = 'Basic '.base64_encode('none'); - $get_uri = $file->getCDNURIWithToken(); + $get_uri = $file->getCDNURI(); $actions['download'] = array( 'href' => $get_uri, 'header' => array( diff --git a/src/applications/files/controller/PhabricatorFileDataController.php b/src/applications/files/controller/PhabricatorFileDataController.php index 848560f099..fc0f2114e9 100644 --- a/src/applications/files/controller/PhabricatorFileDataController.php +++ b/src/applications/files/controller/PhabricatorFileDataController.php @@ -4,7 +4,6 @@ final class PhabricatorFileDataController extends PhabricatorFileController { private $phid; private $key; - private $token; private $file; public function shouldRequireLogin() { @@ -15,7 +14,6 @@ final class PhabricatorFileDataController extends PhabricatorFileController { $viewer = $request->getViewer(); $this->phid = $request->getURIData('phid'); $this->key = $request->getURIData('key'); - $this->token = $request->getURIData('token'); $alt = PhabricatorEnv::getEnvConfig('security.alternate-file-domain'); $base_uri = PhabricatorEnv::getEnvConfig('phabricator.base-uri'); @@ -24,99 +22,40 @@ final class PhabricatorFileDataController extends PhabricatorFileController { $req_domain = $request->getHost(); $main_domain = id(new PhutilURI($base_uri))->getDomain(); - $cache_response = true; - if (empty($alt) || $main_domain == $alt_domain) { - // Alternate files domain isn't configured or it's set - // to the same as the default domain - - $response = $this->loadFile($viewer); - if ($response) { - return $response; - } - $file = $this->getFile(); - - // when the file is not CDNable, don't allow cache - $cache_response = $file->getCanCDN(); + if (!strlen($alt) || $main_domain == $alt_domain) { + // No alternate domain. + $should_redirect = false; + $use_viewer = $viewer; + $is_alternate_domain = false; } else if ($req_domain != $alt_domain) { - // Alternate domain is configured but this request isn't using it + // Alternate domain, but this request is on the main domain. + $should_redirect = true; + $use_viewer = $viewer; + $is_alternate_domain = false; + } else { + // Alternate domain, and on the alternate domain. + $should_redirect = false; + $use_viewer = PhabricatorUser::getOmnipotentUser(); + $is_alternate_domain = true; + } - $response = $this->loadFile($viewer); - if ($response) { - return $response; - } - $file = $this->getFile(); + $response = $this->loadFile($use_viewer); + if ($response) { + return $response; + } - // if the user can see the file, generate a token; - // redirect to the alt domain with the token; - $token_uri = $file->getCDNURIWithToken(); - $token_uri = new PhutilURI($token_uri); - $token_uri = $this->addURIParameters($token_uri); + $file = $this->getFile(); + if ($should_redirect) { return id(new AphrontRedirectResponse()) ->setIsExternal(true) - ->setURI($token_uri); - - } else { - // We are using the alternate domain. We don't have authentication - // on this domain, so we bypass policy checks when loading the file. - - $bypass_policies = PhabricatorUser::getOmnipotentUser(); - $response = $this->loadFile($bypass_policies); - if ($response) { - return $response; - } - $file = $this->getFile(); - - $acquire_token_uri = id(new PhutilURI($file->getViewURI())) - ->setDomain($main_domain); - $acquire_token_uri = $this->addURIParameters($acquire_token_uri); - - if ($this->token) { - // validate the token, if it is valid, continue - $validated_token = $file->validateOneTimeToken($this->token); - - if (!$validated_token) { - $dialog = $this->newDialog() - ->setShortTitle(pht('Expired File')) - ->setTitle(pht('File Link Has Expired')) - ->appendParagraph( - pht( - 'The link you followed to view this file is invalid or '. - 'expired.')) - ->appendParagraph( - pht( - 'Continue to generate a new link to the file. You may be '. - 'required to log in.')) - ->addCancelButton( - $acquire_token_uri, - pht('Continue')); - - // Build an explicit response so we can respond with HTTP/403 instead - // of HTTP/200. - $response = id(new AphrontDialogResponse()) - ->setDialog($dialog) - ->setHTTPResponseCode(403); - - return $response; - } - // return the file data without cache headers - $cache_response = false; - } else if (!$file->getCanCDN()) { - // file cannot be served via cdn, and no token given - // redirect to the main domain to aquire a token - - // This is marked as an "external" URI because it is fully qualified. - return id(new AphrontRedirectResponse()) - ->setIsExternal(true) - ->setURI($acquire_token_uri); - } + ->setURI($file->getCDNURI()); } $response = new AphrontFileResponse(); - if ($cache_response) { - $response->setCacheDurationInSeconds(60 * 60 * 24 * 30); - } + $response->setCacheDurationInSeconds(60 * 60 * 24 * 30); + $response->setCanCDN($file->getCanCDN()); $begin = null; $end = null; @@ -138,10 +77,6 @@ final class PhabricatorFileDataController extends PhabricatorFileController { $response->setHTTPResponseCode(206); $response->setRange($begin, ($end - 1)); } - } else if (isset($validated_token)) { - // We set this on the response, and the response deletes it after the - // transfer completes. This allows transfers to be resumed, in theory. - $response->setTemporaryFileToken($validated_token); } $is_viewable = $file->isViewableInBrowser(); @@ -150,7 +85,7 @@ final class PhabricatorFileDataController extends PhabricatorFileController { if ($is_viewable && !$force_download) { $response->setMimeType($file->getViewableMimeType()); } else { - if (!$request->isHTTPPost() && !$alt_domain) { + if (!$request->isHTTPPost() && !$is_alternate_domain) { // NOTE: Require POST to download files from the primary domain. We'd // rather go full-bore and do a real CSRF check, but can't currently // authenticate users on the file domain. This should blunt any @@ -174,20 +109,6 @@ final class PhabricatorFileDataController extends PhabricatorFileController { return $response; } - /** - * Add passthrough parameters to the URI so they aren't lost when we - * redirect to acquire tokens. - */ - private function addURIParameters(PhutilURI $uri) { - $request = $this->getRequest(); - - if ($request->getBool('download')) { - $uri->setQueryParam('download', 1); - } - - return $uri; - } - private function loadFile(PhabricatorUser $viewer) { $file = id(new PhabricatorFileQuery()) ->setViewer($viewer) diff --git a/src/applications/files/storage/PhabricatorFile.php b/src/applications/files/storage/PhabricatorFile.php index 81b02f21c5..5c7c805663 100644 --- a/src/applications/files/storage/PhabricatorFile.php +++ b/src/applications/files/storage/PhabricatorFile.php @@ -697,10 +697,10 @@ final class PhabricatorFile extends PhabricatorFileDAO pht('You must save a file before you can generate a view URI.')); } - return $this->getCDNURI(null); + return $this->getCDNURI(); } - private function getCDNURI($token) { + public function getCDNURI() { $name = self::normalizeFileName($this->getName()); $name = phutil_escape_uri($name); @@ -720,9 +720,6 @@ final class PhabricatorFile extends PhabricatorFileDAO $parts[] = $this->getSecretKey(); $parts[] = $this->getPHID(); - if ($token) { - $parts[] = $token; - } $parts[] = $name; $path = '/'.implode('/', $parts); @@ -737,19 +734,6 @@ final class PhabricatorFile extends PhabricatorFileDAO } } - /** - * Get the CDN URI for this file, including a one-time-use security token. - * - */ - public function getCDNURIWithToken() { - if (!$this->getPHID()) { - throw new Exception( - pht('You must save a file before you can generate a CDN URI.')); - } - - return $this->getCDNURI($this->generateOneTimeToken()); - } - public function getInfoURI() { return '/'.$this->getMonogram(); @@ -1120,38 +1104,6 @@ final class PhabricatorFile extends PhabricatorFileDAO return $this; } - protected function generateOneTimeToken() { - $key = Filesystem::readRandomCharacters(16); - $token_type = PhabricatorFileAccessTemporaryTokenType::TOKENTYPE; - - // Save the new secret. - $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); - $token = id(new PhabricatorAuthTemporaryToken()) - ->setTokenResource($this->getPHID()) - ->setTokenType($token_type) - ->setTokenExpires(time() + phutil_units('1 hour in seconds')) - ->setTokenCode(PhabricatorHash::digest($key)) - ->save(); - unset($unguarded); - - return $key; - } - - public function validateOneTimeToken($token_code) { - $token_type = PhabricatorFileAccessTemporaryTokenType::TOKENTYPE; - - $token = id(new PhabricatorAuthTemporaryTokenQuery()) - ->setViewer(PhabricatorUser::getOmnipotentUser()) - ->withTokenResources(array($this->getPHID())) - ->withTokenTypes(array($token_type)) - ->withExpired(false) - ->withTokenCodes(array(PhabricatorHash::digest($token_code))) - ->executeOne(); - - return $token; - } - - /** * Write the policy edge between this file and some object. * diff --git a/src/applications/files/temporarytoken/PhabricatorFileAccessTemporaryTokenType.php b/src/applications/files/temporarytoken/PhabricatorFileAccessTemporaryTokenType.php deleted file mode 100644 index 73b5cd8c09..0000000000 --- a/src/applications/files/temporarytoken/PhabricatorFileAccessTemporaryTokenType.php +++ /dev/null @@ -1,17 +0,0 @@ -setContent($content) - ->setCacheDurationInSeconds(phutil_units('2 hours in seconds')); + ->setCacheDurationInSeconds(phutil_units('2 hours in seconds')) + ->setCanCDN(true); } } From 8aad862cd4e257fa2c9b42513ee46afa8a35ef0a Mon Sep 17 00:00:00 2001 From: Chad Little Date: Wed, 6 Apr 2016 15:20:53 -0700 Subject: [PATCH 70/84] Normalize casing on property boxes Summary: Going to render these all normal case instead of all caps, and bump up the font size. Should be more consistent. Yellow if you green anything orange. Test Plan: grep, lint Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15645 --- .../almanac/controller/AlmanacBindingViewController.php | 2 +- src/applications/almanac/controller/AlmanacController.php | 2 +- .../almanac/controller/AlmanacServiceViewController.php | 2 +- .../badges/controller/PhabricatorBadgesViewController.php | 2 +- .../controller/PhabricatorCalendarEventViewController.php | 4 ++-- .../controller/PhabricatorDaemonBulkJobViewController.php | 2 +- .../controller/PhabricatorDashboardManageController.php | 2 +- .../PhabricatorDashboardPanelViewController.php | 2 +- .../controller/DifferentialRevisionViewController.php | 2 +- .../diffusion/controller/DiffusionBrowseController.php | 8 ++++---- .../diffusion/controller/DiffusionCommitController.php | 4 ++-- .../controller/DiffusionRepositoryController.php | 4 ++-- .../fund/controller/FundInitiativeViewController.php | 2 +- .../controller/HarbormasterBuildViewController.php | 2 +- .../controller/HarbormasterBuildableViewController.php | 2 +- .../controller/HarbormasterStepViewController.php | 2 +- .../herald/controller/HeraldRuleViewController.php | 4 ++-- .../controller/LegalpadDocumentManageController.php | 2 +- .../macro/controller/PhabricatorMacroViewController.php | 4 ++-- .../controller/ManiphestTaskDetailController.php | 4 ++-- .../PhabricatorApplicationDetailViewController.php | 2 +- .../nuance/controller/NuanceItemManageController.php | 2 +- .../nuance/controller/NuanceSourceViewController.php | 4 ++-- .../controller/PassphraseCredentialViewController.php | 2 +- .../PhabricatorPeopleProfileManageController.php | 2 +- src/applications/phlux/controller/PhluxViewController.php | 2 +- .../controller/PhortuneMerchantViewController.php | 4 ++-- .../phortune/controller/PhortuneProductViewController.php | 2 +- .../controller/PhortuneSubscriptionViewController.php | 2 +- .../controller/PhabricatorPhurlURLViewController.php | 2 +- .../ponder/controller/PonderQuestionViewController.php | 2 +- .../controller/PhabricatorProjectManageController.php | 2 +- .../spaces/controller/PhabricatorSpacesViewController.php | 2 +- 33 files changed, 44 insertions(+), 44 deletions(-) diff --git a/src/applications/almanac/controller/AlmanacBindingViewController.php b/src/applications/almanac/controller/AlmanacBindingViewController.php index ead3e1b4c1..d1ed2178d2 100644 --- a/src/applications/almanac/controller/AlmanacBindingViewController.php +++ b/src/applications/almanac/controller/AlmanacBindingViewController.php @@ -68,7 +68,7 @@ final class AlmanacBindingViewController $this->buildAlmanacPropertiesTable($binding), $timeline, )) - ->addPropertySection(pht('DETAILS'), $details); + ->addPropertySection(pht('Details'), $details); return $this->newPage() ->setTitle($title) diff --git a/src/applications/almanac/controller/AlmanacController.php b/src/applications/almanac/controller/AlmanacController.php index c76fdf11b5..c23e99591d 100644 --- a/src/applications/almanac/controller/AlmanacController.php +++ b/src/applications/almanac/controller/AlmanacController.php @@ -158,7 +158,7 @@ abstract class AlmanacController ->setIcon('fa-plus'); $header = id(new PHUIHeaderView()) - ->setHeader(pht('PROPERTIES')) + ->setHeader(pht('Properties')) ->addActionLink($add_button); return id(new PHUIObjectBoxView()) diff --git a/src/applications/almanac/controller/AlmanacServiceViewController.php b/src/applications/almanac/controller/AlmanacServiceViewController.php index 1036dc9e78..f4057ca32f 100644 --- a/src/applications/almanac/controller/AlmanacServiceViewController.php +++ b/src/applications/almanac/controller/AlmanacServiceViewController.php @@ -81,7 +81,7 @@ final class AlmanacServiceViewController $service->getServiceImplementation()->getServiceTypeShortName()); return id(new PHUIObjectBoxView()) - ->setHeaderText(pht('DETAILS')) + ->setHeaderText(pht('Details')) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($properties); } diff --git a/src/applications/badges/controller/PhabricatorBadgesViewController.php b/src/applications/badges/controller/PhabricatorBadgesViewController.php index 4539440588..05f6286020 100644 --- a/src/applications/badges/controller/PhabricatorBadgesViewController.php +++ b/src/applications/badges/controller/PhabricatorBadgesViewController.php @@ -72,7 +72,7 @@ final class PhabricatorBadgesViewController $timeline, $comment_view, )) - ->addPropertySection(pht('DESCRIPTION'), $details); + ->addPropertySection(pht('Description'), $details); return $this->newPage() ->setTitle($title) diff --git a/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php b/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php index 5880db1210..7c3b34bc09 100644 --- a/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php +++ b/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php @@ -94,8 +94,8 @@ final class PhabricatorCalendarEventViewController $add_comment_form, )) ->setCurtain($curtain) - ->addPropertySection(pht('DETAILS'), $details) - ->addPropertySection(pht('DESCRIPTION'), $description); + ->addPropertySection(pht('Details'), $details) + ->addPropertySection(pht('Description'), $description); return $this->newPage() ->setTitle($page_title) diff --git a/src/applications/daemon/controller/PhabricatorDaemonBulkJobViewController.php b/src/applications/daemon/controller/PhabricatorDaemonBulkJobViewController.php index f78bfdd7df..f794024591 100644 --- a/src/applications/daemon/controller/PhabricatorDaemonBulkJobViewController.php +++ b/src/applications/daemon/controller/PhabricatorDaemonBulkJobViewController.php @@ -29,7 +29,7 @@ final class PhabricatorDaemonBulkJobViewController $curtain = $this->buildCurtainView($job); $box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('DETAILS')) + ->setHeaderText(pht('Details')) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->addPropertyList($properties); diff --git a/src/applications/dashboard/controller/PhabricatorDashboardManageController.php b/src/applications/dashboard/controller/PhabricatorDashboardManageController.php index 475716d18d..4e31b2d2ae 100644 --- a/src/applications/dashboard/controller/PhabricatorDashboardManageController.php +++ b/src/applications/dashboard/controller/PhabricatorDashboardManageController.php @@ -198,7 +198,7 @@ final class PhabricatorDashboardManageController $viewer->renderHandleList($dashboard->getPanelPHIDs())); return id(new PHUIObjectBoxView()) - ->setHeaderText(pht('DETAILS')) + ->setHeaderText(pht('Details')) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->addPropertyList($properties); } diff --git a/src/applications/dashboard/controller/PhabricatorDashboardPanelViewController.php b/src/applications/dashboard/controller/PhabricatorDashboardPanelViewController.php index b16f60abd7..9fd9e1840d 100644 --- a/src/applications/dashboard/controller/PhabricatorDashboardPanelViewController.php +++ b/src/applications/dashboard/controller/PhabricatorDashboardPanelViewController.php @@ -165,7 +165,7 @@ final class PhabricatorDashboardPanelViewController : phutil_tag('em', array(), $does_not_appear)); return id(new PHUIObjectBoxView()) - ->setHeaderText(pht('DETAILS')) + ->setHeaderText(pht('Details')) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->addPropertyList($properties); } diff --git a/src/applications/differential/controller/DifferentialRevisionViewController.php b/src/applications/differential/controller/DifferentialRevisionViewController.php index 61fffd9b2a..78bcd667b2 100644 --- a/src/applications/differential/controller/DifferentialRevisionViewController.php +++ b/src/applications/differential/controller/DifferentialRevisionViewController.php @@ -480,7 +480,7 @@ final class DifferentialRevisionViewController extends DifferentialController { } $header = id(new PHUIHeaderView()) - ->setHeader(pht('DETAILS')); + ->setHeader(pht('Details')); return id(new PHUIObjectBoxView()) ->setHeader($header) diff --git a/src/applications/diffusion/controller/DiffusionBrowseController.php b/src/applications/diffusion/controller/DiffusionBrowseController.php index a79613cfa9..7616922aa8 100644 --- a/src/applications/diffusion/controller/DiffusionBrowseController.php +++ b/src/applications/diffusion/controller/DiffusionBrowseController.php @@ -310,7 +310,7 @@ final class DiffusionBrowseController extends DiffusionController { )); if ($properties) { - $view->addPropertySection(pht('DETAILS'), $properties); + $view->addPropertySection(pht('Details'), $properties); } $title = array($basename, $repository->getDisplayName()); @@ -413,7 +413,7 @@ final class DiffusionBrowseController extends DiffusionController { )); if ($details) { - $view->addPropertySection(pht('DETAILS'), $details); + $view->addPropertySection(pht('Details'), $details); } return $this->newPage() @@ -1409,7 +1409,7 @@ final class DiffusionBrowseController extends DiffusionController { $file = $this->renderFileButton($file_uri); $header = id(new PHUIHeaderView()) - ->setHeader(pht('DETAILS')) + ->setHeader(pht('Details')) ->addActionLink($file); $box = id(new PHUIObjectBoxView()) @@ -1426,7 +1426,7 @@ final class DiffusionBrowseController extends DiffusionController { ->appendChild($message); $header = id(new PHUIHeaderView()) - ->setHeader(pht('DETAILS')); + ->setHeader(pht('Details')); $box = id(new PHUIObjectBoxView()) ->setHeader($header) diff --git a/src/applications/diffusion/controller/DiffusionCommitController.php b/src/applications/diffusion/controller/DiffusionCommitController.php index 69c40b044f..e2f0d51f4a 100644 --- a/src/applications/diffusion/controller/DiffusionCommitController.php +++ b/src/applications/diffusion/controller/DiffusionCommitController.php @@ -355,8 +355,8 @@ final class DiffusionCommitController extends DiffusionController { $change_list, $add_comment, )) - ->addPropertySection(pht('DESCRIPTION'), $detail_list) - ->addPropertySection(pht('DETAILS'), $details) + ->addPropertySection(pht('Description'), $detail_list) + ->addPropertySection(pht('Details'), $details) ->setCurtain($curtain); $page = $this->newPage() diff --git a/src/applications/diffusion/controller/DiffusionRepositoryController.php b/src/applications/diffusion/controller/DiffusionRepositoryController.php index 792bcc4249..b9e8fa40c6 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryController.php @@ -288,7 +288,7 @@ final class DiffusionRepositoryController extends DiffusionController { $description = new PHUIRemarkupView($viewer, $description); $view->addTextContent($description); return id(new PHUIObjectBoxView()) - ->setHeaderText(pht('DESCRIPTION')) + ->setHeaderText(pht('Description')) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($view); } @@ -354,7 +354,7 @@ final class DiffusionRepositoryController extends DiffusionController { } $box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('DETAILS')) + ->setHeaderText(pht('Details')) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($view); diff --git a/src/applications/fund/controller/FundInitiativeViewController.php b/src/applications/fund/controller/FundInitiativeViewController.php index 18b123bf3a..4960e7aa35 100644 --- a/src/applications/fund/controller/FundInitiativeViewController.php +++ b/src/applications/fund/controller/FundInitiativeViewController.php @@ -62,7 +62,7 @@ final class FundInitiativeViewController $timeline, $add_comment, )) - ->addPropertySection(pht('DETAILS'), $details); + ->addPropertySection(pht('Details'), $details); return $this->newPage() ->setTitle($title) diff --git a/src/applications/harbormaster/controller/HarbormasterBuildViewController.php b/src/applications/harbormaster/controller/HarbormasterBuildViewController.php index 320e94d642..6b0e7c1e0d 100644 --- a/src/applications/harbormaster/controller/HarbormasterBuildViewController.php +++ b/src/applications/harbormaster/controller/HarbormasterBuildViewController.php @@ -539,7 +539,7 @@ final class HarbormasterBuildViewController $this->getStatus($build)); return id(new PHUIObjectBoxView()) - ->setHeaderText(pht('PROPERTIES')) + ->setHeaderText(pht('Properties')) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($properties); diff --git a/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php b/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php index fe124367df..390ff2ec3d 100644 --- a/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php +++ b/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php @@ -178,7 +178,7 @@ final class HarbormasterBuildableViewController : pht('Automatic Buildable')); return id(new PHUIObjectBoxView()) - ->setHeaderText(pht('PROPERTIES')) + ->setHeaderText(pht('Properties')) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($properties); } diff --git a/src/applications/harbormaster/controller/HarbormasterStepViewController.php b/src/applications/harbormaster/controller/HarbormasterStepViewController.php index 07be537fa9..a404b48e88 100644 --- a/src/applications/harbormaster/controller/HarbormasterStepViewController.php +++ b/src/applications/harbormaster/controller/HarbormasterStepViewController.php @@ -104,7 +104,7 @@ final class HarbormasterStepViewController } return id(new PHUIObjectBoxView()) - ->setHeaderText(pht('PROPERTIES')) + ->setHeaderText(pht('Properties')) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($view); } diff --git a/src/applications/herald/controller/HeraldRuleViewController.php b/src/applications/herald/controller/HeraldRuleViewController.php index f5e058d3f3..818eb7560f 100644 --- a/src/applications/herald/controller/HeraldRuleViewController.php +++ b/src/applications/herald/controller/HeraldRuleViewController.php @@ -54,8 +54,8 @@ final class HeraldRuleViewController extends HeraldController { ->setHeader($header) ->setCurtain($curtain) ->setMainColumn($timeline) - ->addPropertySection(pht('DETAILS'), $details) - ->addPropertySection(pht('DESCRIPTION'), $description); + ->addPropertySection(pht('Details'), $details) + ->addPropertySection(pht('Description'), $description); return $this->newPage() ->setTitle($title) diff --git a/src/applications/legalpad/controller/LegalpadDocumentManageController.php b/src/applications/legalpad/controller/LegalpadDocumentManageController.php index 134dada128..1a39f2c143 100644 --- a/src/applications/legalpad/controller/LegalpadDocumentManageController.php +++ b/src/applications/legalpad/controller/LegalpadDocumentManageController.php @@ -176,7 +176,7 @@ final class LegalpadDocumentManageController extends LegalpadController { } return id(new PHUIObjectBoxView()) - ->setHeaderText(pht('PROPERTIES')) + ->setHeaderText(pht('Properties')) ->addPropertyList($properties) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY); } diff --git a/src/applications/macro/controller/PhabricatorMacroViewController.php b/src/applications/macro/controller/PhabricatorMacroViewController.php index 8c12e5528a..386029c7ce 100644 --- a/src/applications/macro/controller/PhabricatorMacroViewController.php +++ b/src/applications/macro/controller/PhabricatorMacroViewController.php @@ -72,8 +72,8 @@ final class PhabricatorMacroViewController $timeline, $add_comment_form, )) - ->addPropertySection(pht('MACRO'), $file) - ->addPropertySection(pht('DETAILS'), $details); + ->addPropertySection(pht('Macro'), $file) + ->addPropertySection(pht('Details'), $details); return $this->newPage() ->setTitle($title_short) diff --git a/src/applications/maniphest/controller/ManiphestTaskDetailController.php b/src/applications/maniphest/controller/ManiphestTaskDetailController.php index 285f919ce8..1007672e23 100644 --- a/src/applications/maniphest/controller/ManiphestTaskDetailController.php +++ b/src/applications/maniphest/controller/ManiphestTaskDetailController.php @@ -88,8 +88,8 @@ final class ManiphestTaskDetailController extends ManiphestController { $timeline, $comment_view, )) - ->addPropertySection(pht('DESCRIPTION'), $description) - ->addPropertySection(pht('DETAILS'), $details); + ->addPropertySection(pht('Description'), $description) + ->addPropertySection(pht('Details'), $details); return $this->newPage() ->setTitle($title) diff --git a/src/applications/meta/controller/PhabricatorApplicationDetailViewController.php b/src/applications/meta/controller/PhabricatorApplicationDetailViewController.php index f86f9eb4d1..54068e1e71 100644 --- a/src/applications/meta/controller/PhabricatorApplicationDetailViewController.php +++ b/src/applications/meta/controller/PhabricatorApplicationDetailViewController.php @@ -63,7 +63,7 @@ final class PhabricatorApplicationDetailViewController $policies, $panels, )) - ->addPropertySection(pht('DETAILS'), $details); + ->addPropertySection(pht('Details'), $details); return $this->newPage() ->setTitle($title) diff --git a/src/applications/nuance/controller/NuanceItemManageController.php b/src/applications/nuance/controller/NuanceItemManageController.php index c86d2cd985..2b6b4c89b8 100644 --- a/src/applications/nuance/controller/NuanceItemManageController.php +++ b/src/applications/nuance/controller/NuanceItemManageController.php @@ -40,7 +40,7 @@ final class NuanceItemManageController extends NuanceController { $view = id(new PHUITwoColumnView()) ->setHeader($header) ->setCurtain($curtain) - ->addPropertySection(pht('DETAILS'), $properties) + ->addPropertySection(pht('Details'), $properties) ->setMainColumn($timeline); return $this->newPage() diff --git a/src/applications/nuance/controller/NuanceSourceViewController.php b/src/applications/nuance/controller/NuanceSourceViewController.php index af602bfd7e..93facde3c1 100644 --- a/src/applications/nuance/controller/NuanceSourceViewController.php +++ b/src/applications/nuance/controller/NuanceSourceViewController.php @@ -40,8 +40,8 @@ final class NuanceSourceViewController $view = id(new PHUITwoColumnView()) ->setHeader($header) ->setCurtain($curtain) - ->addPropertySection(pht('DETAILS'), $properties) - ->addPropertySection(pht('ROUTING'), $routing_list) + ->addPropertySection(pht('Details'), $properties) + ->addPropertySection(pht('Routing'), $routing_list) ->setMainColumn($timeline); return $this->newPage() diff --git a/src/applications/passphrase/controller/PassphraseCredentialViewController.php b/src/applications/passphrase/controller/PassphraseCredentialViewController.php index db31964773..1bbea88ec6 100644 --- a/src/applications/passphrase/controller/PassphraseCredentialViewController.php +++ b/src/applications/passphrase/controller/PassphraseCredentialViewController.php @@ -36,7 +36,7 @@ final class PassphraseCredentialViewController extends PassphraseController { ->setSubheader($subheader) ->setCurtain($curtain) ->setMainColumn($timeline) - ->addPropertySection(pht('PROPERTIES'), $content); + ->addPropertySection(pht('Properties'), $content); return $this->newPage() ->setTitle($title) diff --git a/src/applications/people/controller/PhabricatorPeopleProfileManageController.php b/src/applications/people/controller/PhabricatorPeopleProfileManageController.php index 51cf79ecff..08b438eaa2 100644 --- a/src/applications/people/controller/PhabricatorPeopleProfileManageController.php +++ b/src/applications/people/controller/PhabricatorPeopleProfileManageController.php @@ -57,7 +57,7 @@ final class PhabricatorPeopleProfileManageController $manage = id(new PHUITwoColumnView()) ->setHeader($header) ->setCurtain($curtain) - ->addPropertySection(pht('DETAILS'), $properties) + ->addPropertySection(pht('Details'), $properties) ->setMainColumn( array( $timeline, diff --git a/src/applications/phlux/controller/PhluxViewController.php b/src/applications/phlux/controller/PhluxViewController.php index b3019443be..6ad0fe22cd 100644 --- a/src/applications/phlux/controller/PhluxViewController.php +++ b/src/applications/phlux/controller/PhluxViewController.php @@ -41,7 +41,7 @@ final class PhluxViewController extends PhluxController { $timeline->setShouldTerminate(true); $object_box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('DETAILS')) + ->setHeaderText(pht('Details')) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->addPropertyList($properties); diff --git a/src/applications/phortune/controller/PhortuneMerchantViewController.php b/src/applications/phortune/controller/PhortuneMerchantViewController.php index 59d9273eaa..a0e1100004 100644 --- a/src/applications/phortune/controller/PhortuneMerchantViewController.php +++ b/src/applications/phortune/controller/PhortuneMerchantViewController.php @@ -131,7 +131,7 @@ final class PhortuneMerchantViewController $view->addProperty(pht('Status'), $status_view); return id(new PHUIObjectBoxView()) - ->setHeaderText(pht('DETAILS')) + ->setHeaderText(pht('Details')) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($view); } @@ -146,7 +146,7 @@ final class PhortuneMerchantViewController $description = new PHUIRemarkupView($viewer, $description); $view->addTextContent($description); return id(new PHUIObjectBoxView()) - ->setHeaderText(pht('DESCRIPTION')) + ->setHeaderText(pht('Description')) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($view); } diff --git a/src/applications/phortune/controller/PhortuneProductViewController.php b/src/applications/phortune/controller/PhortuneProductViewController.php index 0bf022e373..a434a94534 100644 --- a/src/applications/phortune/controller/PhortuneProductViewController.php +++ b/src/applications/phortune/controller/PhortuneProductViewController.php @@ -38,7 +38,7 @@ final class PhortuneProductViewController extends PhortuneController { $product->getPriceAsCurrency()->formatForDisplay()); $object_box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('DETAILS')) + ->setHeaderText(pht('Details')) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->addPropertyList($properties); diff --git a/src/applications/phortune/controller/PhortuneSubscriptionViewController.php b/src/applications/phortune/controller/PhortuneSubscriptionViewController.php index 0e1bc55b62..0aea396136 100644 --- a/src/applications/phortune/controller/PhortuneSubscriptionViewController.php +++ b/src/applications/phortune/controller/PhortuneSubscriptionViewController.php @@ -82,7 +82,7 @@ final class PhortuneSubscriptionViewController extends PhortuneController { $autopay_method); $details = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('DETAILS')) + ->setHeaderText(pht('Details')) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->addPropertyList($properties); diff --git a/src/applications/phurl/controller/PhabricatorPhurlURLViewController.php b/src/applications/phurl/controller/PhabricatorPhurlURLViewController.php index 946e35c854..4703adade5 100644 --- a/src/applications/phurl/controller/PhabricatorPhurlURLViewController.php +++ b/src/applications/phurl/controller/PhabricatorPhurlURLViewController.php @@ -145,7 +145,7 @@ final class PhabricatorPhurlURLViewController } return id(new PHUIObjectBoxView()) - ->setHeaderText(pht('DETAILS')) + ->setHeaderText(pht('Details')) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($properties); } diff --git a/src/applications/ponder/controller/PonderQuestionViewController.php b/src/applications/ponder/controller/PonderQuestionViewController.php index a1105c6c8e..529c396328 100644 --- a/src/applications/ponder/controller/PonderQuestionViewController.php +++ b/src/applications/ponder/controller/PonderQuestionViewController.php @@ -119,7 +119,7 @@ final class PonderQuestionViewController extends PonderController { ->setSubheader($subheader) ->setCurtain($curtain) ->setMainColumn($ponder_content) - ->addPropertySection(pht('DETAILS'), $details) + ->addPropertySection(pht('Details'), $details) ->addClass('ponder-question-view'); $page_objects = array_merge( diff --git a/src/applications/project/controller/PhabricatorProjectManageController.php b/src/applications/project/controller/PhabricatorProjectManageController.php index d84df87e93..c827f5abab 100644 --- a/src/applications/project/controller/PhabricatorProjectManageController.php +++ b/src/applications/project/controller/PhabricatorProjectManageController.php @@ -48,7 +48,7 @@ final class PhabricatorProjectManageController $manage = id(new PHUITwoColumnView()) ->setHeader($header) ->setCurtain($curtain) - ->addPropertySection(pht('DETAILS'), $properties) + ->addPropertySection(pht('Details'), $properties) ->setMainColumn( array( $timeline, diff --git a/src/applications/spaces/controller/PhabricatorSpacesViewController.php b/src/applications/spaces/controller/PhabricatorSpacesViewController.php index 8319f19a6e..495a0c8dee 100644 --- a/src/applications/spaces/controller/PhabricatorSpacesViewController.php +++ b/src/applications/spaces/controller/PhabricatorSpacesViewController.php @@ -45,7 +45,7 @@ final class PhabricatorSpacesViewController } $box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('DETAILS')) + ->setHeaderText(pht('Details')) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->addPropertyList($property_list); From 5664c838fbbcca76e10d80d6b8e988508f2634b9 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 6 Apr 2016 14:48:44 -0700 Subject: [PATCH 71/84] Reduce thumbnail flickering in comment previews Summary: Ref T10262. Currently, we always render a tag like this when you `{F123}` an image in remarkup: ``` ``` This either generates the preview or redirects to an existing preview. This is a good behavior in general, because the preview may take a while to generate and we don't want to wait for it to generate on the server side. However, this flickers a lot in Safari. We might be able to cache this, but we really shouldn't, since the preview URI isn't a legitimately stable/permanent one. Instead, do a (cheap) server-side check to see if the preview already exists. If it does, return a direct URI. This gives us a stable thumbnail in Safari. Test Plan: - Dragged a dog picture into comment box. - Typed text. - Thing didn't flicker like crazy all the time in Safari. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10262 Differential Revision: https://secure.phabricator.com/D15646 --- src/aphront/response/AphrontResponse.php | 18 ++++---- .../PhabricatorEmbedFileRemarkupRule.php | 14 +++++- .../files/query/PhabricatorFileQuery.php | 44 +++++++++++++++++++ .../files/storage/PhabricatorFile.php | 10 +++++ 4 files changed, 77 insertions(+), 9 deletions(-) diff --git a/src/aphront/response/AphrontResponse.php b/src/aphront/response/AphrontResponse.php index 729bc5cc42..dbd60d473d 100644 --- a/src/aphront/response/AphrontResponse.php +++ b/src/aphront/response/AphrontResponse.php @@ -192,18 +192,20 @@ abstract class AphrontResponse extends Phobject { public function getCacheHeaders() { $headers = array(); if ($this->cacheable) { + $cache_control = array(); + $cache_control[] = sprintf('max-age=%d', $this->cacheable); + if ($this->canCDN) { - $headers[] = array( - 'Cache-Control', - 'public', - ); + $cache_control[] = 'public'; } else { - $headers[] = array( - 'Cache-Control', - 'private', - ); + $cache_control[] = 'private'; } + $headers[] = array( + 'Cache-Control', + implode(', ', $cache_control), + ); + $headers[] = array( 'Expires', $this->formatEpochTimestampForHTTPHeader(time() + $this->cacheable), diff --git a/src/applications/files/markup/PhabricatorEmbedFileRemarkupRule.php b/src/applications/files/markup/PhabricatorEmbedFileRemarkupRule.php index 7f57db02b1..ecb0e4fd09 100644 --- a/src/applications/files/markup/PhabricatorEmbedFileRemarkupRule.php +++ b/src/applications/files/markup/PhabricatorEmbedFileRemarkupRule.php @@ -16,6 +16,10 @@ final class PhabricatorEmbedFileRemarkupRule $objects = id(new PhabricatorFileQuery()) ->setViewer($viewer) ->withIDs($ids) + ->needTransforms( + array( + PhabricatorFileThumbnailTransform::TRANSFORM_PREVIEW, + )) ->execute(); $phids_key = self::KEY_EMBED_FILE_PHIDS; @@ -109,7 +113,15 @@ final class PhabricatorEmbedFileRemarkupRule default: $preview_key = PhabricatorFileThumbnailTransform::TRANSFORM_PREVIEW; $xform = PhabricatorFileTransform::getTransformByKey($preview_key); - $attrs['src'] = $file->getURIForTransform($xform); + + $existing_xform = $file->getTransform($preview_key); + if ($existing_xform) { + $xform_uri = $existing_xform->getCDNURI(); + } else { + $xform_uri = $file->getURIForTransform($xform); + } + + $attrs['src'] = $xform_uri; $dimensions = $xform->getTransformedDimensions($file); if ($dimensions) { diff --git a/src/applications/files/query/PhabricatorFileQuery.php b/src/applications/files/query/PhabricatorFileQuery.php index ba72156061..03446d9154 100644 --- a/src/applications/files/query/PhabricatorFileQuery.php +++ b/src/applications/files/query/PhabricatorFileQuery.php @@ -15,6 +15,7 @@ final class PhabricatorFileQuery private $maxLength; private $names; private $isPartial; + private $needTransforms; public function withIDs(array $ids) { $this->ids = $ids; @@ -117,6 +118,11 @@ final class PhabricatorFileQuery return $this; } + public function needTransforms(array $transforms) { + $this->needTransforms = $transforms; + return $this; + } + public function newResultObject() { return new PhabricatorFile(); } @@ -218,6 +224,44 @@ final class PhabricatorFileQuery return $files; } + protected function didFilterPage(array $files) { + $xform_keys = $this->needTransforms; + if ($xform_keys !== null) { + $xforms = id(new PhabricatorTransformedFile())->loadAllWhere( + 'originalPHID IN (%Ls) AND transform IN (%Ls)', + mpull($files, 'getPHID'), + $xform_keys); + + if ($xforms) { + $xfiles = id(new PhabricatorFile())->loadAllWhere( + 'phid IN (%Ls)', + mpull($xforms, 'getTransformedPHID')); + $xfiles = mpull($xfiles, null, 'getPHID'); + } + + $xform_map = array(); + foreach ($xforms as $xform) { + $xfile = idx($xfiles, $xform->getTransformedPHID()); + if (!$xfile) { + continue; + } + $original_phid = $xform->getOriginalPHID(); + $xform_key = $xform->getTransform(); + $xform_map[$original_phid][$xform_key] = $xfile; + } + + $default_xforms = array_fill_keys($xform_keys, null); + + foreach ($files as $file) { + $file_xforms = idx($xform_map, $file->getPHID(), array()); + $file_xforms += $default_xforms; + $file->attachTransforms($file_xforms); + } + } + + return $files; + } + protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { $joins = parent::buildJoinClauseParts($conn); diff --git a/src/applications/files/storage/PhabricatorFile.php b/src/applications/files/storage/PhabricatorFile.php index 5c7c805663..ff2a4d27ac 100644 --- a/src/applications/files/storage/PhabricatorFile.php +++ b/src/applications/files/storage/PhabricatorFile.php @@ -56,6 +56,7 @@ final class PhabricatorFile extends PhabricatorFileDAO private $objects = self::ATTACHABLE; private $objectPHIDs = self::ATTACHABLE; private $originalFile = self::ATTACHABLE; + private $transforms = self::ATTACHABLE; public static function initializeNewFile() { $app = id(new PhabricatorApplicationQuery()) @@ -1208,6 +1209,15 @@ final class PhabricatorFile extends PhabricatorFileDAO ->setURI($uri); } + public function attachTransforms(array $map) { + $this->transforms = $map; + return $this; + } + + public function getTransform($key) { + return $this->assertAttachedKey($this->transforms, $key); + } + /* -( PhabricatorApplicationTransactionInterface )------------------------- */ From 0650f725f1f072e2a75be65c8d25c14eec801433 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 6 Apr 2016 15:56:18 -0700 Subject: [PATCH 72/84] Fix getInterestingMoves() fatal? Summary: Fixes T10740. Probably? Test Plan: No you Reviewers: chad Reviewed By: chad Maniphest Tasks: T10740 Differential Revision: https://secure.phabricator.com/D15648 --- .../transactions/storage/PhabricatorApplicationTransaction.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php index 0e533cfa40..571a913f74 100644 --- a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php +++ b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php @@ -561,6 +561,7 @@ abstract class PhabricatorApplicationTransaction if ($field) { return $field->shouldHideInApplicationTransactions($this); } + break; case PhabricatorTransactions::TYPE_COLUMNS: return !$this->getInterestingMoves($this->getNewValue()); case PhabricatorTransactions::TYPE_EDGE: From 8f67d59d2865addbb0cec4c452f1ec5605723833 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Wed, 6 Apr 2016 23:08:20 +0000 Subject: [PATCH 73/84] Bump font size on property headers Summary: Bumps to 14px, fixes some on Differential Test Plan: view various headers in Differential Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15647 --- resources/celerity/map.php | 6 +++--- .../controller/DifferentialRevisionEditController.php | 4 ++-- .../controller/DifferentialRevisionViewController.php | 2 +- webroot/rsrc/css/phui/phui-box.css | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 1c4d121db1..8a6fee9f62 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -7,7 +7,7 @@ */ return array( 'names' => array( - 'core.pkg.css' => '97b752c8', + 'core.pkg.css' => '03a2a623', 'core.pkg.js' => 'e5484f37', 'darkconsole.pkg.js' => 'e7393ebb', 'differential.pkg.css' => '7ba78475', @@ -123,7 +123,7 @@ return array( 'rsrc/css/phui/phui-action-panel.css' => '91c7b835', 'rsrc/css/phui/phui-badge.css' => 'f25c3476', 'rsrc/css/phui/phui-big-info-view.css' => 'bd903741', - 'rsrc/css/phui/phui-box.css' => '9c9159a7', + 'rsrc/css/phui/phui-box.css' => 'd909ea3d', 'rsrc/css/phui/phui-button.css' => 'a64a8de6', 'rsrc/css/phui/phui-chart.css' => '6bf6f78e', 'rsrc/css/phui/phui-crumbs-view.css' => '79d536e5', @@ -805,7 +805,7 @@ return array( 'phui-action-panel-css' => '91c7b835', 'phui-badge-view-css' => 'f25c3476', 'phui-big-info-view-css' => 'bd903741', - 'phui-box-css' => '9c9159a7', + 'phui-box-css' => 'd909ea3d', 'phui-button-css' => 'a64a8de6', 'phui-calendar-css' => 'ccabe893', 'phui-calendar-day-css' => 'd1cf6f93', diff --git a/src/applications/differential/controller/DifferentialRevisionEditController.php b/src/applications/differential/controller/DifferentialRevisionEditController.php index a21e1b2592..eb3aa08521 100644 --- a/src/applications/differential/controller/DifferentialRevisionEditController.php +++ b/src/applications/differential/controller/DifferentialRevisionEditController.php @@ -172,13 +172,13 @@ final class DifferentialRevisionEditController if ($revision->getID()) { if ($diff) { $header_icon = 'fa-upload'; - $title = pht('Update Differential Revision'); + $title = pht('Update Revision'); $crumbs->addTextCrumb( 'D'.$revision->getID(), '/differential/diff/'.$diff->getID().'/'); } else { $header_icon = 'fa-pencil'; - $title = pht('Edit Differential Revision'); + $title = pht('Edit Revision: %s', $revision->getTitle()); $crumbs->addTextCrumb( 'D'.$revision->getID(), '/D'.$revision->getID()); diff --git a/src/applications/differential/controller/DifferentialRevisionViewController.php b/src/applications/differential/controller/DifferentialRevisionViewController.php index 78bcd667b2..14dc99cc80 100644 --- a/src/applications/differential/controller/DifferentialRevisionViewController.php +++ b/src/applications/differential/controller/DifferentialRevisionViewController.php @@ -1033,7 +1033,7 @@ final class DifferentialRevisionViewController extends DifferentialController { } $box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('DIFF DETAIL')) + ->setHeaderText(pht('Diff Detail')) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setUser($viewer); diff --git a/webroot/rsrc/css/phui/phui-box.css b/webroot/rsrc/css/phui/phui-box.css index 30328d0d38..7fed4a535d 100644 --- a/webroot/rsrc/css/phui/phui-box.css +++ b/webroot/rsrc/css/phui/phui-box.css @@ -93,7 +93,7 @@ } .phui-box.phui-box-blue-property .phui-header-header { - font-size: 13px; + font-size: {$biggerfontsize}; color: {$bluetext}; } From 39dfcf4c899a735c6e4adea97d44ff28414f9599 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 6 Apr 2016 16:05:38 -0700 Subject: [PATCH 74/84] Provide nicer string for trying to move a task to its current columns Summary: Ref T6027. We got a not-very-user-friendly default string before. Test Plan: Selected "Move", didn't change the dropdown, hit submit. Now, got a nice human-readable description of the issue. Reviewers: chad Reviewed By: chad Maniphest Tasks: T6027 Differential Revision: https://secure.phabricator.com/D15649 --- .../storage/PhabricatorApplicationTransaction.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php index 571a913f74..e3fe7707f6 100644 --- a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php +++ b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php @@ -727,6 +727,10 @@ abstract class PhabricatorApplicationTransaction return pht('This object is already in that space.'); case PhabricatorTransactions::TYPE_EDGE: return pht('Edges already exist; transaction has no effect.'); + case PhabricatorTransactions::TYPE_COLUMNS: + return pht( + 'You have not moved this object to any columns it is not '. + 'already in.'); } return pht( From 5938d768d6ba6a76bd7f9f417adaa1077cad159d Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 6 Apr 2016 16:19:03 -0700 Subject: [PATCH 75/84] Don't dead-end users with out-of-date links to files Summary: Ref T10262. Instead of dumping an unhelpful 403 "ACCESS DENIED" page on users, explain the most likely cause of the issue and give them a link to return to the file detail page to learn more or get an up-to-date link. Test Plan: Hit both errors, had a lovely experience with the helpful dialog text. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10262 Differential Revision: https://secure.phabricator.com/D15650 --- .../PhabricatorFileDataController.php | 38 +++++++++++++++---- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/src/applications/files/controller/PhabricatorFileDataController.php b/src/applications/files/controller/PhabricatorFileDataController.php index fc0f2114e9..e7ff2827e5 100644 --- a/src/applications/files/controller/PhabricatorFileDataController.php +++ b/src/applications/files/controller/PhabricatorFileDataController.php @@ -119,22 +119,46 @@ final class PhabricatorFileDataController extends PhabricatorFileController { return new Aphront404Response(); } + // We may be on the CDN domain, so we need to use a fully-qualified URI + // here to make sure we end up back on the main domain. + $info_uri = PhabricatorEnv::getURI($file->getInfoURI()); + + if (!$file->validateSecretKey($this->key)) { - return new Aphront403Response(); + $dialog = $this->newDialog() + ->setTitle(pht('Invalid Authorization')) + ->appendParagraph( + pht( + 'The link you followed to access this file is no longer '. + 'valid. The visibility of the file may have changed after '. + 'the link was generated.')) + ->appendParagraph( + pht( + 'You can continue to the file detail page to get more '. + 'information and attempt to access the file.')) + ->addCancelButton($info_uri, pht('Continue')); + + return id(new AphrontDialogResponse()) + ->setDialog($dialog) + ->setHTTPResponseCode(404); } if ($file->getIsPartial()) { - // We may be on the CDN domain, so we need to use a fully-qualified URI - // here to make sure we end up back on the main domain. - $info_uri = PhabricatorEnv::getURI($file->getInfoURI()); - - return $this->newDialog() + $dialog = $this->newDialog() ->setTitle(pht('Partial Upload')) ->appendParagraph( pht( 'This file has only been partially uploaded. It must be '. 'uploaded completely before you can download it.')) - ->addCancelButton($info_uri); + ->appendParagraph( + pht( + 'You can continue to the file detail page to monitor the '. + 'upload progress of the file.')) + ->addCancelButton($info_uri, pht('Continue')); + + return id(new AphrontDialogResponse()) + ->setDialog($dialog) + ->setHTTPResponseCode(404); } $this->file = $file; From 437ff2a718143e68e9b16cf86640fd492c8dca0a Mon Sep 17 00:00:00 2001 From: Chad Little Date: Wed, 6 Apr 2016 17:30:09 -0700 Subject: [PATCH 76/84] Normalize case on active operations Summary: Found another bouncing around. Test Plan: Review in diff Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15651 --- .../controller/DifferentialRevisionViewController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/differential/controller/DifferentialRevisionViewController.php b/src/applications/differential/controller/DifferentialRevisionViewController.php index 14dc99cc80..ef333a4197 100644 --- a/src/applications/differential/controller/DifferentialRevisionViewController.php +++ b/src/applications/differential/controller/DifferentialRevisionViewController.php @@ -1117,7 +1117,7 @@ final class DifferentialRevisionViewController extends DifferentialController { } $box_view = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('ACTIVE OPERATIONS')) + ->setHeaderText(pht('Active Operations')) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY); return id(new DrydockRepositoryOperationStatusView()) From 8d6488f29071750264dac48225ad4aec7f80bb44 Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 7 Apr 2016 03:53:42 -0700 Subject: [PATCH 77/84] Fix a typo in `bin/repository help update` Summary: Fixes T10741. The workflow is `refs`, not `ref`. Test Plan: o.O Reviewers: chad, cspeckmim Reviewed By: cspeckmim Maniphest Tasks: T10741 Differential Revision: https://secure.phabricator.com/D15652 --- .../PhabricatorRepositoryManagementUpdateWorkflow.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementUpdateWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementUpdateWorkflow.php index a754f3c3d4..9d987636b6 100644 --- a/src/applications/repository/management/PhabricatorRepositoryManagementUpdateWorkflow.php +++ b/src/applications/repository/management/PhabricatorRepositoryManagementUpdateWorkflow.php @@ -21,7 +21,7 @@ final class PhabricatorRepositoryManagementUpdateWorkflow ->setSynopsis( pht( 'Update __repository__. This performs the __pull__, __discover__, '. - '__ref__ and __mirror__ operations and is primarily an internal '. + '__refs__ and __mirror__ operations and is primarily an internal '. 'workflow.')) ->setArguments( array( From 27104b57c827d2234a90dcee868c5626a1e690c8 Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 7 Apr 2016 05:10:55 -0700 Subject: [PATCH 78/84] Account for raw limits properly in CalendarEventQuery Summary: Fixes T8613. This was pretty straightforward, I just never dug into it originally. `rawResultLimit = 0` just means "no limit", so the fix is to only apply a limit if it is set to some nonzero value. Also modernize a few pieces of code. Test Plan: I'm actually not sure this can actually be hit normally? I faked `setGenerateGhosts(true)` into an unrelated query, hit the fatal, then fixed it. Reviewers: lpriestley, chad Reviewed By: chad Maniphest Tasks: T8613 Differential Revision: https://secure.phabricator.com/D15653 --- .../query/PhabricatorCalendarEventQuery.php | 76 ++++++++++--------- .../people/query/PhabricatorPeopleQuery.php | 4 + 2 files changed, 43 insertions(+), 37 deletions(-) diff --git a/src/applications/calendar/query/PhabricatorCalendarEventQuery.php b/src/applications/calendar/query/PhabricatorCalendarEventQuery.php index 06416e43a1..51cd0ea25e 100644 --- a/src/applications/calendar/query/PhabricatorCalendarEventQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarEventQuery.php @@ -90,24 +90,11 @@ final class PhabricatorCalendarEventQuery } protected function loadPage() { - $table = new PhabricatorCalendarEvent(); - $conn_r = $table->establishConnection('r'); + $events = $this->loadStandardPage($this->newResultObject()); + $viewer = $this->getViewer(); - - $data = queryfx_all( - $conn_r, - 'SELECT event.* FROM %T event %Q %Q %Q %Q %Q', - $table->getTableName(), - $this->buildJoinClause($conn_r), - $this->buildWhereClause($conn_r), - $this->buildGroupClause($conn_r), - $this->buildOrderClause($conn_r), - $this->buildLimitClause($conn_r)); - - $events = $table->loadAllFromArray($data); - foreach ($events as $event) { - $event->applyViewerTimezone($this->getViewer()); + $event->applyViewerTimezone($viewer); } if (!$this->generateGhosts) { @@ -115,6 +102,15 @@ final class PhabricatorCalendarEventQuery } $enforced_end = null; + $raw_limit = $this->getRawResultLimit(); + + if (!$raw_limit && !$this->rangeEnd) { + throw new Exception( + pht( + 'Event queries which generate ghost events must include either a '. + 'result limit or an end date, because they may otherwise generate '. + 'an infinite number of results. This query has neither.')); + } foreach ($events as $key => $event) { $sequence_start = 0; @@ -176,12 +172,12 @@ final class PhabricatorCalendarEventQuery $sequence_end++; $datetime->modify($modify_key); $date = $datetime->format('U'); - if ($sequence_end > $this->getRawResultLimit() + $sequence_start) { + if ($sequence_end > $raw_limit + $sequence_start) { break; } } } else { - $sequence_end = $this->getRawResultLimit() + $sequence_start; + $sequence_end = $raw_limit + $sequence_start; } $sequence_start = max(1, $sequence_start); @@ -190,10 +186,17 @@ final class PhabricatorCalendarEventQuery $events[] = $event->generateNthGhost($index, $viewer); } - if (count($events) >= $this->getRawResultLimit()) { - $events = msort($events, 'getDateFrom'); - $events = array_slice($events, 0, $this->getRawResultLimit(), true); - $enforced_end = last($events)->getDateFrom(); + // NOTE: We're slicing results every time because this makes it cheaper + // to generate future ghosts. If we already have 100 events that occur + // before July 1, we know we never need to generate ghosts after that + // because they couldn't possibly ever appear in the result set. + + if ($raw_limit) { + if (count($events) >= $raw_limit) { + $events = msort($events, 'getDateFrom'); + $events = array_slice($events, 0, $raw_limit, true); + $enforced_end = last($events)->getDateFrom(); + } } } } @@ -251,61 +254,61 @@ final class PhabricatorCalendarEventQuery return $parts; } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { - $where = array(); + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { + $where = parent::buildWhereClauseParts($conn); if ($this->ids) { $where[] = qsprintf( - $conn_r, + $conn, 'event.id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( - $conn_r, + $conn, 'event.phid IN (%Ls)', $this->phids); } if ($this->rangeBegin) { $where[] = qsprintf( - $conn_r, + $conn, 'event.dateTo >= %d OR event.isRecurring = 1', $this->rangeBegin); } if ($this->rangeEnd) { $where[] = qsprintf( - $conn_r, + $conn, 'event.dateFrom <= %d', $this->rangeEnd); } if ($this->inviteePHIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'invitee.inviteePHID IN (%Ls)', $this->inviteePHIDs); } if ($this->creatorPHIDs) { $where[] = qsprintf( - $conn_r, + $conn, 'event.userPHID IN (%Ls)', $this->creatorPHIDs); } if ($this->isCancelled !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'event.isCancelled = %d', (int)$this->isCancelled); } if ($this->eventsWithNoParent == true) { $where[] = qsprintf( - $conn_r, + $conn, 'event.instanceOfEventPHID IS NULL'); } @@ -314,20 +317,19 @@ final class PhabricatorCalendarEventQuery foreach ($this->instanceSequencePairs as $pair) { $sql[] = qsprintf( - $conn_r, + $conn, '(event.instanceOfEventPHID = %s AND event.sequenceIndex = %d)', $pair[0], $pair[1]); } + $where[] = qsprintf( - $conn_r, + $conn, '%Q', implode(' OR ', $sql)); } - $where[] = $this->buildPagingClause($conn_r); - - return $this->formatWhereClause($where); + return $where; } protected function getPrimaryTableAlias() { diff --git a/src/applications/people/query/PhabricatorPeopleQuery.php b/src/applications/people/query/PhabricatorPeopleQuery.php index 0bdb9fab2d..77feeb313a 100644 --- a/src/applications/people/query/PhabricatorPeopleQuery.php +++ b/src/applications/people/query/PhabricatorPeopleQuery.php @@ -412,6 +412,10 @@ final class PhabricatorPeopleQuery $min_range = PhabricatorTime::getNow(); $max_range = $min_range + phutil_units('72 hours in seconds'); + // NOTE: We don't need to generate ghosts here, because we only care if + // the user is attending, and you can't attend a ghost event: RSVP'ing + // to it creates a real event. + $events = id(new PhabricatorCalendarEventQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) ->withInvitedPHIDs(array_keys($rebuild)) From 37b93f426225280f24fd2aaf2109576a19896398 Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 7 Apr 2016 08:45:56 -0700 Subject: [PATCH 79/84] Don't require POST to download LFS files from main domain Summary: Ref T7789. If you don't have `security.alternate-file-domain` configured, we won't serve binary files over GET. This is a security measure intended to prevent `` attacks and similar, where you upload some "dangerous" binary, include it in another page, and it gets some of the host's permissions because Java/Flash security models are (or were, in the past) goofy. Allow them to be served over GET if the client is Git LFS. This is safe; these attacks can't add arbitrary HTTP headers. Test Plan: Fetched files over GET with and without the LFS header. ``` $ curl -v http://local.phacility.com/file/data/@local/jfht2cxjazi5cmjomfhl/PHID-FILE-sa7mh2pfaocz2adiimeh/netgear_rma.pdf > /dev/null ... HTTP 302 Redirect ... ``` ``` $ curl -v -H 'X-Phabricator-Request-Type: git-lfs' http://localcontent.phacility.com/file/data/@local/jfht2cxjazi5cmjomfhl/PHID-FILE-sa7mh2pfaocz2adiimeh/netgear_rma.pdf > /dev/null ... HTTP 200 Content ... ``` Reviewers: chad Reviewed By: chad Maniphest Tasks: T7789 Differential Revision: https://secure.phabricator.com/D15654 --- .../diffusion/controller/DiffusionServeController.php | 1 + .../files/controller/PhabricatorFileDataController.php | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/applications/diffusion/controller/DiffusionServeController.php b/src/applications/diffusion/controller/DiffusionServeController.php index fab2bcca96..a5871ca074 100644 --- a/src/applications/diffusion/controller/DiffusionServeController.php +++ b/src/applications/diffusion/controller/DiffusionServeController.php @@ -996,6 +996,7 @@ final class DiffusionServeController extends DiffusionController { 'href' => $get_uri, 'header' => array( 'Authorization' => $no_authorization, + 'X-Phabricator-Request-Type' => 'git-lfs', ), ); } else { diff --git a/src/applications/files/controller/PhabricatorFileDataController.php b/src/applications/files/controller/PhabricatorFileDataController.php index e7ff2827e5..dfae99d201 100644 --- a/src/applications/files/controller/PhabricatorFileDataController.php +++ b/src/applications/files/controller/PhabricatorFileDataController.php @@ -82,10 +82,13 @@ final class PhabricatorFileDataController extends PhabricatorFileController { $is_viewable = $file->isViewableInBrowser(); $force_download = $request->getExists('download'); + $request_type = $request->getHTTPHeader('X-Phabricator-Request-Type'); + $is_lfs = ($request_type == 'git-lfs'); + if ($is_viewable && !$force_download) { $response->setMimeType($file->getViewableMimeType()); } else { - if (!$request->isHTTPPost() && !$is_alternate_domain) { + if (!$request->isHTTPPost() && !$is_alternate_domain && !$is_lfs) { // NOTE: Require POST to download files from the primary domain. We'd // rather go full-bore and do a real CSRF check, but can't currently // authenticate users on the file domain. This should blunt any From 1f423c3bd105c846ff94150142ce5360fb3189b3 Mon Sep 17 00:00:00 2001 From: lkassianik Date: Thu, 7 Apr 2016 12:12:34 -0700 Subject: [PATCH 80/84] Make badges searchable by name Summary: Closes T10690 Test Plan: Open Badges application, go to Advanced Search, search for a badge by its name and see result. Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: Korvin Maniphest Tasks: T10690 Differential Revision: https://secure.phabricator.com/D15656 --- .../autopatches/20160406.badges.ngrams.php | 11 ++++++++++ .../autopatches/20160406.badges.ngrams.sql | 7 ++++++ src/__phutil_library_map__.php | 3 +++ .../badges/editor/PhabricatorBadgesEditor.php | 4 ++++ .../badges/query/PhabricatorBadgesQuery.php | 18 +++++++++++---- .../query/PhabricatorBadgesSearchEngine.php | 22 +++++++------------ .../badges/storage/PhabricatorBadgesBadge.php | 15 +++++++++++-- .../PhabricatorBadgesBadgeNameNgrams.php | 18 +++++++++++++++ 8 files changed, 78 insertions(+), 20 deletions(-) create mode 100644 resources/sql/autopatches/20160406.badges.ngrams.php create mode 100644 resources/sql/autopatches/20160406.badges.ngrams.sql create mode 100644 src/applications/badges/storage/PhabricatorBadgesBadgeNameNgrams.php diff --git a/resources/sql/autopatches/20160406.badges.ngrams.php b/resources/sql/autopatches/20160406.badges.ngrams.php new file mode 100644 index 0000000000..ce8d8896ef --- /dev/null +++ b/resources/sql/autopatches/20160406.badges.ngrams.php @@ -0,0 +1,11 @@ +getPHID(), + array( + 'force' => true, + )); +} diff --git a/resources/sql/autopatches/20160406.badges.ngrams.sql b/resources/sql/autopatches/20160406.badges.ngrams.sql new file mode 100644 index 0000000000..14a03759c9 --- /dev/null +++ b/resources/sql/autopatches/20160406.badges.ngrams.sql @@ -0,0 +1,7 @@ +CREATE TABLE {$NAMESPACE}_badges.badges_badgename_ngrams ( + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, + objectID INT UNSIGNED NOT NULL, + ngram CHAR(3) NOT NULL COLLATE {$COLLATE_TEXT}, + KEY `key_object` (objectID), + KEY `key_ngram` (ngram, objectID) +) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 9d0150ebee..f6f7cf1865 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1874,6 +1874,7 @@ phutil_register_library_map(array( 'PhabricatorBadgesAwardController' => 'applications/badges/controller/PhabricatorBadgesAwardController.php', 'PhabricatorBadgesAwardQuery' => 'applications/badges/query/PhabricatorBadgesAwardQuery.php', 'PhabricatorBadgesBadge' => 'applications/badges/storage/PhabricatorBadgesBadge.php', + 'PhabricatorBadgesBadgeNameNgrams' => 'applications/badges/storage/PhabricatorBadgesBadgeNameNgrams.php', 'PhabricatorBadgesCommentController' => 'applications/badges/controller/PhabricatorBadgesCommentController.php', 'PhabricatorBadgesController' => 'applications/badges/controller/PhabricatorBadgesController.php', 'PhabricatorBadgesCreateCapability' => 'applications/badges/capability/PhabricatorBadgesCreateCapability.php', @@ -6253,7 +6254,9 @@ phutil_register_library_map(array( 'PhabricatorFlaggableInterface', 'PhabricatorDestructibleInterface', 'PhabricatorConduitResultInterface', + 'PhabricatorNgramsInterface', ), + 'PhabricatorBadgesBadgeNameNgrams' => 'PhabricatorSearchNgrams', 'PhabricatorBadgesCommentController' => 'PhabricatorBadgesController', 'PhabricatorBadgesController' => 'PhabricatorController', 'PhabricatorBadgesCreateCapability' => 'PhabricatorPolicyCapability', diff --git a/src/applications/badges/editor/PhabricatorBadgesEditor.php b/src/applications/badges/editor/PhabricatorBadgesEditor.php index c8dc23df3d..e71dd622c8 100644 --- a/src/applications/badges/editor/PhabricatorBadgesEditor.php +++ b/src/applications/badges/editor/PhabricatorBadgesEditor.php @@ -11,6 +11,10 @@ final class PhabricatorBadgesEditor return pht('Badges'); } + protected function supportsSearch() { + return true; + } + public function getTransactionTypes() { $types = parent::getTransactionTypes(); diff --git a/src/applications/badges/query/PhabricatorBadgesQuery.php b/src/applications/badges/query/PhabricatorBadgesQuery.php index c5d8b2dd12..b6277c5f34 100644 --- a/src/applications/badges/query/PhabricatorBadgesQuery.php +++ b/src/applications/badges/query/PhabricatorBadgesQuery.php @@ -36,6 +36,12 @@ final class PhabricatorBadgesQuery return $this; } + public function withNameNgrams($ngrams) { + return $this->withNgramsConstraint( + id(new PhabricatorBadgesBadgeNameNgrams()), + $ngrams); + } + public function needRecipients($need_recipients) { $this->needRecipients = $need_recipients; return $this; @@ -45,6 +51,10 @@ final class PhabricatorBadgesQuery return $this->loadStandardPage($this->newResultObject()); } + protected function getPrimaryTableAlias() { + return 'badges'; + } + public function newResultObject() { return new PhabricatorBadgesBadge(); } @@ -73,28 +83,28 @@ final class PhabricatorBadgesQuery if ($this->ids !== null) { $where[] = qsprintf( $conn, - 'id IN (%Ld)', + 'badges.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, - 'phid IN (%Ls)', + 'badges.phid IN (%Ls)', $this->phids); } if ($this->qualities !== null) { $where[] = qsprintf( $conn, - 'quality IN (%Ls)', + 'badges.quality IN (%Ls)', $this->qualities); } if ($this->statuses !== null) { $where[] = qsprintf( $conn, - 'status IN (%Ls)', + 'badges.status IN (%Ls)', $this->statuses); } diff --git a/src/applications/badges/query/PhabricatorBadgesSearchEngine.php b/src/applications/badges/query/PhabricatorBadgesSearchEngine.php index 025ad3b53f..fc2bf7ef1e 100644 --- a/src/applications/badges/query/PhabricatorBadgesSearchEngine.php +++ b/src/applications/badges/query/PhabricatorBadgesSearchEngine.php @@ -15,22 +15,12 @@ final class PhabricatorBadgesSearchEngine return new PhabricatorBadgesQuery(); } - public function buildSavedQueryFromRequest(AphrontRequest $request) { - $saved = new PhabricatorSavedQuery(); - - $saved->setParameter( - 'statuses', - $this->readListFromRequest($request, 'statuses')); - - $saved->setParameter( - 'qualities', - $this->readListFromRequest($request, 'qualities')); - - return $saved; - } - protected function buildCustomSearchFields() { return array( + id(new PhabricatorSearchTextField()) + ->setLabel(pht('Name Contains')) + ->setKey('name') + ->setDescription(pht('Search for badges by name substring.')), id(new PhabricatorSearchCheckboxesField()) ->setKey('qualities') ->setLabel(pht('Quality')) @@ -55,6 +45,10 @@ final class PhabricatorBadgesSearchEngine $query->withQualities($map['qualities']); } + if ($map['name'] !== null) { + $query->withNameNgrams($map['name']); + } + return $query; } diff --git a/src/applications/badges/storage/PhabricatorBadgesBadge.php b/src/applications/badges/storage/PhabricatorBadgesBadge.php index 95ceb7a9e8..91ed3cf34d 100644 --- a/src/applications/badges/storage/PhabricatorBadgesBadge.php +++ b/src/applications/badges/storage/PhabricatorBadgesBadge.php @@ -8,7 +8,8 @@ final class PhabricatorBadgesBadge extends PhabricatorBadgesDAO PhabricatorTokenReceiverInterface, PhabricatorFlaggableInterface, PhabricatorDestructibleInterface, - PhabricatorConduitResultInterface { + PhabricatorConduitResultInterface, + PhabricatorNgramsInterface { protected $name; protected $flavor; @@ -59,7 +60,7 @@ final class PhabricatorBadgesBadge extends PhabricatorBadgesDAO return array( self::CONFIG_AUX_PHID => true, self::CONFIG_COLUMN_SCHEMA => array( - 'name' => 'text255', + 'name' => 'sort255', 'flavor' => 'text255', 'description' => 'text', 'icon' => 'text255', @@ -225,4 +226,14 @@ final class PhabricatorBadgesBadge extends PhabricatorBadgesDAO return array(); } +/* -( PhabricatorNgramInterface )------------------------------------------ */ + + + public function newNgrams() { + return array( + id(new PhabricatorBadgesBadgeNameNgrams()) + ->setValue($this->getName()), + ); + } + } diff --git a/src/applications/badges/storage/PhabricatorBadgesBadgeNameNgrams.php b/src/applications/badges/storage/PhabricatorBadgesBadgeNameNgrams.php new file mode 100644 index 0000000000..c5db47f42d --- /dev/null +++ b/src/applications/badges/storage/PhabricatorBadgesBadgeNameNgrams.php @@ -0,0 +1,18 @@ + Date: Thu, 7 Apr 2016 11:06:26 -0700 Subject: [PATCH 81/84] Convert Countdown to EditEngine Summary: Fixes T10684. Fixes T10520. This primarily implements a date/epoch field, and then does a bunch of standard plumbing. Test Plan: - Created countdowns. - Edited countdowns. - Used HTTP prefilling. - Created a countdown ending on "Christmas Morning", etc. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10520, T10684 Differential Revision: https://secure.phabricator.com/D15655 --- src/__phutil_library_map__.php | 8 +- .../AphrontEpochHTTPParameterType.php | 37 ++++ .../PhabricatorCountdownApplication.php | 4 +- .../PhabricatorCountdownCommentController.php | 63 ------ .../PhabricatorCountdownController.php | 11 - .../PhabricatorCountdownEditController.php | 202 +----------------- .../PhabricatorCountdownListController.php | 10 + .../PhabricatorCountdownViewController.php | 28 +-- .../editor/PhabricatorCountdownEditEngine.php | 108 ++++++++++ .../editor/PhabricatorCountdownEditor.php | 24 ++- .../storage/PhabricatorCountdown.php | 9 +- .../PhabricatorCountdownTransaction.php | 106 +++------ .../editfield/PhabricatorEpochEditField.php | 21 ++ .../form/control/AphrontFormDateControl.php | 9 +- .../control/AphrontFormDateControlValue.php | 182 ++++++++++------ 15 files changed, 366 insertions(+), 456 deletions(-) create mode 100644 src/aphront/httpparametertype/AphrontEpochHTTPParameterType.php delete mode 100644 src/applications/countdown/controller/PhabricatorCountdownCommentController.php create mode 100644 src/applications/countdown/editor/PhabricatorCountdownEditEngine.php create mode 100644 src/applications/transactions/editfield/PhabricatorEpochEditField.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index f6f7cf1865..596901a360 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -139,6 +139,7 @@ phutil_register_library_map(array( 'AphrontDefaultApplicationConfiguration' => 'aphront/configuration/AphrontDefaultApplicationConfiguration.php', 'AphrontDialogResponse' => 'aphront/response/AphrontDialogResponse.php', 'AphrontDialogView' => 'view/AphrontDialogView.php', + 'AphrontEpochHTTPParameterType' => 'aphront/httpparametertype/AphrontEpochHTTPParameterType.php', 'AphrontException' => 'aphront/exception/AphrontException.php', 'AphrontFileResponse' => 'aphront/response/AphrontFileResponse.php', 'AphrontFormCheckboxControl' => 'view/form/control/AphrontFormCheckboxControl.php', @@ -2100,7 +2101,6 @@ phutil_register_library_map(array( 'PhabricatorCoreConfigOptions' => 'applications/config/option/PhabricatorCoreConfigOptions.php', 'PhabricatorCountdown' => 'applications/countdown/storage/PhabricatorCountdown.php', 'PhabricatorCountdownApplication' => 'applications/countdown/application/PhabricatorCountdownApplication.php', - 'PhabricatorCountdownCommentController' => 'applications/countdown/controller/PhabricatorCountdownCommentController.php', 'PhabricatorCountdownController' => 'applications/countdown/controller/PhabricatorCountdownController.php', 'PhabricatorCountdownCountdownPHIDType' => 'applications/countdown/phid/PhabricatorCountdownCountdownPHIDType.php', 'PhabricatorCountdownDAO' => 'applications/countdown/storage/PhabricatorCountdownDAO.php', @@ -2108,6 +2108,7 @@ phutil_register_library_map(array( 'PhabricatorCountdownDefaultViewCapability' => 'applications/countdown/capability/PhabricatorCountdownDefaultViewCapability.php', 'PhabricatorCountdownDeleteController' => 'applications/countdown/controller/PhabricatorCountdownDeleteController.php', 'PhabricatorCountdownEditController' => 'applications/countdown/controller/PhabricatorCountdownEditController.php', + 'PhabricatorCountdownEditEngine' => 'applications/countdown/editor/PhabricatorCountdownEditEngine.php', 'PhabricatorCountdownEditor' => 'applications/countdown/editor/PhabricatorCountdownEditor.php', 'PhabricatorCountdownListController' => 'applications/countdown/controller/PhabricatorCountdownListController.php', 'PhabricatorCountdownMailReceiver' => 'applications/countdown/mail/PhabricatorCountdownMailReceiver.php', @@ -2325,6 +2326,7 @@ phutil_register_library_map(array( 'PhabricatorEmptyQueryException' => 'infrastructure/query/PhabricatorEmptyQueryException.php', 'PhabricatorEnv' => 'infrastructure/env/PhabricatorEnv.php', 'PhabricatorEnvTestCase' => 'infrastructure/env/__tests__/PhabricatorEnvTestCase.php', + 'PhabricatorEpochEditField' => 'applications/transactions/editfield/PhabricatorEpochEditField.php', 'PhabricatorEvent' => 'infrastructure/events/PhabricatorEvent.php', 'PhabricatorEventEngine' => 'infrastructure/events/PhabricatorEventEngine.php', 'PhabricatorEventListener' => 'infrastructure/events/PhabricatorEventListener.php', @@ -4261,6 +4263,7 @@ phutil_register_library_map(array( 'AphrontView', 'AphrontResponseProducerInterface', ), + 'AphrontEpochHTTPParameterType' => 'AphrontHTTPParameterType', 'AphrontException' => 'Exception', 'AphrontFileResponse' => 'AphrontResponse', 'AphrontFormCheckboxControl' => 'AphrontFormControl', @@ -6525,7 +6528,6 @@ phutil_register_library_map(array( 'PhabricatorProjectInterface', ), 'PhabricatorCountdownApplication' => 'PhabricatorApplication', - 'PhabricatorCountdownCommentController' => 'PhabricatorCountdownController', 'PhabricatorCountdownController' => 'PhabricatorController', 'PhabricatorCountdownCountdownPHIDType' => 'PhabricatorPHIDType', 'PhabricatorCountdownDAO' => 'PhabricatorLiskDAO', @@ -6533,6 +6535,7 @@ phutil_register_library_map(array( 'PhabricatorCountdownDefaultViewCapability' => 'PhabricatorPolicyCapability', 'PhabricatorCountdownDeleteController' => 'PhabricatorCountdownController', 'PhabricatorCountdownEditController' => 'PhabricatorCountdownController', + 'PhabricatorCountdownEditEngine' => 'PhabricatorEditEngine', 'PhabricatorCountdownEditor' => 'PhabricatorApplicationTransactionEditor', 'PhabricatorCountdownListController' => 'PhabricatorCountdownController', 'PhabricatorCountdownMailReceiver' => 'PhabricatorObjectMailReceiver', @@ -6776,6 +6779,7 @@ phutil_register_library_map(array( 'PhabricatorEmptyQueryException' => 'Exception', 'PhabricatorEnv' => 'Phobject', 'PhabricatorEnvTestCase' => 'PhabricatorTestCase', + 'PhabricatorEpochEditField' => 'PhabricatorEditField', 'PhabricatorEvent' => 'PhutilEvent', 'PhabricatorEventEngine' => 'Phobject', 'PhabricatorEventListener' => 'PhutilEventListener', diff --git a/src/aphront/httpparametertype/AphrontEpochHTTPParameterType.php b/src/aphront/httpparametertype/AphrontEpochHTTPParameterType.php new file mode 100644 index 0000000000..f1932bd872 --- /dev/null +++ b/src/aphront/httpparametertype/AphrontEpochHTTPParameterType.php @@ -0,0 +1,37 @@ +getExists($key) || + $request->getExists($key.'_d'); + } + + protected function getParameterValue(AphrontRequest $request, $key) { + return AphrontFormDateControlValue::newFromRequest($request, $key); + } + + protected function getParameterTypeName() { + return 'epoch'; + } + + protected function getParameterFormatDescriptions() { + return array( + pht('An epoch timestamp, as an integer.'), + pht('An absolute date, as a string.'), + pht('A relative date, as a string.'), + pht('Separate date and time inputs, as strings.'), + ); + } + + protected function getParameterExamples() { + return array( + 'v=1460050737', + 'v=2022-01-01', + 'v=yesterday', + 'v_d=2022-01-01&v_t=12:34', + ); + } + +} diff --git a/src/applications/countdown/application/PhabricatorCountdownApplication.php b/src/applications/countdown/application/PhabricatorCountdownApplication.php index d6c62d7e1c..a446c88a88 100644 --- a/src/applications/countdown/application/PhabricatorCountdownApplication.php +++ b/src/applications/countdown/application/PhabricatorCountdownApplication.php @@ -46,9 +46,7 @@ final class PhabricatorCountdownApplication extends PhabricatorApplication { => 'PhabricatorCountdownViewController', 'comment/(?P[1-9]\d*)/' => 'PhabricatorCountdownCommentController', - 'edit/(?:(?P[1-9]\d*)/)?' - => 'PhabricatorCountdownEditController', - 'create/' + $this->getEditRoutePattern('edit/') => 'PhabricatorCountdownEditController', 'delete/(?P[1-9]\d*)/' => 'PhabricatorCountdownDeleteController', diff --git a/src/applications/countdown/controller/PhabricatorCountdownCommentController.php b/src/applications/countdown/controller/PhabricatorCountdownCommentController.php deleted file mode 100644 index 03b2001289..0000000000 --- a/src/applications/countdown/controller/PhabricatorCountdownCommentController.php +++ /dev/null @@ -1,63 +0,0 @@ -getViewer(); - $id = $request->getURIData('id'); - - if (!$request->isFormPost()) { - return new Aphront400Response(); - } - - $countdown = id(new PhabricatorCountdownQuery()) - ->setViewer($viewer) - ->withIDs(array($id)) - ->executeOne(); - if (!$countdown) { - return new Aphront404Response(); - } - - $is_preview = $request->isPreviewRequest(); - $draft = PhabricatorDraft::buildFromRequest($request); - - $view_uri = '/'.$countdown->getMonogram(); - - $xactions = array(); - $xactions[] = id(new PhabricatorCountdownTransaction()) - ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) - ->attachComment( - id(new PhabricatorCountdownTransactionComment()) - ->setContent($request->getStr('comment'))); - - $editor = id(new PhabricatorCountdownEditor()) - ->setActor($viewer) - ->setContinueOnNoEffect($request->isContinueRequest()) - ->setContentSourceFromRequest($request) - ->setIsPreview($is_preview); - - try { - $xactions = $editor->applyTransactions($countdown, $xactions); - } catch (PhabricatorApplicationTransactionNoEffectException $ex) { - return id(new PhabricatorApplicationTransactionNoEffectResponse()) - ->setCancelURI($view_uri) - ->setException($ex); - } - - if ($draft) { - $draft->replaceOrDelete(); - } - - if ($request->isAjax() && $is_preview) { - return id(new PhabricatorApplicationTransactionResponse()) - ->setViewer($viewer) - ->setTransactions($xactions) - ->setIsPreview($is_preview); - } else { - return id(new AphrontRedirectResponse()) - ->setURI($view_uri); - } - } - -} diff --git a/src/applications/countdown/controller/PhabricatorCountdownController.php b/src/applications/countdown/controller/PhabricatorCountdownController.php index 37b0e49a68..4d09d7ed5b 100644 --- a/src/applications/countdown/controller/PhabricatorCountdownController.php +++ b/src/applications/countdown/controller/PhabricatorCountdownController.php @@ -7,16 +7,5 @@ abstract class PhabricatorCountdownController extends PhabricatorController { ->setSearchEngine(new PhabricatorCountdownSearchEngine()); } - protected function buildApplicationCrumbs() { - $crumbs = parent::buildApplicationCrumbs(); - - $crumbs->addAction( - id(new PHUIListItemView()) - ->setName(pht('Create Countdown')) - ->setHref($this->getApplicationURI('create/')) - ->setIcon('fa-plus-square')); - - return $crumbs; - } } diff --git a/src/applications/countdown/controller/PhabricatorCountdownEditController.php b/src/applications/countdown/controller/PhabricatorCountdownEditController.php index ea89c1591b..7bdd4236b7 100644 --- a/src/applications/countdown/controller/PhabricatorCountdownEditController.php +++ b/src/applications/countdown/controller/PhabricatorCountdownEditController.php @@ -4,205 +4,9 @@ final class PhabricatorCountdownEditController extends PhabricatorCountdownController { public function handleRequest(AphrontRequest $request) { - $viewer = $request->getViewer(); - $id = $request->getURIData('id'); - - if ($id) { - $countdown = id(new PhabricatorCountdownQuery()) - ->setViewer($viewer) - ->withIDs(array($id)) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->executeOne(); - if (!$countdown) { - return new Aphront404Response(); - } - $date_value = AphrontFormDateControlValue::newFromEpoch( - $viewer, - $countdown->getEpoch()); - $v_projects = PhabricatorEdgeQuery::loadDestinationPHIDs( - $countdown->getPHID(), - PhabricatorProjectObjectHasProjectEdgeType::EDGECONST); - $v_projects = array_reverse($v_projects); - $title = pht('Edit Countdown: %s', $countdown->getTitle()); - } else { - $title = pht('Create Countdown'); - $countdown = PhabricatorCountdown::initializeNewCountdown($viewer); - $date_value = AphrontFormDateControlValue::newFromEpoch( - $viewer, PhabricatorTime::getNow()); - $v_projects = array(); - } - - $errors = array(); - $e_text = true; - $e_epoch = null; - - $v_text = $countdown->getTitle(); - $v_desc = $countdown->getDescription(); - $v_space = $countdown->getSpacePHID(); - $v_view = $countdown->getViewPolicy(); - $v_edit = $countdown->getEditPolicy(); - - if ($request->isFormPost()) { - $v_text = $request->getStr('title'); - $v_desc = $request->getStr('description'); - $v_space = $request->getStr('spacePHID'); - $date_value = AphrontFormDateControlValue::newFromRequest( - $request, - 'epoch'); - $v_view = $request->getStr('viewPolicy'); - $v_edit = $request->getStr('editPolicy'); - $v_projects = $request->getArr('projects'); - - $type_title = PhabricatorCountdownTransaction::TYPE_TITLE; - $type_epoch = PhabricatorCountdownTransaction::TYPE_EPOCH; - $type_description = PhabricatorCountdownTransaction::TYPE_DESCRIPTION; - $type_space = PhabricatorTransactions::TYPE_SPACE; - $type_view = PhabricatorTransactions::TYPE_VIEW_POLICY; - $type_edit = PhabricatorTransactions::TYPE_EDIT_POLICY; - - $xactions = array(); - - $xactions[] = id(new PhabricatorCountdownTransaction()) - ->setTransactionType($type_title) - ->setNewValue($v_text); - - $xactions[] = id(new PhabricatorCountdownTransaction()) - ->setTransactionType($type_epoch) - ->setNewValue($date_value); - - $xactions[] = id(new PhabricatorCountdownTransaction()) - ->setTransactionType($type_description) - ->setNewValue($v_desc); - - $xactions[] = id(new PhabricatorCountdownTransaction()) - ->setTransactionType($type_space) - ->setNewValue($v_space); - - $xactions[] = id(new PhabricatorCountdownTransaction()) - ->setTransactionType($type_view) - ->setNewValue($v_view); - - $xactions[] = id(new PhabricatorCountdownTransaction()) - ->setTransactionType($type_edit) - ->setNewValue($v_edit); - - $proj_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST; - $xactions[] = id(new PhabricatorCountdownTransaction()) - ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) - ->setMetadataValue('edge:type', $proj_edge_type) - ->setNewValue(array('=' => array_fuse($v_projects))); - - $editor = id(new PhabricatorCountdownEditor()) - ->setActor($viewer) - ->setContentSourceFromRequest($request) - ->setContinueOnNoEffect(true); - - try { - $editor->applyTransactions($countdown, $xactions); - - return id(new AphrontRedirectResponse()) - ->setURI('/'.$countdown->getMonogram()); - } catch (PhabricatorApplicationTransactionValidationException $ex) { - $validation_exception = $ex; - - $e_title = $ex->getShortMessage($type_title); - $e_epoch = $ex->getShortMessage($type_epoch); - } - - } - - $crumbs = $this->buildApplicationCrumbs(); - $crumbs->setBorder(true); - - $cancel_uri = '/countdown/'; - if ($countdown->getID()) { - $cancel_uri = '/countdown/'.$countdown->getID().'/'; - $crumbs->addTextCrumb('C'.$countdown->getID(), $cancel_uri); - $crumbs->addTextCrumb(pht('Edit')); - $submit_label = pht('Save Changes'); - $header_icon = 'fa-pencil'; - } else { - $crumbs->addTextCrumb(pht('Create Countdown')); - $submit_label = pht('Create Countdown'); - $header_icon = 'fa-plus-square'; - } - - $policies = id(new PhabricatorPolicyQuery()) - ->setViewer($viewer) - ->setObject($countdown) - ->execute(); - - $form = id(new AphrontFormView()) - ->setUser($viewer) - ->setAction($request->getRequestURI()->getPath()) - ->appendChild( - id(new AphrontFormTextControl()) - ->setLabel(pht('Title')) - ->setValue($v_text) - ->setName('title') - ->setError($e_text)) - ->appendControl( - id(new AphrontFormDateControl()) - ->setName('epoch') - ->setLabel(pht('End Date')) - ->setError($e_epoch) - ->setValue($date_value)) - ->appendControl( - id(new PhabricatorRemarkupControl()) - ->setName('description') - ->setLabel(pht('Description')) - ->setValue($v_desc)) - ->appendControl( - id(new AphrontFormPolicyControl()) - ->setName('viewPolicy') - ->setPolicyObject($countdown) - ->setPolicies($policies) - ->setSpacePHID($v_space) - ->setValue($v_view) - ->setCapability(PhabricatorPolicyCapability::CAN_VIEW)) - ->appendControl( - id(new AphrontFormPolicyControl()) - ->setName('editPolicy') - ->setPolicyObject($countdown) - ->setPolicies($policies) - ->setValue($v_edit) - ->setCapability(PhabricatorPolicyCapability::CAN_EDIT)) - ->appendControl( - id(new AphrontFormTokenizerControl()) - ->setLabel(pht('Projects')) - ->setName('projects') - ->setValue($v_projects) - ->setDatasource(new PhabricatorProjectDatasource())) - ->appendChild( - id(new AphrontFormSubmitControl()) - ->addCancelButton($cancel_uri) - ->setValue($submit_label)); - - $form_box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Countdown')) - ->setFormErrors($errors) - ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) - ->setForm($form); - - $header = id(new PHUIHeaderView()) - ->setHeader($title) - ->setHeaderIcon($header_icon); - - $view = id(new PHUITwoColumnView()) - ->setHeader($header) - ->setFooter($form_box); - - return $this->newPage() - ->setTitle($title) - ->setCrumbs($crumbs) - ->appendChild( - array( - $view, - )); + return id(new PhabricatorCountdownEditEngine()) + ->setController($this) + ->buildResponse(); } } diff --git a/src/applications/countdown/controller/PhabricatorCountdownListController.php b/src/applications/countdown/controller/PhabricatorCountdownListController.php index 382f1a0306..b06cfd0e7f 100644 --- a/src/applications/countdown/controller/PhabricatorCountdownListController.php +++ b/src/applications/countdown/controller/PhabricatorCountdownListController.php @@ -13,4 +13,14 @@ final class PhabricatorCountdownListController ->buildResponse(); } + protected function buildApplicationCrumbs() { + $crumbs = parent::buildApplicationCrumbs(); + + id(new PhabricatorCountdownEditEngine()) + ->setViewer($this->getViewer()) + ->addActionToCrumbs($crumbs); + + return $crumbs; + } + } diff --git a/src/applications/countdown/controller/PhabricatorCountdownViewController.php b/src/applications/countdown/controller/PhabricatorCountdownViewController.php index 6e259df555..56911bf43b 100644 --- a/src/applications/countdown/controller/PhabricatorCountdownViewController.php +++ b/src/applications/countdown/controller/PhabricatorCountdownViewController.php @@ -55,12 +55,15 @@ final class PhabricatorCountdownViewController $timeline = $this->buildTransactionTimeline( $countdown, new PhabricatorCountdownTransactionQuery()); - $add_comment = $this->buildCommentForm($countdown); + + $comment_view = id(new PhabricatorCountdownEditEngine()) + ->setViewer($viewer) + ->buildEditEngineCommentView($countdown); $content = array( $countdown_view, $timeline, - $add_comment, + $comment_view, ); $view = id(new PHUITwoColumnView()) @@ -135,25 +138,4 @@ final class PhabricatorCountdownViewController ->setContent($content); } - private function buildCommentForm(PhabricatorCountdown $countdown) { - $viewer = $this->getViewer(); - - $is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business'); - - $add_comment_header = $is_serious - ? pht('Add Comment') - : pht('Last Words'); - - $draft = PhabricatorDraft::newFromUserAndKey( - $viewer, $countdown->getPHID()); - - return id(new PhabricatorApplicationTransactionCommentView()) - ->setUser($viewer) - ->setObjectPHID($countdown->getPHID()) - ->setDraft($draft) - ->setHeaderText($add_comment_header) - ->setAction($this->getApplicationURI('/comment/'.$countdown->getID().'/')) - ->setSubmitButtonName(pht('Add Comment')); - } - } diff --git a/src/applications/countdown/editor/PhabricatorCountdownEditEngine.php b/src/applications/countdown/editor/PhabricatorCountdownEditEngine.php new file mode 100644 index 0000000000..c1d5f6753a --- /dev/null +++ b/src/applications/countdown/editor/PhabricatorCountdownEditEngine.php @@ -0,0 +1,108 @@ +getViewer()); + } + + protected function newObjectQuery() { + return id(new PhabricatorCountdownQuery()); + } + + protected function getObjectCreateTitleText($object) { + return pht('Create Countdown'); + } + + protected function getObjectCreateButtonText($object) { + return pht('Create Countdown'); + } + + protected function getObjectEditTitleText($object) { + return pht('Edit Countdown: %s', $object->getTitle()); + } + + protected function getObjectEditShortText($object) { + return pht('Edit Countdown'); + } + + protected function getObjectCreateShortText() { + return pht('Create Countdown'); + } + + protected function getObjectName() { + return pht('Countdown'); + } + + protected function getCommentViewHeaderText($object) { + return pht('Last Words'); + } + + protected function getCommentViewButtonText($object) { + return pht('Contemplate Infinity'); + } + + protected function getObjectViewURI($object) { + return $object->getURI(); + } + + protected function buildCustomEditFields($object) { + $epoch_value = $object->getEpoch(); + if ($epoch_value === null) { + $epoch_value = PhabricatorTime::getNow(); + } + + return array( + id(new PhabricatorTextEditField()) + ->setKey('name') + ->setLabel(pht('Name')) + ->setIsRequired(true) + ->setTransactionType(PhabricatorCountdownTransaction::TYPE_TITLE) + ->setDescription(pht('The countdown name.')) + ->setConduitDescription(pht('Rename the countdown.')) + ->setConduitTypeDescription(pht('New countdown name.')) + ->setValue($object->getTitle()), + id(new PhabricatorEpochEditField()) + ->setKey('epoch') + ->setLabel(pht('End Date')) + ->setTransactionType(PhabricatorCountdownTransaction::TYPE_EPOCH) + ->setDescription(pht('Date when the countdown ends.')) + ->setConduitDescription(pht('Change the end date of the countdown.')) + ->setConduitTypeDescription(pht('New countdown end date.')) + ->setValue($epoch_value), + id(new PhabricatorRemarkupEditField()) + ->setKey('description') + ->setLabel(pht('Description')) + ->setTransactionType(PhabricatorCountdownTransaction::TYPE_DESCRIPTION) + ->setDescription(pht('Description of the countdown.')) + ->setConduitDescription(pht('Change the countdown description.')) + ->setConduitTypeDescription(pht('New description.')) + ->setValue($object->getDescription()), + ); + } + +} diff --git a/src/applications/countdown/editor/PhabricatorCountdownEditor.php b/src/applications/countdown/editor/PhabricatorCountdownEditor.php index e1eddf2270..37f23f6bd6 100644 --- a/src/applications/countdown/editor/PhabricatorCountdownEditor.php +++ b/src/applications/countdown/editor/PhabricatorCountdownEditor.php @@ -120,19 +120,27 @@ final class PhabricatorCountdownEditor } break; case PhabricatorCountdownTransaction::TYPE_EPOCH: - $date_value = AphrontFormDateControlValue::newFromEpoch( - $this->requireActor(), - $object->getEpoch()); - if (!$date_value->isValid()) { + if (!$object->getEpoch() && !$xactions) { $error = new PhabricatorApplicationTransactionValidationError( $type, - pht('Invalid'), - pht('You must give the countdown a valid end date.'), - nonempty(last($xactions), null)); - + pht('Required'), + pht('You must give the countdown an end date.'), + null); $error->setIsMissingFieldError(true); $errors[] = $error; } + + foreach ($xactions as $xaction) { + $value = $xaction->getNewValue(); + if (!$value->isValid()) { + $error = new PhabricatorApplicationTransactionValidationError( + $type, + pht('Invalid'), + pht('You must give the countdown a valid end date.'), + $xaction); + $errors[] = $error; + } + } break; } diff --git a/src/applications/countdown/storage/PhabricatorCountdown.php b/src/applications/countdown/storage/PhabricatorCountdown.php index 52a395f0c2..1a985b0b83 100644 --- a/src/applications/countdown/storage/PhabricatorCountdown.php +++ b/src/applications/countdown/storage/PhabricatorCountdown.php @@ -28,10 +28,13 @@ final class PhabricatorCountdown extends PhabricatorCountdownDAO $view_policy = $app->getPolicy( PhabricatorCountdownDefaultViewCapability::CAPABILITY); + $edit_policy = $app->getPolicy( + PhabricatorCountdownDefaultEditCapability::CAPABILITY); + return id(new PhabricatorCountdown()) ->setAuthorPHID($actor->getPHID()) ->setViewPolicy($view_policy) - ->setEpoch(PhabricatorTime::getNow()) + ->setEditPolicy($edit_policy) ->setSpacePHID($actor->getDefaultSpacePHID()); } @@ -55,6 +58,10 @@ final class PhabricatorCountdown extends PhabricatorCountdownDAO return 'C'.$this->getID(); } + public function getURI() { + return '/'.$this->getMonogram(); + } + public function save() { if (!$this->getMailKey()) { $this->setMailKey(Filesystem::readRandomCharacters(20)); diff --git a/src/applications/countdown/storage/PhabricatorCountdownTransaction.php b/src/applications/countdown/storage/PhabricatorCountdownTransaction.php index 72303a369f..247466ffe1 100644 --- a/src/applications/countdown/storage/PhabricatorCountdownTransaction.php +++ b/src/applications/countdown/storage/PhabricatorCountdownTransaction.php @@ -33,42 +33,20 @@ final class PhabricatorCountdownTransaction $type = $this->getTransactionType(); switch ($type) { case self::TYPE_TITLE: - if ($old === null) { - return pht( - '%s created this countdown.', - $this->renderHandleLink($author_phid)); - } else { - return pht( - '%s renamed this countdown from "%s" to "%s".', - $this->renderHandleLink($author_phid), - $old, - $new); - } - break; + return pht( + '%s renamed this countdown from "%s" to "%s".', + $this->renderHandleLink($author_phid), + $old, + $new); case self::TYPE_DESCRIPTION: - if ($old === null) { - return pht( - '%s set the description of this countdown.', - $this->renderHandleLink($author_phid)); - } else { - return pht( - '%s edited the description of this countdown.', - $this->renderHandleLink($author_phid)); - } - break; + return pht( + '%s edited the description of this countdown.', + $this->renderHandleLink($author_phid)); case self::TYPE_EPOCH: - if ($old === null) { - return pht( - '%s set this countdown to end on %s.', - $this->renderHandleLink($author_phid), - phabricator_datetime($new, $this->getViewer())); - } else if ($old != $new) { - return pht( - '%s updated this countdown to end on %s.', - $this->renderHandleLink($author_phid), - phabricator_datetime($new, $this->getViewer())); - } - break; + return pht( + '%s updated this countdown to end on %s.', + $this->renderHandleLink($author_phid), + phabricator_datetime($new, $this->getViewer())); } return parent::getTitle(); @@ -84,47 +62,20 @@ final class PhabricatorCountdownTransaction $type = $this->getTransactionType(); switch ($type) { case self::TYPE_TITLE: - if ($old === null) { - return pht( - '%s created %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid)); - - } else { - return pht( - '%s renamed %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid)); - } - break; + return pht( + '%s renamed %s.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($object_phid)); case self::TYPE_DESCRIPTION: - if ($old === null) { - return pht( - '%s set the description of %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid)); - - } else { - return pht( - '%s edited the description of %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid)); - } - break; + return pht( + '%s edited the description of %s.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($object_phid)); case self::TYPE_EPOCH: - if ($old === null) { - return pht( - '%s set the end date of %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid)); - - } else { - return pht( - '%s edited the end date of %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid)); - } - break; + return pht( + '%s edited the end date of %s.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($object_phid)); } return parent::getTitleForFeed(); @@ -150,15 +101,6 @@ final class PhabricatorCountdownTransaction return $tags; } - public function shouldHide() { - $old = $this->getOldValue(); - switch ($this->getTransactionType()) { - case self::TYPE_DESCRIPTION: - return ($old === null); - } - return parent::shouldHide(); - } - public function hasChangeDetails() { switch ($this->getTransactionType()) { case self::TYPE_DESCRIPTION: diff --git a/src/applications/transactions/editfield/PhabricatorEpochEditField.php b/src/applications/transactions/editfield/PhabricatorEpochEditField.php new file mode 100644 index 0000000000..c5dabe6171 --- /dev/null +++ b/src/applications/transactions/editfield/PhabricatorEpochEditField.php @@ -0,0 +1,21 @@ +setViewer($this->getViewer()); + } + + protected function newHTTPParameterType() { + return new AphrontEpochHTTPParameterType(); + } + + protected function newConduitParameterType() { + // TODO: This isn't correct, but we don't have any methods which use this + // yet. + return new ConduitIntParameterType(); + } + +} diff --git a/src/view/form/control/AphrontFormDateControl.php b/src/view/form/control/AphrontFormDateControl.php index 25dec8eef0..38420748d8 100644 --- a/src/view/form/control/AphrontFormDateControl.php +++ b/src/view/form/control/AphrontFormDateControl.php @@ -130,10 +130,13 @@ final class AphrontFormDateControl extends AphrontFormControl { $date_format = $this->getDateFormat(); $timezone = $this->getTimezone(); - $datetime = new DateTime($this->valueDate, $timezone); - $date = $datetime->format($date_format); + try { + $datetime = new DateTime($this->valueDate, $timezone); + } catch (Exception $ex) { + return $this->valueDate; + } - return $date; + return $datetime->format($date_format); } private function getTimeFormat() { diff --git a/src/view/form/control/AphrontFormDateControlValue.php b/src/view/form/control/AphrontFormDateControlValue.php index f533bcaddb..114340006e 100644 --- a/src/view/form/control/AphrontFormDateControlValue.php +++ b/src/view/form/control/AphrontFormDateControlValue.php @@ -84,10 +84,33 @@ final class AphrontFormDateControlValue extends Phobject { $value = new AphrontFormDateControlValue(); $value->viewer = $request->getViewer(); - list($value->valueDate, $value->valueTime) = - $value->getFormattedDateFromDate( - $request->getStr($key.'_d'), - $request->getStr($key.'_t')); + $datetime = $request->getStr($key); + if (strlen($datetime)) { + $date = $datetime; + $time = null; + } else { + $date = $request->getStr($key.'_d'); + $time = $request->getStr($key.'_t'); + } + + // If this looks like an epoch timestamp, prefix it with "@" so that + // DateTime() reads it as one. Assume small numbers are a "Ymd" digit + // string instead of an epoch timestamp for a time in 1970. + if (ctype_digit($date) && ($date > 30000000)) { + $date = '@'.$date; + $time = null; + } + + $value->valueDate = $date; + $value->valueTime = $time; + + $formatted = $value->getFormattedDateFromDate( + $value->valueDate, + $value->valueTime); + + if ($formatted) { + list($value->valueDate, $value->valueTime) = $formatted; + } $value->valueEnabled = $request->getStr($key.'_e'); return $value; @@ -96,6 +119,11 @@ final class AphrontFormDateControlValue extends Phobject { public static function newFromEpoch(PhabricatorUser $viewer, $epoch) { $value = new AphrontFormDateControlValue(); $value->viewer = $viewer; + + if (!$epoch) { + return $value; + } + $readable = $value->formatTime($epoch, 'Y!m!d!g:i A'); $readable = explode('!', $readable, 4); @@ -120,10 +148,16 @@ final class AphrontFormDateControlValue extends Phobject { $value = new AphrontFormDateControlValue(); $value->viewer = $viewer; - list($value->valueDate, $value->valueTime) = - $value->getFormattedDateFromDate( - idx($dictionary, 'd'), - idx($dictionary, 't')); + $value->valueDate = idx($dictionary, 'd'); + $value->valueTime = idx($dictionary, 't'); + + $formatted = $value->getFormattedDateFromDate( + $value->valueDate, + $value->valueTime); + + if ($formatted) { + list($value->valueDate, $value->valueTime) = $formatted; + } $value->valueEnabled = idx($dictionary, 'e'); @@ -170,37 +204,12 @@ final class AphrontFormDateControlValue extends Phobject { return null; } - $date = $this->valueDate; - $time = $this->valueTime; - $zone = $this->getTimezone(); - - if (!strlen($time)) { + $datetime = $this->newDateTime($this->valueDate, $this->valueTime); + if (!$datetime) { return null; } - $colloquial = array( - 'elevenses' => '11:00 AM', - 'morning tea' => '11:00 AM', - 'noon' => '12:00 PM', - 'high noon' => '12:00 PM', - 'lunch' => '12:00 PM', - 'tea time' => '3:00 PM', - 'witching hour' => '12:00 AM', - 'midnight' => '12:00 AM', - ); - - $normalized = phutil_utf8_strtolower($time); - if (isset($colloquial[$normalized])) { - $time = $colloquial[$normalized]; - } - - try { - $datetime = new DateTime("{$date} {$time}", $zone); - $value = $datetime->format('U'); - } catch (Exception $ex) { - $value = null; - } - return $value; + return $datetime->format('U'); } private function getTimeFormat() { @@ -214,25 +223,34 @@ final class AphrontFormDateControlValue extends Phobject { } private function getFormattedDateFromDate($date, $time) { - $original_input = $date; - $zone = $this->getTimezone(); - $separator = $this->getFormatSeparator(); - $parts = preg_split('@[,./:-]@', $date); - $date = implode($separator, $parts); - $date = id(new DateTime($date, $zone)); - - if ($date) { - $date = $date->format($this->getDateFormat()); - } else { - $date = $original_input; + $datetime = $this->newDateTime($date, $time); + if (!$datetime) { + return null; } - $date = id(new DateTime("{$date} {$time}", $zone)); - return array( - $date->format($this->getDateFormat()), - $date->format($this->getTimeFormat()), + $datetime->format($this->getDateFormat()), + $datetime->format($this->getTimeFormat()), ); + + return array($date, $time); + } + + private function newDateTime($date, $time) { + $date = $this->getStandardDateFormat($date); + $time = $this->getStandardTimeFormat($time); + try { + $datetime = new DateTime("{$date} {$time}"); + } catch (Exception $ex) { + return null; + } + + // Set the timezone explicitly because it is ignored in the constructor + // if the date is an epoch timestamp. + $zone = $this->getTimezone(); + $datetime->setTimezone($zone); + + return $datetime; } private function getFormattedDateFromParts( @@ -261,16 +279,7 @@ final class AphrontFormDateControlValue extends Phobject { } public function getDateTime() { - $epoch = $this->getEpoch(); - $date = null; - - if ($epoch) { - $zone = $this->getTimezone(); - $date = new DateTime('@'.$epoch); - $date->setTimeZone($zone); - } - - return $date; + return $this->newDateTime(); } private function getTimezone() { @@ -283,5 +292,56 @@ final class AphrontFormDateControlValue extends Phobject { return $this->zone; } + private function getStandardDateFormat($date) { + $colloquial = array( + 'newyear' => 'January 1', + 'valentine' => 'February 14', + 'pi' => 'March 14', + 'christma' => 'December 25', + ); + + // Lowercase the input, then remove punctuation, a "day" suffix, and an + // "s" if one is present. This allows all of these to match. This allows + // variations like "New Year's Day" and "New Year" to both match. + $normalized = phutil_utf8_strtolower($date); + $normalized = preg_replace('/[^a-z]/', '', $normalized); + $normalized = preg_replace('/day\z/', '', $normalized); + $normalized = preg_replace('/s\z/', '', $normalized); + + if (isset($colloquial[$normalized])) { + return $colloquial[$normalized]; + } + + $separator = $this->getFormatSeparator(); + $parts = preg_split('@[,./:-]@', $date); + return implode($separator, $parts); + } + + private function getStandardTimeFormat($time) { + $colloquial = array( + 'crack of dawn' => '5:00 AM', + 'dawn' => '6:00 AM', + 'early' => '7:00 AM', + 'morning' => '8:00 AM', + 'elevenses' => '11:00 AM', + 'morning tea' => '11:00 AM', + 'noon' => '12:00 PM', + 'high noon' => '12:00 PM', + 'lunch' => '12:00 PM', + 'afternoon' => '2:00 PM', + 'tea time' => '3:00 PM', + 'evening' => '7:00 PM', + 'late' => '11:00 PM', + 'witching hour' => '12:00 AM', + 'midnight' => '12:00 AM', + ); + + $normalized = phutil_utf8_strtolower($time); + if (isset($colloquial[$normalized])) { + $time = $colloquial[$normalized]; + } + + return $time; + } } From 0900ffe9cb3e3d9fffc67ed4ba63344f16ec6676 Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 7 Apr 2016 12:10:56 -0700 Subject: [PATCH 82/84] Support sorting countdowns by end date Summary: Fixes T5813, while I'm in here... Test Plan: Sorted stuff by end date. Reviewers: chad Reviewed By: chad Maniphest Tasks: T5813 Differential Revision: https://secure.phabricator.com/D15657 --- .../query/PhabricatorCountdownQuery.php | 31 +++++++++++++++++++ .../PhabricatorCountdownSearchEngine.php | 20 ++++++------ .../storage/PhabricatorCountdown.php | 8 +++++ 3 files changed, 48 insertions(+), 11 deletions(-) diff --git a/src/applications/countdown/query/PhabricatorCountdownQuery.php b/src/applications/countdown/query/PhabricatorCountdownQuery.php index 17fde126f8..e6c410ee49 100644 --- a/src/applications/countdown/query/PhabricatorCountdownQuery.php +++ b/src/applications/countdown/query/PhabricatorCountdownQuery.php @@ -74,4 +74,35 @@ final class PhabricatorCountdownQuery return 'PhabricatorCountdownApplication'; } + public function getBuiltinOrders() { + return array( + 'ending' => array( + 'vector' => array('-epoch', '-id'), + 'name' => pht('End Date (Past to Future)'), + ), + 'unending' => array( + 'vector' => array('epoch', 'id'), + 'name' => pht('End Date (Future to Past)'), + ), + ) + parent::getBuiltinOrders(); + } + + public function getOrderableColumns() { + return array( + 'epoch' => array( + 'table' => $this->getPrimaryTableAlias(), + 'column' => 'epoch', + 'type' => 'int', + ), + ) + parent::getOrderableColumns(); + } + + protected function getPagingValueMap($cursor, array $keys) { + $countdown = $this->loadCursorObject($cursor); + return array( + 'epoch' => $countdown->getEpoch(), + 'id' => $countdown->getID(), + ); + } + } diff --git a/src/applications/countdown/query/PhabricatorCountdownSearchEngine.php b/src/applications/countdown/query/PhabricatorCountdownSearchEngine.php index 79b329385a..93aec9a037 100644 --- a/src/applications/countdown/query/PhabricatorCountdownSearchEngine.php +++ b/src/applications/countdown/query/PhabricatorCountdownSearchEngine.php @@ -30,20 +30,18 @@ final class PhabricatorCountdownSearchEngine } protected function buildCustomSearchFields() { - return array( - id(new PhabricatorUsersSearchField()) - ->setLabel(pht('Authors')) - ->setKey('authorPHIDs') - ->setAliases(array('author', 'authors')), - - id(new PhabricatorSearchCheckboxesField()) - ->setKey('upcoming') - ->setOptions(array( + id(new PhabricatorUsersSearchField()) + ->setLabel(pht('Authors')) + ->setKey('authorPHIDs') + ->setAliases(array('author', 'authors')), + id(new PhabricatorSearchCheckboxesField()) + ->setKey('upcoming') + ->setOptions( + array( 'upcoming' => pht('Show only upcoming countdowns.'), )), - ); - + ); } protected function getURI($path) { diff --git a/src/applications/countdown/storage/PhabricatorCountdown.php b/src/applications/countdown/storage/PhabricatorCountdown.php index 1a985b0b83..ad9b1b33aa 100644 --- a/src/applications/countdown/storage/PhabricatorCountdown.php +++ b/src/applications/countdown/storage/PhabricatorCountdown.php @@ -46,6 +46,14 @@ final class PhabricatorCountdown extends PhabricatorCountdownDAO 'description' => 'text', 'mailKey' => 'bytes20', ), + self::CONFIG_KEY_SCHEMA => array( + 'key_epoch' => array( + 'columns' => array('epoch'), + ), + 'key_author' => array( + 'columns' => array('authorPHID', 'epoch'), + ), + ), ) + parent::getConfiguration(); } From 60e91d393421b2a6218d53e622152f21c2927f09 Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 8 Apr 2016 09:58:17 -0700 Subject: [PATCH 83/84] Fix an issue with passing HTTP headers through in proxied cluster requests Summary: I think this fixes the Mercurial + HTTP cluster issue. PHP adds `HTTP_` but we were not stripping it, so we would convert an `X-Whatever-Zebra` header into an `Http-X-Whatever-Zebra` header. I don't think this behavior has changed? So maybe it just never worked? Git is more popular than Mercurial and SSH is easier to configure than HTTP, so it's plausible. I'll keep a careful eye on this when it deploys. Test Plan: - Set up local service-based Mercurial repository. - Tried to clone, got similar error to cluster. - Applied patch, clean clone. Reviewers: chad Reviewed By: chad Differential Revision: https://secure.phabricator.com/D15660 --- src/aphront/AphrontRequest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/aphront/AphrontRequest.php b/src/aphront/AphrontRequest.php index f8b01eb94c..3a95b0ebda 100644 --- a/src/aphront/AphrontRequest.php +++ b/src/aphront/AphrontRequest.php @@ -756,6 +756,7 @@ final class AphrontRequest extends Phobject { foreach ($_SERVER as $key => $value) { if (preg_match('/^HTTP_/', $key)) { // Unmangle the header as best we can. + $key = substr($key, strlen('HTTP_')); $key = str_replace('_', ' ', $key); $key = strtolower($key); $key = ucwords($key); From 57e606b39575372a1fd8158fbe237ead0a848ec8 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Fri, 8 Apr 2016 22:00:38 +0000 Subject: [PATCH 84/84] Update Settings/Config UI Summary: Testing out a new 'nav' layout in Settings / Config. Spent a few days here and couldn't find much better overall. Test Plan: View each page in Settings and in Config. Save some config options. Test mobile, desktop, tablet. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D15659 --- resources/celerity/map.php | 10 +++---- .../PhabricatorConfigAllController.php | 12 +++++--- .../PhabricatorConfigCacheController.php | 11 +++---- ...abricatorConfigDatabaseIssueController.php | 12 ++++---- ...bricatorConfigDatabaseStatusController.php | 10 +++++-- .../PhabricatorConfigHistoryController.php | 12 +++++--- .../PhabricatorConfigIssueListController.php | 11 ++++--- .../PhabricatorConfigListController.php | 16 +++++----- .../PhabricatorConfigModuleController.php | 10 ++++--- .../PhabricatorConfigWelcomeController.php | 10 +++++-- .../PhabricatorSettingsMainController.php | 7 +++-- src/view/phui/PHUITwoColumnView.php | 19 +++++++++++- webroot/rsrc/css/phui/phui-crumbs-view.css | 2 +- .../rsrc/css/phui/phui-two-column-view.css | 29 +++++++++++++++++++ 14 files changed, 122 insertions(+), 49 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 8a6fee9f62..edc6372e8d 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -7,7 +7,7 @@ */ return array( 'names' => array( - 'core.pkg.css' => '03a2a623', + 'core.pkg.css' => '82cefddc', 'core.pkg.js' => 'e5484f37', 'darkconsole.pkg.js' => 'e7393ebb', 'differential.pkg.css' => '7ba78475', @@ -126,7 +126,7 @@ return array( 'rsrc/css/phui/phui-box.css' => 'd909ea3d', 'rsrc/css/phui/phui-button.css' => 'a64a8de6', 'rsrc/css/phui/phui-chart.css' => '6bf6f78e', - 'rsrc/css/phui/phui-crumbs-view.css' => '79d536e5', + 'rsrc/css/phui/phui-crumbs-view.css' => '1a1265d4', 'rsrc/css/phui/phui-curtain-view.css' => '7148ae25', 'rsrc/css/phui/phui-document-pro.css' => '73e45fd2', 'rsrc/css/phui/phui-document-summary.css' => '9ca48bdf', @@ -156,7 +156,7 @@ return array( 'rsrc/css/phui/phui-status.css' => '37309046', 'rsrc/css/phui/phui-tag-view.css' => '6bbd83e2', 'rsrc/css/phui/phui-timeline-view.css' => '6e342216', - 'rsrc/css/phui/phui-two-column-view.css' => '691fec04', + 'rsrc/css/phui/phui-two-column-view.css' => 'b9538af1', 'rsrc/css/phui/workboards/phui-workboard-color.css' => 'ac6fe6a7', 'rsrc/css/phui/workboards/phui-workboard.css' => 'e6d89647', 'rsrc/css/phui/workboards/phui-workcard.css' => '3646fb96', @@ -812,7 +812,7 @@ return array( 'phui-calendar-list-css' => 'c1c7f338', 'phui-calendar-month-css' => '476be7e0', 'phui-chart-css' => '6bf6f78e', - 'phui-crumbs-view-css' => '79d536e5', + 'phui-crumbs-view-css' => '1a1265d4', 'phui-curtain-view-css' => '7148ae25', 'phui-document-summary-view-css' => '9ca48bdf', 'phui-document-view-css' => '9c71d2bf', @@ -846,7 +846,7 @@ return array( 'phui-tag-view-css' => '6bbd83e2', 'phui-theme-css' => '027ba77e', 'phui-timeline-view-css' => '6e342216', - 'phui-two-column-view-css' => '691fec04', + 'phui-two-column-view-css' => 'b9538af1', 'phui-workboard-color-css' => 'ac6fe6a7', 'phui-workboard-view-css' => 'e6d89647', 'phui-workcard-view-css' => '3646fb96', diff --git a/src/applications/config/controller/PhabricatorConfigAllController.php b/src/applications/config/controller/PhabricatorConfigAllController.php index 7f15273b88..e46db665de 100644 --- a/src/applications/config/controller/PhabricatorConfigAllController.php +++ b/src/applications/config/controller/PhabricatorConfigAllController.php @@ -58,15 +58,19 @@ final class PhabricatorConfigAllController $panel->setHeaderText(pht('Current Settings')); $panel->setTable($table); - $nav = $this->buildSideNavView(); $nav->selectFilter('all/'); - $nav->setCrumbs($crumbs); - $nav->appendChild($panel); + + $view = id(new PHUITwoColumnView()) + ->setNavigation($nav) + ->setMainColumn(array( + $panel, + )); return $this->newPage() ->setTitle($title) - ->appendChild($nav); + ->setCrumbs($crumbs) + ->appendChild($view); } diff --git a/src/applications/config/controller/PhabricatorConfigCacheController.php b/src/applications/config/controller/PhabricatorConfigCacheController.php index 0f5681dea6..ab9ddb3ad0 100644 --- a/src/applications/config/controller/PhabricatorConfigCacheController.php +++ b/src/applications/config/controller/PhabricatorConfigCacheController.php @@ -18,16 +18,17 @@ final class PhabricatorConfigCacheController $code_box = $this->renderCodeBox(); $data_box = $this->renderDataBox(); - $nav->appendChild( - array( - $crumbs, + $view = id(new PHUITwoColumnView()) + ->setNavigation($nav) + ->setMainColumn(array( $code_box, $data_box, - )); + )); return $this->newPage() ->setTitle($title) - ->appendChild($nav); + ->setCrumbs($crumbs) + ->appendChild($view); } private function renderCodeBox() { diff --git a/src/applications/config/controller/PhabricatorConfigDatabaseIssueController.php b/src/applications/config/controller/PhabricatorConfigDatabaseIssueController.php index 32ec2fa70b..0366b90b16 100644 --- a/src/applications/config/controller/PhabricatorConfigDatabaseIssueController.php +++ b/src/applications/config/controller/PhabricatorConfigDatabaseIssueController.php @@ -153,15 +153,17 @@ final class PhabricatorConfigDatabaseIssueController $nav = $this->buildSideNavView(); $nav->selectFilter('dbissue/'); - $nav->appendChild( - array( - $crumbs, + + $view = id(new PHUITwoColumnView()) + ->setNavigation($nav) + ->setMainColumn(array( $table_box, - )); + )); return $this->newPage() ->setTitle($title) - ->appendChild($nav); + ->setCrumbs($crumbs) + ->appendChild($view); } } diff --git a/src/applications/config/controller/PhabricatorConfigDatabaseStatusController.php b/src/applications/config/controller/PhabricatorConfigDatabaseStatusController.php index 1b2f9dbcb4..b03ce2a9fc 100644 --- a/src/applications/config/controller/PhabricatorConfigDatabaseStatusController.php +++ b/src/applications/config/controller/PhabricatorConfigDatabaseStatusController.php @@ -91,12 +91,16 @@ final class PhabricatorConfigDatabaseStatusController $crumbs->addTextCrumb(pht('Database Status')); } - $nav->setCrumbs($crumbs); - $nav->appendChild($body); + $view = id(new PHUITwoColumnView()) + ->setNavigation($nav) + ->setMainColumn(array( + $body, + )); return $this->newPage() ->setTitle($title) - ->appendChild($nav); + ->setCrumbs($crumbs) + ->appendChild($view); } diff --git a/src/applications/config/controller/PhabricatorConfigHistoryController.php b/src/applications/config/controller/PhabricatorConfigHistoryController.php index 9fc538332f..2709041f94 100644 --- a/src/applications/config/controller/PhabricatorConfigHistoryController.php +++ b/src/applications/config/controller/PhabricatorConfigHistoryController.php @@ -31,18 +31,22 @@ final class PhabricatorConfigHistoryController $title = pht('Settings History'); $crumbs = $this->buildApplicationCrumbs(); - $crumbs->setBorder(true); $crumbs->addTextCrumb('Config', $this->getApplicationURI()); $crumbs->addTextCrumb($title, '/config/history/'); $nav = $this->buildSideNavView(); $nav->selectFilter('history/'); - $nav->setCrumbs($crumbs); - $nav->appendChild($timeline); + + $view = id(new PHUITwoColumnView()) + ->setNavigation($nav) + ->setMainColumn(array( + $timeline, + )); return $this->newPage() ->setTitle($title) - ->appendChild($nav); + ->setCrumbs($crumbs) + ->appendChild($view); } } diff --git a/src/applications/config/controller/PhabricatorConfigIssueListController.php b/src/applications/config/controller/PhabricatorConfigIssueListController.php index 5f30a0411d..02bec4e082 100644 --- a/src/applications/config/controller/PhabricatorConfigIssueListController.php +++ b/src/applications/config/controller/PhabricatorConfigIssueListController.php @@ -56,19 +56,22 @@ final class PhabricatorConfigIssueListController ->setSeverity(PHUIInfoView::SEVERITY_NOTICE); } - $nav->appendChild($setup_issues); - $title = pht('Setup Issues'); $crumbs = $this ->buildApplicationCrumbs($nav) ->addTextCrumb(pht('Setup'), $this->getApplicationURI('issue/')); - $nav->setCrumbs($crumbs); + $view = id(new PHUITwoColumnView()) + ->setNavigation($nav) + ->setMainColumn(array( + $setup_issues, + )); return $this->newPage() ->setTitle($title) - ->appendChild($nav); + ->setCrumbs($crumbs) + ->appendChild($view); } private function buildIssueList(array $issues, $group) { diff --git a/src/applications/config/controller/PhabricatorConfigListController.php b/src/applications/config/controller/PhabricatorConfigListController.php index 220d47cfcd..8b0ff42e50 100644 --- a/src/applications/config/controller/PhabricatorConfigListController.php +++ b/src/applications/config/controller/PhabricatorConfigListController.php @@ -23,21 +23,21 @@ final class PhabricatorConfigListController ->setHeaderText(pht('Applications Configuration')) ->setObjectList($apps_list); - $nav->appendChild( - array( - $core, - $apps, - )); - $crumbs = $this ->buildApplicationCrumbs() ->addTextCrumb(pht('Config'), $this->getApplicationURI()); - $nav->setCrumbs($crumbs); + $view = id(new PHUITwoColumnView()) + ->setNavigation($nav) + ->setMainColumn(array( + $core, + $apps, + )); return $this->newPage() ->setTitle($title) - ->appendChild($nav); + ->setCrumbs($crumbs) + ->appendChild($view); } private function buildConfigOptionsList(array $groups, $type) { diff --git a/src/applications/config/controller/PhabricatorConfigModuleController.php b/src/applications/config/controller/PhabricatorConfigModuleController.php index fee5cb9756..3a67a8cdb4 100644 --- a/src/applications/config/controller/PhabricatorConfigModuleController.php +++ b/src/applications/config/controller/PhabricatorConfigModuleController.php @@ -21,15 +21,17 @@ final class PhabricatorConfigModuleController $nav = $this->buildSideNavView(); $nav->selectFilter('module/'.$key.'/'); - $nav->appendChild( - array( - $crumbs, + + $view = id(new PHUITwoColumnView()) + ->setNavigation($nav) + ->setMainColumn(array( $content, )); return $this->newPage() ->setTitle($title) - ->appendChild($nav); + ->setCrumbs($crumbs) + ->appendChild($view); } } diff --git a/src/applications/config/controller/PhabricatorConfigWelcomeController.php b/src/applications/config/controller/PhabricatorConfigWelcomeController.php index 435ce6f01e..addf80e62f 100644 --- a/src/applications/config/controller/PhabricatorConfigWelcomeController.php +++ b/src/applications/config/controller/PhabricatorConfigWelcomeController.php @@ -15,12 +15,16 @@ final class PhabricatorConfigWelcomeController ->buildApplicationCrumbs() ->addTextCrumb(pht('Welcome')); - $nav->setCrumbs($crumbs); - $nav->appendChild($this->buildWelcomeScreen($request)); + $view = id(new PHUITwoColumnView()) + ->setNavigation($nav) + ->setMainColumn(array( + $this->buildWelcomeScreen($request), + )); return $this->newPage() ->setTitle($title) - ->appendChild($nav); + ->setCrumbs($crumbs) + ->appendChild($view); } public function buildWelcomeScreen(AphrontRequest $request) { diff --git a/src/applications/settings/controller/PhabricatorSettingsMainController.php b/src/applications/settings/controller/PhabricatorSettingsMainController.php index 48683a154c..bf9b171031 100644 --- a/src/applications/settings/controller/PhabricatorSettingsMainController.php +++ b/src/applications/settings/controller/PhabricatorSettingsMainController.php @@ -61,14 +61,17 @@ final class PhabricatorSettingsMainController '/p/'.$this->getUser()->getUsername().'/'); } $crumbs->addTextCrumb($panel->getPanelName()); - $nav->appendChild($response); $title = $panel->getPanelName(); + $view = id(new PHUITwoColumnView()) + ->setNavigation($nav) + ->setMainColumn($response); + return $this->newPage() ->setTitle($title) ->setCrumbs($crumbs) - ->setNavigation($nav); + ->appendChild($view); } diff --git a/src/view/phui/PHUITwoColumnView.php b/src/view/phui/PHUITwoColumnView.php index d174547559..819e726309 100644 --- a/src/view/phui/PHUITwoColumnView.php +++ b/src/view/phui/PHUITwoColumnView.php @@ -4,6 +4,7 @@ final class PHUITwoColumnView extends AphrontTagView { private $mainColumn; private $sideColumn = null; + private $navigation; private $display; private $fluid; private $header; @@ -25,6 +26,12 @@ final class PHUITwoColumnView extends AphrontTagView { return $this; } + public function setNavigation($nav) { + $this->navigation = $nav; + $this->display = self::DISPLAY_LEFT; + return $this; + } + public function setHeader(PHUIHeaderView $header) { $this->header = $header; return $this; @@ -162,14 +169,24 @@ final class PHUITwoColumnView extends AphrontTagView { private function buildSideColumn() { + $classes = array(); + $classes[] = 'phui-side-column'; + $navigation = null; + if ($this->navigation) { + $classes[] = 'side-has-nav'; + $navigation = id(new PHUIObjectBoxView()) + ->appendChild($this->navigation); + } + $curtain = $this->getCurtain(); return phutil_tag( 'div', array( - 'class' => 'phui-side-column', + 'class' => implode($classes, ' '), ), array( + $navigation, $curtain, $this->sideColumn, )); diff --git a/webroot/rsrc/css/phui/phui-crumbs-view.css b/webroot/rsrc/css/phui/phui-crumbs-view.css index 47fd0a4da9..6e99b70e19 100644 --- a/webroot/rsrc/css/phui/phui-crumbs-view.css +++ b/webroot/rsrc/css/phui/phui-crumbs-view.css @@ -5,7 +5,7 @@ .phui-crumbs-view { overflow: hidden; vertical-align: top; - padding: 0 8px 0 16px; + padding: 0 20px 0 28px; /* TODO: Position this over the slider for Differential's file tree view. Remove this once that gets sorted out. */ position: relative; diff --git a/webroot/rsrc/css/phui/phui-two-column-view.css b/webroot/rsrc/css/phui/phui-two-column-view.css index 600945103d..4d71c3d5ba 100644 --- a/webroot/rsrc/css/phui/phui-two-column-view.css +++ b/webroot/rsrc/css/phui/phui-two-column-view.css @@ -70,6 +70,16 @@ width: 300px; } +.device-desktop .phui-two-column-view.phui-side-column-left .phui-main-column { + float: right; + width: calc(100% - 280px); +} + +.device-desktop .phui-two-column-view.phui-side-column-left .phui-side-column { + float: left; + width: 260px; +} + .device .phui-side-column { margin-bottom: 20px; } @@ -202,3 +212,22 @@ .phui-header-shell + .phui-info-view { margin: 16px; } + +/* Navigation */ + +.phui-two-column-view .side-has-nav .phabricator-nav-local { + width: auto; + position: static; + margin: 0; +} + +.device .phui-two-column-view .side-has-nav { + display: none; +} + +/* Document View */ + +.phui-two-column-view .phui-two-column-content .phui-document-fluid + .phui-document-view { + margin: 0 0 20px 0; +}