diff --git a/src/applications/releeph/controller/product/ReleephProductActionController.php b/src/applications/releeph/controller/product/ReleephProductActionController.php index cca9b0bf0a..da82e2d6ad 100644 --- a/src/applications/releeph/controller/product/ReleephProductActionController.php +++ b/src/applications/releeph/controller/product/ReleephProductActionController.php @@ -1,93 +1,85 @@ <?php final class ReleephProductActionController extends ReleephProductController { - private $id; - private $action; - - public function willProcessRequest(array $data) { - $this->id = $data['projectID']; - $this->action = $data['action']; - } - - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); + $id = $request->getURIData('projectID'); + $action = $request->getURIData('action'); $product = id(new ReleephProductQuery()) - ->withIDs(array($this->id)) + ->withIDs(array($id)) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )) ->setViewer($viewer) ->executeOne(); if (!$product) { return new Aphront404Response(); } $this->setProduct($product); $product_id = $product->getID(); $product_uri = $this->getProductViewURI($product); - $action = $this->action; switch ($action) { case 'deactivate': case 'activate': break; default: throw new Aphront404Response(); } if ($request->isFormPost()) { $type_active = ReleephProductTransaction::TYPE_ACTIVE; $xactions = array(); if ($action == 'activate') { $xactions[] = id(new ReleephProductTransaction()) ->setTransactionType($type_active) ->setNewValue(1); } else { $xactions[] = id(new ReleephProductTransaction()) ->setTransactionType($type_active) ->setNewValue(0); } $editor = id(new ReleephProductEditor()) ->setActor($viewer) ->setContentSourceFromRequest($request) ->setContinueOnNoEffect(true) ->setContinueOnMissingFields(true); $editor->applyTransactions($product, $xactions); return id(new AphrontRedirectResponse())->setURI($product_uri); } if ($action == 'activate') { $title = pht('Activate Product?'); $body = pht( 'Reactivate the product %s?', phutil_tag('strong', array(), $product->getName())); $submit = pht('Reactivate Product'); $short = pht('Deactivate'); } else { $title = pht('Really Deactivate Product?'); $body = pht( 'Really deactivate the product %s?', phutil_tag('strong', array(), $product->getName())); $submit = pht('Deactivate Product'); $short = pht('Activate'); } return $this->newDialog() ->setTitle($title) ->setShortTitle($short) ->appendParagraph($body) ->addSubmitButton($submit) ->addCancelButton($product_uri); } } diff --git a/src/applications/releeph/controller/product/ReleephProductCreateController.php b/src/applications/releeph/controller/product/ReleephProductCreateController.php index 6d5f29ff37..9772b293fe 100644 --- a/src/applications/releeph/controller/product/ReleephProductCreateController.php +++ b/src/applications/releeph/controller/product/ReleephProductCreateController.php @@ -1,133 +1,132 @@ <?php final class ReleephProductCreateController extends ReleephProductController { - public function processRequest() { - $request = $this->getRequest(); + public function handleRequest(AphrontRequest $request) { $name = trim($request->getStr('name')); $trunk_branch = trim($request->getStr('trunkBranch')); $repository_phid = $request->getStr('repositoryPHID'); $e_name = true; $e_trunk_branch = true; $errors = array(); if ($request->isFormPost()) { if (!$name) { $e_name = pht('Required'); $errors[] = pht( 'Your product should have a simple, descriptive name.'); } if (!$trunk_branch) { $e_trunk_branch = pht('Required'); $errors[] = pht( 'You must specify which branch you will be picking from.'); } $pr_repository = id(new PhabricatorRepositoryQuery()) ->setViewer($request->getUser()) ->withPHIDs(array($repository_phid)) ->executeOne(); if (!$errors) { $releeph_product = id(new ReleephProject()) ->setName($name) ->setTrunkBranch($trunk_branch) ->setRepositoryPHID($pr_repository->getPHID()) ->setCreatedByUserPHID($request->getUser()->getPHID()) ->setIsActive(1); try { $releeph_product->save(); return id(new AphrontRedirectResponse()) ->setURI($releeph_product->getURI()); } catch (AphrontDuplicateKeyQueryException $ex) { $e_name = pht('Not Unique'); $errors[] = pht('Another product already uses this name.'); } } } $repo_options = $this->getRepositorySelectOptions(); $product_name_input = id(new AphrontFormTextControl()) ->setLabel(pht('Name')) ->setDisableAutocomplete(true) ->setName('name') ->setValue($name) ->setError($e_name) ->setCaption(pht('A name like "Thrift" but not "Thrift releases".')); $repository_input = id(new AphrontFormSelectControl()) ->setLabel(pht('Repository')) ->setName('repositoryPHID') ->setValue($repository_phid) ->setOptions($repo_options); $branch_name_preview = id(new ReleephBranchPreviewView()) ->setLabel(pht('Example Branch')) ->addControl('projectName', $product_name_input) ->addControl('repositoryPHID', $repository_input) ->addStatic('template', '') ->addStatic('isSymbolic', false); $form = id(new AphrontFormView()) ->setUser($request->getUser()) ->appendChild($product_name_input) ->appendChild($repository_input) ->appendChild( id(new AphrontFormTextControl()) ->setLabel(pht('Trunk')) ->setName('trunkBranch') ->setValue($trunk_branch) ->setError($e_trunk_branch) ->setCaption(pht( 'The development branch, from which requests will be picked.'))) ->appendChild($branch_name_preview) ->appendChild( id(new AphrontFormSubmitControl()) ->addCancelButton('/releeph/project/') ->setValue(pht('Create Release Product'))); $form_box = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Create New Product')) ->setFormErrors($errors) ->setForm($form); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('New Product')); return $this->buildApplicationPage( array( $crumbs, $form_box, ), array( 'title' => pht('Create New Product'), )); } private function getRepositorySelectOptions() { $repos = id(new PhabricatorRepositoryQuery()) ->setViewer($this->getRequest()->getUser()) ->execute(); $repos = msort($repos, 'getName'); $repos = mpull($repos, null, 'getID'); $choices = array(); foreach ($repos as $repo_id => $repo) { $repo_name = $repo->getName(); $callsign = $repo->getCallsign(); $choices[$repo->getPHID()] = "r{$callsign} ({$repo_name})"; } ksort($choices); return $choices; } } diff --git a/src/applications/releeph/controller/product/ReleephProductEditController.php b/src/applications/releeph/controller/product/ReleephProductEditController.php index d0769c7b28..7fd8e81563 100644 --- a/src/applications/releeph/controller/product/ReleephProductEditController.php +++ b/src/applications/releeph/controller/product/ReleephProductEditController.php @@ -1,273 +1,267 @@ <?php final class ReleephProductEditController extends ReleephProductController { - private $productID; - - public function willProcessRequest(array $data) { - $this->productID = $data['projectID']; - } - - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); + $id = $request->getURIData('projectID'); $product = id(new ReleephProductQuery()) ->setViewer($viewer) - ->withIDs(array($this->productID)) + ->withIDs(array($id)) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )) ->executeOne(); if (!$product) { return new Aphront404Response(); } $this->setProduct($product); $e_name = true; $e_trunk_branch = true; $e_branch_template = false; $errors = array(); $product_name = $request->getStr('name', $product->getName()); $trunk_branch = $request->getStr('trunkBranch', $product->getTrunkBranch()); $branch_template = $request->getStr('branchTemplate'); if ($branch_template === null) { $branch_template = $product->getDetail('branchTemplate'); } $pick_failure_instructions = $request->getStr('pickFailureInstructions', $product->getDetail('pick_failure_instructions')); $test_paths = $request->getStr('testPaths'); if ($test_paths !== null) { $test_paths = array_filter(explode("\n", $test_paths)); } else { $test_paths = $product->getDetail('testPaths', array()); } $repository_phid = $product->getRepositoryPHID(); if ($request->isFormPost()) { $pusher_phids = $request->getArr('pushers'); if (!$product_name) { $e_name = pht('Required'); $errors[] = pht('Your releeph product should have a simple descriptive name.'); } if (!$trunk_branch) { $e_trunk_branch = pht('Required'); $errors[] = pht('You must specify which branch you will be picking from.'); } $other_releeph_products = id(new ReleephProject()) ->loadAllWhere('id != %d', $product->getID()); $other_releeph_product_names = mpull($other_releeph_products, 'getName', 'getID'); if (in_array($product_name, $other_releeph_product_names)) { $errors[] = pht('Releeph product name %s is already taken', $product_name); } foreach ($test_paths as $test_path) { $result = @preg_match($test_path, ''); $is_a_valid_regexp = $result !== false; if (!$is_a_valid_regexp) { $errors[] = pht('Please provide a valid regular expression: '. '%s is not valid', $test_path); } } $product ->setName($product_name) ->setTrunkBranch($trunk_branch) ->setDetail('pushers', $pusher_phids) ->setDetail('pick_failure_instructions', $pick_failure_instructions) ->setDetail('branchTemplate', $branch_template) ->setDetail('testPaths', $test_paths); $fake_commit_handle = ReleephBranchTemplate::getFakeCommitHandleFor( $repository_phid, $viewer); if ($branch_template) { list($branch_name, $template_errors) = id(new ReleephBranchTemplate()) ->setCommitHandle($fake_commit_handle) ->setReleephProjectName($product_name) ->interpolate($branch_template); if ($template_errors) { $e_branch_template = pht('Whoopsies!'); foreach ($template_errors as $template_error) { $errors[] = pht('Template error: %s', $template_error); } } } if (!$errors) { $product->save(); return id(new AphrontRedirectResponse())->setURI($product->getURI()); } } $pusher_phids = $request->getArr( 'pushers', $product->getDetail('pushers', array())); $form = id(new AphrontFormView()) ->setUser($request->getUser()) ->appendChild( id(new AphrontFormTextControl()) ->setLabel(pht('Name')) ->setName('name') ->setValue($product_name) ->setError($e_name) ->setCaption(pht('A name like "Thrift" but not "Thrift releases".'))) ->appendChild( id(new AphrontFormStaticControl()) ->setLabel(pht('Repository')) ->setValue( $product->getRepository()->getName())) ->appendChild( id(new AphrontFormStaticControl()) ->setLabel(pht('Repository')) ->setValue( $product->getRepository()->getName())) ->appendChild( id(new AphrontFormStaticControl()) ->setLabel(pht('Releeph Project PHID')) ->setValue( $product->getPHID())) ->appendChild( id(new AphrontFormTextControl()) ->setLabel(pht('Trunk')) ->setValue($trunk_branch) ->setName('trunkBranch') ->setError($e_trunk_branch)) ->appendChild( id(new AphrontFormTextAreaControl()) ->setLabel(pht('Pick Instructions')) ->setValue($pick_failure_instructions) ->setName('pickFailureInstructions') ->setCaption( pht('Instructions for pick failures, which will be used '. 'in emails generated by failed picks'))) ->appendChild( id(new AphrontFormTextAreaControl()) ->setLabel(pht('Tests paths')) ->setValue(implode("\n", $test_paths)) ->setName('testPaths') ->setCaption( pht('List of strings that all test files contain in their path '. 'in this project. One string per line. '. 'Examples: \'__tests__\', \'/javatests/\'...'))); $branch_template_input = id(new AphrontFormTextControl()) ->setName('branchTemplate') ->setValue($branch_template) ->setLabel(pht('Branch Template')) ->setError($e_branch_template) ->setCaption( pht("Leave this blank to use your installation's default.")); $branch_template_preview = id(new ReleephBranchPreviewView()) ->setLabel(pht('Preview')) ->addControl('template', $branch_template_input) ->addStatic('repositoryPHID', $repository_phid) ->addStatic('isSymbolic', false) ->addStatic('projectName', $product->getName()); $form ->appendControl( id(new AphrontFormTokenizerControl()) ->setLabel(pht('Pushers')) ->setName('pushers') ->setDatasource(new PhabricatorPeopleDatasource()) ->setValue($pusher_phids)) ->appendChild($branch_template_input) ->appendChild($branch_template_preview) ->appendRemarkupInstructions($this->getBranchHelpText()); $form ->appendChild( id(new AphrontFormSubmitControl()) ->addCancelButton('/releeph/product/') ->setValue(pht('Save'))); $box = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Edit Releeph Product')) ->setFormErrors($errors) ->appendChild($form); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Edit Product')); return $this->buildStandardPageResponse( array( $crumbs, $box, ), array( 'title' => pht('Edit Releeph Product'), 'device' => true, )); } private function getBranchHelpText() { return <<<EOTEXT ==== Interpolations ==== | Code | Meaning | ----- | ------- | `%P` | The name of your product, with spaces changed to "-". | `%p` | Like %P, but all lowercase. | `%Y` | The four digit year associated with the branch date. | `%m` | The two digit month. | `%d` | The two digit day. | `%v` | The handle of the commit where the branch was cut ("rXYZa4b3c2d1"). | `%V` | The abbreviated commit id where the branch was cut ("a4b3c2d1"). | `%..` | Any other sequence interpreted by `strftime()`. | `%%` | A literal percent sign. ==== Tips for Branch Templates ==== Use a directory to separate your release branches from other branches: lang=none releases/%Y-%M-%d-%v => releases/2012-30-16-rHERGE32cd512a52b7 Include a second hierarchy if you share your repository with other products: lang=none releases/%P/%p-release-%Y%m%d-%V => releases/Tintin/tintin-release-20121116-32cd512a52b7 Keep your branch names simple, avoiding strange punctuation, most of which is forbidden or escaped anyway: lang=none, counterexample releases//..clown-releases..//`date --iso=seconds`-$(sudo halt) Include the date early in your template, in an order which sorts properly: lang=none releases/%Y%m%d-%v => releases/20121116-rHERGE32cd512a52b7 (good!) releases/%V-%m.%d.%Y => releases/32cd512a52b7-11.16.2012 (awful!) EOTEXT; } } diff --git a/src/applications/releeph/controller/product/ReleephProductHistoryController.php b/src/applications/releeph/controller/product/ReleephProductHistoryController.php index 15d7139854..ebe9f15725 100644 --- a/src/applications/releeph/controller/product/ReleephProductHistoryController.php +++ b/src/applications/releeph/controller/product/ReleephProductHistoryController.php @@ -1,46 +1,41 @@ <?php final class ReleephProductHistoryController extends ReleephProductController { - private $id; - public function shouldAllowPublic() { return true; } - public function willProcessRequest(array $data) { - $this->id = $data['projectID']; - } - - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); + public function handleRequest(AphrontRequest $request) { + $viewer = $request->getViewer(); + $id = $request->getURIData('projectID'); $product = id(new ReleephProductQuery()) ->setViewer($viewer) - ->withIDs(array($this->id)) + ->withIDs(array($id)) ->executeOne(); if (!$product) { return new Aphront404Response(); } $this->setProduct($product); $timeline = $this->buildTransactionTimeline( $product, new ReleephProductTransactionQuery()); $timeline->setShouldTerminate(true); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('History')); + $crumbs->setBorder(true); return $this->buildApplicationPage( array( $crumbs, $timeline, ), array( 'title' => pht('Product History'), )); } } diff --git a/src/applications/releeph/controller/product/ReleephProductListController.php b/src/applications/releeph/controller/product/ReleephProductListController.php index 73d2b27416..14cc964e03 100644 --- a/src/applications/releeph/controller/product/ReleephProductListController.php +++ b/src/applications/releeph/controller/product/ReleephProductListController.php @@ -1,36 +1,31 @@ <?php final class ReleephProductListController extends ReleephController { - private $queryKey; - public function shouldAllowPublic() { return true; } - public function willProcessRequest(array $data) { - $this->queryKey = idx($data, 'queryKey'); - } - - public function processRequest() { + public function handleRequest(AphrontRequest $request) { + $query_key = $request->getURIData('queryKey'); $controller = id(new PhabricatorApplicationSearchController()) - ->setQueryKey($this->queryKey) + ->setQueryKey($query_key) ->setSearchEngine(new ReleephProductSearchEngine()) ->setNavigation($this->buildSideNavView()); return $this->delegateToController($controller); } protected function buildApplicationCrumbs() { $crumbs = parent::buildApplicationCrumbs(); $crumbs->addAction( id(new PHUIListItemView()) ->setName(pht('Create Product')) ->setHref($this->getApplicationURI('product/create/')) ->setIcon('fa-plus-square')); return $crumbs; } } diff --git a/src/applications/releeph/controller/product/ReleephProductViewController.php b/src/applications/releeph/controller/product/ReleephProductViewController.php index 3bccae0ea0..e35497eec7 100644 --- a/src/applications/releeph/controller/product/ReleephProductViewController.php +++ b/src/applications/releeph/controller/product/ReleephProductViewController.php @@ -1,161 +1,154 @@ <?php final class ReleephProductViewController extends ReleephProductController { - private $productID; - private $queryKey; - public function shouldAllowPublic() { return true; } - public function willProcessRequest(array $data) { - $this->productID = idx($data, 'projectID'); - $this->queryKey = idx($data, 'queryKey'); - } - - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); + public function handleRequest(AphrontRequest $request) { + $id = $request->getURIData('projectID'); + $query_key = $request->getURIData('queryKey'); + $viewer = $request->getViewer(); $product = id(new ReleephProductQuery()) ->setViewer($viewer) - ->withIDs(array($this->productID)) + ->withIDs(array($id)) ->executeOne(); if (!$product) { return new Aphront404Response(); } $this->setProduct($product); $controller = id(new PhabricatorApplicationSearchController()) - ->setQueryKey($this->queryKey) + ->setQueryKey($query_key) ->setPreface($this->renderPreface()) ->setSearchEngine( id(new ReleephBranchSearchEngine()) ->setProduct($product)) ->setNavigation($this->buildSideNavView()); return $this->delegateToController($controller); } public function buildSideNavView($for_app = false) { $viewer = $this->getRequest()->getUser(); $product = $this->getProduct(); $nav = new AphrontSideNavFilterView(); $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); if ($for_app) { $nav->addFilter('product/create/', pht('Create Product')); } id(new ReleephBranchSearchEngine()) ->setProduct($product) ->setViewer($viewer) ->addNavigationItems($nav->getMenu()); $nav->selectFilter(null); return $nav; } protected function buildApplicationCrumbs() { $crumbs = parent::buildApplicationCrumbs(); $product = $this->getProduct(); if ($product) { $crumbs->addAction( id(new PHUIListItemView()) ->setHref($product->getURI('cutbranch/')) ->setName(pht('Cut New Branch')) ->setIcon('fa-plus')); } return $crumbs; } private function renderPreface() { $viewer = $this->getRequest()->getUser(); $product = $this->getProduct(); $id = $product->getID(); $header = id(new PHUIHeaderView()) ->setHeader($product->getName()) ->setUser($viewer) ->setPolicyObject($product); if ($product->getIsActive()) { $header->setStatus('fa-check', 'bluegrey', pht('Active')); } else { $header->setStatus('fa-ban', 'dark', pht('Inactive')); } $actions = id(new PhabricatorActionListView()) ->setUser($viewer) ->setObject($product) ->setObjectURI($this->getRequest()->getRequestURI()); $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, $product, PhabricatorPolicyCapability::CAN_EDIT); $edit_uri = $this->getApplicationURI("product/{$id}/edit/"); $history_uri = $this->getApplicationURI("product/{$id}/history/"); $actions->addAction( id(new PhabricatorActionView()) ->setName(pht('Edit Product')) ->setHref($edit_uri) ->setIcon('fa-pencil') ->setDisabled(!$can_edit) ->setWorkflow(!$can_edit)); if ($product->getIsActive()) { $status_name = pht('Deactivate Product'); $status_href = "product/{$id}/action/deactivate/"; $status_icon = 'fa-times'; } else { $status_name = pht('Reactivate Product'); $status_href = "product/{$id}/action/activate/"; $status_icon = 'fa-plus-circle-o'; } $actions->addAction( id(new PhabricatorActionView()) ->setName($status_name) ->setHref($this->getApplicationURI($status_href)) ->setIcon($status_icon) ->setDisabled(!$can_edit) ->setWorkflow(true)); $actions->addAction( id(new PhabricatorActionView()) ->setName(pht('View History')) ->setHref($history_uri) ->setIcon('fa-list')); $properties = id(new PHUIPropertyListView()) ->setUser($viewer) ->setObject($product); $properties->addProperty( pht('Repository'), $product->getRepository()->getName()); $properties->setActionList($actions); $pushers = $product->getPushers(); if ($pushers) { $properties->addProperty( pht('Pushers'), $viewer->renderHandleList($pushers)); } return id(new PHUIObjectBoxView()) ->setHeader($header) ->addPropertyList($properties); } } diff --git a/src/applications/releeph/controller/request/ReleephRequestActionController.php b/src/applications/releeph/controller/request/ReleephRequestActionController.php index bbcc1df2bb..1a53b08b8c 100644 --- a/src/applications/releeph/controller/request/ReleephRequestActionController.php +++ b/src/applications/releeph/controller/request/ReleephRequestActionController.php @@ -1,132 +1,122 @@ <?php final class ReleephRequestActionController extends ReleephRequestController { - private $action; - private $requestID; - - public function willProcessRequest(array $data) { - $this->action = $data['action']; - $this->requestID = $data['requestID']; - } - - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); + public function handleRequest(AphrontRequest $request) { + $action = $request->getURIData('action'); + $id = $request->getURIData('requestID'); + $viewer = $request->getViewer(); $request->validateCSRF(); $pull = id(new ReleephRequestQuery()) ->setViewer($viewer) - ->withIDs(array($this->requestID)) + ->withIDs(array($id)) ->executeOne(); if (!$pull) { return new Aphront404Response(); } $branch = $pull->getBranch(); $product = $branch->getProduct(); - - $action = $this->action; - $origin_uri = '/'.$pull->getMonogram(); $editor = id(new ReleephRequestTransactionalEditor()) ->setActor($viewer) ->setContinueOnNoEffect(true) ->setContentSourceFromRequest($request); $xactions = array(); switch ($action) { case 'want': case 'pass': static $action_map = array( 'want' => ReleephRequest::INTENT_WANT, 'pass' => ReleephRequest::INTENT_PASS, ); $intent = $action_map[$action]; $xactions[] = id(new ReleephRequestTransaction()) ->setTransactionType(ReleephRequestTransaction::TYPE_USER_INTENT) ->setMetadataValue( 'isAuthoritative', $product->isAuthoritative($viewer)) ->setNewValue($intent); break; case 'mark-manually-picked': case 'mark-manually-reverted': if ( $pull->getRequestUserPHID() === $viewer->getPHID() || $product->isAuthoritative($viewer)) { // We're all good! } else { throw new Exception( pht( "Bug! Only pushers or the requestor can manually change a ". "request's in-branch status!")); } if ($action === 'mark-manually-picked') { $in_branch = 1; $intent = ReleephRequest::INTENT_WANT; } else { $in_branch = 0; $intent = ReleephRequest::INTENT_PASS; } $xactions[] = id(new ReleephRequestTransaction()) ->setTransactionType(ReleephRequestTransaction::TYPE_USER_INTENT) ->setMetadataValue('isManual', true) ->setMetadataValue('isAuthoritative', true) ->setNewValue($intent); $xactions[] = id(new ReleephRequestTransaction()) ->setTransactionType(ReleephRequestTransaction::TYPE_MANUAL_IN_BRANCH) ->setNewValue($in_branch); break; default: throw new Exception( pht('Unknown or unimplemented action %s.', $action)); } $editor->applyTransactions($pull, $xactions); if ($request->getBool('render')) { $field_list = PhabricatorCustomField::getObjectFields( $pull, PhabricatorCustomField::ROLE_VIEW); $field_list ->setViewer($viewer) ->readFieldsFromStorage($pull); // TODO: This should be more modern and general. $engine = id(new PhabricatorMarkupEngine()) ->setViewer($viewer); foreach ($field_list->getFields() as $field) { if ($field->shouldMarkup()) { $field->setMarkupEngine($engine); } } $engine->process(); $pull_box = id(new ReleephRequestView()) ->setUser($viewer) ->setCustomFields($field_list) ->setPullRequest($pull) ->setIsListView(true); return id(new AphrontAjaxResponse())->setContent( array( 'markup' => hsprintf('%s', $pull_box), )); } return id(new AphrontRedirectResponse())->setURI($origin_uri); } } diff --git a/src/applications/releeph/controller/request/ReleephRequestCommentController.php b/src/applications/releeph/controller/request/ReleephRequestCommentController.php index 4c728341af..0a31261a13 100644 --- a/src/applications/releeph/controller/request/ReleephRequestCommentController.php +++ b/src/applications/releeph/controller/request/ReleephRequestCommentController.php @@ -1,69 +1,63 @@ <?php final class ReleephRequestCommentController extends ReleephRequestController { - private $requestID; - - public function willProcessRequest(array $data) { - $this->requestID = $data['requestID']; - } - - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); + public function handleRequest(AphrontRequest $request) { + $id = $request->getURIData('requestID'); + $viewer = $request->getViewer(); if (!$request->isFormPost()) { return new Aphront400Response(); } $pull = id(new ReleephRequestQuery()) ->setViewer($viewer) - ->withIDs(array($this->requestID)) + ->withIDs(array($id)) ->executeOne(); if (!$pull) { return new Aphront404Response(); } $is_preview = $request->isPreviewRequest(); $draft = PhabricatorDraft::buildFromRequest($request); $view_uri = $this->getApplicationURI('/'.$pull->getMonogram()); $xactions = array(); $xactions[] = id(new ReleephRequestTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) ->attachComment( id(new ReleephRequestTransactionComment()) ->setContent($request->getStr('comment'))); $editor = id(new ReleephRequestTransactionalEditor()) ->setActor($viewer) ->setContinueOnNoEffect($request->isContinueRequest()) ->setContentSourceFromRequest($request) ->setIsPreview($is_preview); try { $xactions = $editor->applyTransactions($pull, $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/releeph/controller/request/ReleephRequestDifferentialCreateController.php b/src/applications/releeph/controller/request/ReleephRequestDifferentialCreateController.php index 3c2dc3d735..ffd6388284 100644 --- a/src/applications/releeph/controller/request/ReleephRequestDifferentialCreateController.php +++ b/src/applications/releeph/controller/request/ReleephRequestDifferentialCreateController.php @@ -1,107 +1,102 @@ <?php // TODO: After T2222, this is likely unreachable? final class ReleephRequestDifferentialCreateController extends ReleephController { - private $revisionID; private $revision; - public function willProcessRequest(array $data) { - $this->revisionID = $data['diffRevID']; - } - - public function processRequest() { - $request = $this->getRequest(); - $user = $request->getUser(); + public function handleRequest(AphrontRequest $request) { + $revision_id = $request->getURIData('diffRevID'); + $viewer = $request->getViewer(); $diff_rev = id(new DifferentialRevisionQuery()) - ->setViewer($user) - ->withIDs(array($this->revisionID)) + ->setViewer($viewer) + ->withIDs(array($revision_id)) ->executeOne(); if (!$diff_rev) { return new Aphront404Response(); } $this->revision = $diff_rev; $repository = $this->revision->getRepository(); $projects = id(new ReleephProject())->loadAllWhere( 'repositoryPHID = %s AND isActive = 1', $repository->getPHID()); if (!$projects) { throw new Exception( pht( "%s belongs to the '%s' repository, ". "which is not part of any Releeph project!", 'D'.$this->revision->getID(), $repository->getMonogram())); } $branches = id(new ReleephBranch())->loadAllWhere( 'releephProjectID IN (%Ld) AND isActive = 1', mpull($projects, 'getID')); if (!$branches) { throw new Exception(pht( '%s could be in the Releeph project(s) %s, '. 'but this project / none of these projects have open branches.', 'D'.$this->revision->getID(), implode(', ', mpull($projects, 'getName')))); } if (count($branches) === 1) { return id(new AphrontRedirectResponse()) ->setURI($this->buildReleephRequestURI(head($branches))); } $projects = msort( mpull($projects, null, 'getID'), 'getName'); $branch_groups = mgroup($branches, 'getReleephProjectID'); require_celerity_resource('releeph-request-differential-create-dialog'); $dialog = id(new AphrontDialogView()) - ->setUser($user) + ->setUser($viewer) ->setTitle(pht('Choose Releeph Branch')) ->setClass('releeph-request-differential-create-dialog') ->addCancelButton('/D'.$request->getStr('D')); $dialog->appendChild( pht( 'This differential revision changes code that is associated '. 'with multiple Releeph branches. Please select the branch '. 'where you would like this code to be picked.')); foreach ($branch_groups as $project_id => $branches) { $project = idx($projects, $project_id); $dialog->appendChild( phutil_tag( 'h1', array(), $project->getName())); $branches = msort($branches, 'getBasename'); foreach ($branches as $branch) { $uri = $this->buildReleephRequestURI($branch); $dialog->appendChild( phutil_tag( 'a', array( 'href' => $uri, ), $branch->getDisplayNameWithDetail())); } } return id(new AphrontDialogResponse()) ->setDialog($dialog); } private function buildReleephRequestURI(ReleephBranch $branch) { $uri = $branch->getURI('request/'); return id(new PhutilURI($uri)) ->setQueryParam('D', $this->revision->getID()); } } diff --git a/src/applications/releeph/controller/request/ReleephRequestEditController.php b/src/applications/releeph/controller/request/ReleephRequestEditController.php index 279fee4bd6..d5f5187349 100644 --- a/src/applications/releeph/controller/request/ReleephRequestEditController.php +++ b/src/applications/releeph/controller/request/ReleephRequestEditController.php @@ -1,314 +1,308 @@ <?php final class ReleephRequestEditController extends ReleephBranchController { - private $requestID; - private $branchID; + public function handleRequest(AphrontRequest $request) { + $action = $request->getURIData('action'); + $request_id = $request->getURIData('requestID'); + $branch_id = $request->getURIData('branchID'); + $viewer = $request->getViewer(); - public function willProcessRequest(array $data) { - $this->requestID = idx($data, 'requestID'); - $this->branchID = idx($data, 'branchID'); - } - - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); - - if ($this->requestID) { + if ($request_id) { $pull = id(new ReleephRequestQuery()) ->setViewer($viewer) - ->withIDs(array($this->requestID)) + ->withIDs(array($request_id)) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )) ->executeOne(); if (!$pull) { return new Aphront404Response(); } $branch = $pull->getBranch(); $is_edit = true; } else { $branch = id(new ReleephBranchQuery()) ->setViewer($viewer) - ->withIDs(array($this->branchID)) + ->withIDs(array($branch_id)) ->executeOne(); if (!$branch) { return new Aphront404Response(); } $pull = id(new ReleephRequest()) ->setRequestUserPHID($viewer->getPHID()) ->setBranchID($branch->getID()) ->setInBranch(0) ->attachBranch($branch); $is_edit = false; } $this->setBranch($branch); $product = $branch->getProduct(); $request_identifier = $request->getStr('requestIdentifierRaw'); $e_request_identifier = true; // Load all the ReleephFieldSpecifications $selector = $branch->getProduct()->getReleephFieldSelector(); $fields = $selector->getFieldSpecifications(); foreach ($fields as $field) { $field ->setReleephProject($product) ->setReleephBranch($branch) ->setReleephRequest($pull); } $field_list = PhabricatorCustomField::getObjectFields( $pull, PhabricatorCustomField::ROLE_EDIT); foreach ($field_list->getFields() as $field) { $field ->setReleephProject($product) ->setReleephBranch($branch) ->setReleephRequest($pull); } $field_list->readFieldsFromStorage($pull); - if ($this->branchID) { - $cancel_uri = $this->getApplicationURI('branch/'.$this->branchID.'/'); + if ($branch_id) { + $cancel_uri = $this->getApplicationURI('branch/'.$branch_id.'/'); } else { $cancel_uri = '/'.$pull->getMonogram(); } // Make edits $errors = array(); if ($request->isFormPost()) { $xactions = array(); // The commit-identifier being requested... if (!$is_edit) { if ($request_identifier === ReleephRequestTypeaheadControl::PLACEHOLDER) { $errors[] = pht('No commit ID was provided.'); $e_request_identifier = pht('Required'); } else { $pr_commit = null; $finder = id(new ReleephCommitFinder()) ->setUser($viewer) ->setReleephProject($product); try { $pr_commit = $finder->fromPartial($request_identifier); } catch (Exception $e) { $e_request_identifier = pht('Invalid'); $errors[] = pht( 'Request %s is probably not a valid commit.', $request_identifier); $errors[] = $e->getMessage(); } if (!$errors) { $object_phid = $finder->getRequestedObjectPHID(); if (!$object_phid) { $object_phid = $pr_commit->getPHID(); } $pull->setRequestedObjectPHID($object_phid); } } if (!$errors) { $existing = id(new ReleephRequest()) ->loadOneWhere('requestCommitPHID = %s AND branchID = %d', $pr_commit->getPHID(), $branch->getID()); if ($existing) { return id(new AphrontRedirectResponse()) ->setURI('/releeph/request/edit/'.$existing->getID(). '?existing=1'); } $xactions[] = id(new ReleephRequestTransaction()) ->setTransactionType(ReleephRequestTransaction::TYPE_REQUEST) ->setNewValue($pr_commit->getPHID()); $xactions[] = id(new ReleephRequestTransaction()) ->setTransactionType(ReleephRequestTransaction::TYPE_USER_INTENT) // To help hide these implicit intents... ->setMetadataValue('isRQCreate', true) ->setMetadataValue('userPHID', $viewer->getPHID()) ->setMetadataValue( 'isAuthoritative', $product->isAuthoritative($viewer)) ->setNewValue(ReleephRequest::INTENT_WANT); } } // TODO: This should happen implicitly while building transactions // instead. foreach ($field_list->getFields() as $field) { $field->readValueFromRequest($request); } if (!$errors) { foreach ($fields as $field) { if ($field->isEditable()) { try { $data = $request->getRequestData(); $value = idx($data, $field->getRequiredStorageKey()); $field->validate($value); $xactions[] = id(new ReleephRequestTransaction()) ->setTransactionType(ReleephRequestTransaction::TYPE_EDIT_FIELD) ->setMetadataValue('fieldClass', get_class($field)) ->setNewValue($value); } catch (ReleephFieldParseException $ex) { $errors[] = $ex->getMessage(); } } } } if (!$errors) { $editor = id(new ReleephRequestTransactionalEditor()) ->setActor($viewer) ->setContinueOnNoEffect(true) ->setContentSourceFromRequest($request); $editor->applyTransactions($pull, $xactions); return id(new AphrontRedirectResponse())->setURI($cancel_uri); } } $handle_phids = array( $pull->getRequestUserPHID(), $pull->getRequestCommitPHID(), ); $handle_phids = array_filter($handle_phids); if ($handle_phids) { $handles = id(new PhabricatorHandleQuery()) ->setViewer($viewer) ->withPHIDs($handle_phids) ->execute(); } else { $handles = array(); } $age_string = ''; if ($is_edit) { $age_string = phutil_format_relative_time( time() - $pull->getDateCreated()).' ago'; } // Warn the user if we've been redirected here because we tried to // re-request something. $notice_view = null; if ($request->getInt('existing')) { $notice_messages = array( pht('You are editing an existing pick request!'), pht( 'Requested %s by %s', $age_string, $handles[$pull->getRequestUserPHID()]->renderLink()), ); $notice_view = id(new PHUIInfoView()) ->setSeverity(PHUIInfoView::SEVERITY_NOTICE) ->setErrors($notice_messages); } $form = id(new AphrontFormView()) ->setUser($viewer); if ($is_edit) { $form ->appendChild( id(new AphrontFormMarkupControl()) ->setLabel(pht('Original Commit')) ->setValue( $handles[$pull->getRequestCommitPHID()]->renderLink())) ->appendChild( id(new AphrontFormMarkupControl()) ->setLabel(pht('Requestor')) ->setValue(hsprintf( '%s %s', $handles[$pull->getRequestUserPHID()]->renderLink(), $age_string))); } else { $origin = null; $diff_rev_id = $request->getStr('D'); if ($diff_rev_id) { $diff_rev = id(new DifferentialRevisionQuery()) ->setViewer($viewer) ->withIDs(array($diff_rev_id)) ->executeOne(); $origin = '/D'.$diff_rev->getID(); $title = sprintf( 'D%d: %s', $diff_rev_id, $diff_rev->getTitle()); $form ->addHiddenInput('requestIdentifierRaw', 'D'.$diff_rev_id) ->appendChild( id(new AphrontFormStaticControl()) ->setLabel(pht('Diff')) ->setValue($title)); } else { $origin = $branch->getURI(); $repo = $product->getRepository(); $branch_cut_point = id(new PhabricatorRepositoryCommit()) ->loadOneWhere( 'phid = %s', $branch->getCutPointCommitPHID()); $form->appendChild( id(new ReleephRequestTypeaheadControl()) ->setName('requestIdentifierRaw') ->setLabel(pht('Commit ID')) ->setRepo($repo) ->setValue($request_identifier) ->setError($e_request_identifier) ->setStartTime($branch_cut_point->getEpoch()) ->setCaption( pht( 'Start typing to autocomplete on commit title, '. 'or give a Phabricator commit identifier like rFOO1234.'))); } } $field_list->appendFieldsToForm($form); $crumbs = $this->buildApplicationCrumbs(); if ($is_edit) { $title = pht('Edit Pull Request'); $submit_name = pht('Save'); $crumbs->addTextCrumb($pull->getMonogram(), '/'.$pull->getMonogram()); $crumbs->addTextCrumb(pht('Edit')); } else { $title = pht('Create Pull Request'); $submit_name = pht('Create Pull Request'); $crumbs->addTextCrumb(pht('New Pull Request')); } $form->appendChild( id(new AphrontFormSubmitControl()) ->addCancelButton($cancel_uri, pht('Cancel')) ->setValue($submit_name)); $box = id(new PHUIObjectBoxView()) ->setHeaderText($title) ->setFormErrors($errors) ->appendChild($form); return $this->buildApplicationPage( array( $crumbs, $notice_view, $box, ), array( 'title' => $title, )); } } diff --git a/src/applications/releeph/controller/request/ReleephRequestTypeaheadController.php b/src/applications/releeph/controller/request/ReleephRequestTypeaheadController.php index ca49a52858..3a93de7453 100644 --- a/src/applications/releeph/controller/request/ReleephRequestTypeaheadController.php +++ b/src/applications/releeph/controller/request/ReleephRequestTypeaheadController.php @@ -1,92 +1,90 @@ <?php final class ReleephRequestTypeaheadController extends PhabricatorTypeaheadDatasourceController { - public function processRequest() { - $request = $this->getRequest(); - + public function handleRequest(AphrontRequest $request) { $query = $request->getStr('q'); $repo_id = $request->getInt('repo'); $since = $request->getInt('since'); $limit = $request->getInt('limit'); $now = time(); $data = array(); // Dummy instances used for getting connections, table names, etc. $pr_commit = new PhabricatorRepositoryCommit(); $pr_commit_data = new PhabricatorRepositoryCommitData(); $conn = $pr_commit->establishConnection('r'); $rows = queryfx_all( $conn, 'SELECT rc.phid as commitPHID, rc.authorPHID, rcd.authorName, SUBSTRING(rcd.commitMessage, 1, 100) AS shortMessage, rc.commitIdentifier, rc.epoch FROM %T rc INNER JOIN %T rcd ON rcd.commitID = rc.id WHERE repositoryID = %d AND rc.epoch >= %d AND ( rcd.commitMessage LIKE %~ OR rc.commitIdentifier LIKE %~ ) ORDER BY rc.epoch DESC LIMIT %d', $pr_commit->getTableName(), $pr_commit_data->getTableName(), $repo_id, $since, $query, $query, $limit); foreach ($rows as $row) { $full_commit_id = $row['commitIdentifier']; $short_commit_id = substr($full_commit_id, 0, 12); $first_line = $this->getFirstLine($row['shortMessage']); $data[] = array( $full_commit_id, $short_commit_id, $row['authorName'], phutil_format_relative_time($now - $row['epoch']), $first_line, ); } return id(new AphrontAjaxResponse()) ->setContent($data); } /** * Split either at the first new line, or a bunch of dashes. * * Really just a legacy from old Releeph Daemon commit messages where I used * to say: * * Commit of FOO for BAR * ------------ * This does X Y Z * */ private function getFirstLine($commit_message_fragment) { static $separators = array('-------', "\n"); $string = ltrim($commit_message_fragment); $first_line = $string; foreach ($separators as $separator) { if ($pos = strpos($string, $separator)) { $first_line = substr($string, 0, $pos); break; } } return $first_line; } } diff --git a/src/applications/releeph/controller/request/ReleephRequestViewController.php b/src/applications/releeph/controller/request/ReleephRequestViewController.php index 04f91b5fc6..694505cd20 100644 --- a/src/applications/releeph/controller/request/ReleephRequestViewController.php +++ b/src/applications/releeph/controller/request/ReleephRequestViewController.php @@ -1,100 +1,93 @@ <?php final class ReleephRequestViewController extends ReleephBranchController { - private $requestID; - - public function willProcessRequest(array $data) { - $this->requestID = $data['requestID']; - } - - public function processRequest() { - $request = $this->getRequest(); - $viewer = $request->getUser(); + public function handleRequest(AphrontRequest $request) { + $id = $request->getURIData('requestID'); + $viewer = $request->getViewer(); $pull = id(new ReleephRequestQuery()) ->setViewer($viewer) - ->withIDs(array($this->requestID)) + ->withIDs(array($id)) ->executeOne(); if (!$pull) { return new Aphront404Response(); } $this->setBranch($pull->getBranch()); // Redirect older URIs to new "Y" URIs. // TODO: Get rid of this eventually. $actual_path = $request->getRequestURI()->getPath(); $expect_path = '/'.$pull->getMonogram(); if ($actual_path != $expect_path) { return id(new AphrontRedirectResponse())->setURI($expect_path); } // TODO: Break this 1:1 stuff? $branch = $pull->getBranch(); $field_list = PhabricatorCustomField::getObjectFields( $pull, PhabricatorCustomField::ROLE_VIEW); $field_list ->setViewer($viewer) ->readFieldsFromStorage($pull); // TODO: This should be more modern and general. $engine = id(new PhabricatorMarkupEngine()) ->setViewer($viewer); foreach ($field_list->getFields() as $field) { if ($field->shouldMarkup()) { $field->setMarkupEngine($engine); } } $engine->process(); $pull_box = id(new ReleephRequestView()) ->setUser($viewer) ->setCustomFields($field_list) ->setPullRequest($pull); $timeline = $this->buildTransactionTimeline( $pull, new ReleephRequestTransactionQuery()); $add_comment_header = pht('Plea or Yield'); $draft = PhabricatorDraft::newFromUserAndKey( $viewer, $pull->getPHID()); $title = hsprintf( '%s %s', $pull->getMonogram(), $pull->getSummaryForDisplay()); $add_comment_form = id(new PhabricatorApplicationTransactionCommentView()) ->setUser($viewer) ->setObjectPHID($pull->getPHID()) ->setDraft($draft) ->setHeaderText($add_comment_header) ->setAction($this->getApplicationURI( '/request/comment/'.$pull->getID().'/')) ->setSubmitButtonName(pht('Comment')); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb($pull->getMonogram(), '/'.$pull->getMonogram()); return $this->buildStandardPageResponse( array( $crumbs, $pull_box, $timeline, $add_comment_form, ), array( 'title' => $title, - 'device' => true, )); } }