diff --git a/src/applications/project/controller/PhabricatorProjectMembersAddController.php b/src/applications/project/controller/PhabricatorProjectMembersAddController.php
index 79c66dc5b1..572e71f51a 100644
--- a/src/applications/project/controller/PhabricatorProjectMembersAddController.php
+++ b/src/applications/project/controller/PhabricatorProjectMembersAddController.php
@@ -1,77 +1,82 @@
 <?php
 
 final class PhabricatorProjectMembersAddController
   extends PhabricatorProjectController {
 
   public function handleRequest(AphrontRequest $request) {
     $viewer = $request->getViewer();
     $id = $request->getURIData('id');
 
     $project = id(new PhabricatorProjectQuery())
       ->setViewer($viewer)
       ->withIDs(array($id))
       ->requireCapabilities(
         array(
           PhabricatorPolicyCapability::CAN_VIEW,
           PhabricatorPolicyCapability::CAN_EDIT,
         ))
       ->executeOne();
     if (!$project) {
       return new Aphront404Response();
     }
 
     $this->setProject($project);
     $done_uri = "/project/members/{$id}/";
 
     if (!$project->supportsEditMembers()) {
       $copy = pht('Parent projects and milestones do not support adding '.
         'members. You can add members directly to any non-parent subproject.');
 
+      $subprojects_uri = "/project/subprojects/{$id}/";
+
       return $this->newDialog()
         ->setTitle(pht('Unsupported Project'))
         ->appendParagraph($copy)
+        ->setSubmitURI($subprojects_uri)
+        ->addSubmitButton(pht('See Subprojects'))
+        ->setDisableWorkflowOnSubmit(true)
         ->addCancelButton($done_uri);
     }
 
     if ($request->isFormPost()) {
       $member_phids = $request->getArr('memberPHIDs');
 
       $type_member = PhabricatorProjectProjectHasMemberEdgeType::EDGECONST;
 
       $xactions = array();
 
       $xactions[] = id(new PhabricatorProjectTransaction())
         ->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
         ->setMetadataValue('edge:type', $type_member)
         ->setNewValue(
           array(
             '+' => array_fuse($member_phids),
           ));
 
       $editor = id(new PhabricatorProjectTransactionEditor())
         ->setActor($viewer)
         ->setContentSourceFromRequest($request)
         ->setContinueOnNoEffect(true)
         ->setContinueOnMissingFields(true)
         ->applyTransactions($project, $xactions);
 
       return id(new AphrontRedirectResponse())
         ->setURI($done_uri);
     }
 
     $form = id(new AphrontFormView())
       ->setUser($viewer)
       ->appendControl(
         id(new AphrontFormTokenizerControl())
           ->setName('memberPHIDs')
           ->setLabel(pht('Members'))
           ->setDatasource(new PhabricatorPeopleDatasource()));
 
     return $this->newDialog()
       ->setTitle(pht('Add Members'))
       ->appendForm($form)
       ->addCancelButton($done_uri)
       ->addSubmitButton(pht('Add Members'));
   }
 
 }
diff --git a/src/applications/project/controller/PhabricatorProjectUpdateController.php b/src/applications/project/controller/PhabricatorProjectUpdateController.php
index 7cbd77b4cb..5707693654 100644
--- a/src/applications/project/controller/PhabricatorProjectUpdateController.php
+++ b/src/applications/project/controller/PhabricatorProjectUpdateController.php
@@ -1,120 +1,125 @@
 <?php
 
 final class PhabricatorProjectUpdateController
   extends PhabricatorProjectController {
 
   public function handleRequest(AphrontRequest $request) {
     $viewer = $request->getViewer();
     $id = $request->getURIData('id');
     $action = $request->getURIData('action');
 
     $capabilities = array(
       PhabricatorPolicyCapability::CAN_VIEW,
     );
 
     switch ($action) {
       case 'join':
         $capabilities[] = PhabricatorPolicyCapability::CAN_JOIN;
         break;
       case 'leave':
         break;
       default:
         return new Aphront404Response();
     }
 
     $project = id(new PhabricatorProjectQuery())
       ->setViewer($viewer)
       ->withIDs(array($id))
       ->needMembers(true)
       ->requireCapabilities($capabilities)
       ->executeOne();
     if (!$project) {
       return new Aphront404Response();
     }
 
     $done_uri = "/project/members/{$id}/";
 
     if (!$project->supportsEditMembers()) {
       $copy = pht('Parent projects and milestones do not support adding '.
         'members. You can add members directly to any non-parent subproject.');
 
+      $subprojects_uri = "/project/subprojects/{$id}/";
+
       return $this->newDialog()
         ->setTitle(pht('Unsupported Project'))
         ->appendParagraph($copy)
+        ->setSubmitURI($subprojects_uri)
+        ->addSubmitButton(pht('See Subprojects'))
+        ->setDisableWorkflowOnSubmit(true)
         ->addCancelButton($done_uri);
     }
 
     if ($request->isFormPost()) {
       $edge_action = null;
       switch ($action) {
         case 'join':
           $edge_action = '+';
           break;
         case 'leave':
           $edge_action = '-';
           break;
       }
 
       $type_member = PhabricatorProjectProjectHasMemberEdgeType::EDGECONST;
 
       $member_spec = array(
         $edge_action => array($viewer->getPHID() => $viewer->getPHID()),
       );
 
       $xactions = array();
       $xactions[] = id(new PhabricatorProjectTransaction())
         ->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
         ->setMetadataValue('edge:type', $type_member)
         ->setNewValue($member_spec);
 
       $editor = id(new PhabricatorProjectTransactionEditor())
         ->setActor($viewer)
         ->setContentSourceFromRequest($request)
         ->setContinueOnNoEffect(true)
         ->setContinueOnMissingFields(true)
         ->applyTransactions($project, $xactions);
 
       return id(new AphrontRedirectResponse())->setURI($done_uri);
     }
 
     $is_locked = $project->getIsMembershipLocked();
     $can_edit = PhabricatorPolicyFilter::hasCapability(
       $viewer,
       $project,
       PhabricatorPolicyCapability::CAN_EDIT);
     $can_leave = ($can_edit || !$is_locked);
 
     $button = null;
     if ($action == 'leave') {
       if ($can_leave) {
         $title = pht('Leave Project');
         $body = pht(
           'Your tremendous contributions to this project will be sorely '.
           'missed. Are you sure you want to leave?');
         $button = pht('Leave Project');
       } else {
         $title = pht('Membership Locked');
         $body = pht(
           'Membership for this project is locked. You can not leave.');
       }
     } else {
       $title = pht('Join Project');
       $body = pht(
         'Join this project? You will become a member and enjoy whatever '.
         'benefits membership may confer.');
       $button = pht('Join Project');
     }
 
     $dialog = $this->newDialog()
       ->setTitle($title)
       ->appendParagraph($body)
       ->addCancelButton($done_uri);
 
     if ($button) {
       $dialog->addSubmitButton($button);
     }
 
     return $dialog;
   }
 
 }
diff --git a/src/view/AphrontDialogView.php b/src/view/AphrontDialogView.php
index b8b00a6b3e..14aa4a19b6 100644
--- a/src/view/AphrontDialogView.php
+++ b/src/view/AphrontDialogView.php
@@ -1,470 +1,496 @@
 <?php
 
 final class AphrontDialogView
   extends AphrontView
   implements AphrontResponseProducerInterface {
 
   private $title;
   private $shortTitle;
   private $submitButton;
   private $cancelURI;
   private $cancelText = 'Cancel';
   private $submitURI;
   private $hidden = array();
   private $class;
   private $renderAsForm = true;
   private $formID;
   private $footers = array();
   private $isStandalone;
   private $method = 'POST';
   private $disableWorkflowOnSubmit;
   private $disableWorkflowOnCancel;
   private $width      = 'default';
   private $errors = array();
   private $flush;
   private $validationException;
   private $objectList;
   private $resizeX;
   private $resizeY;
 
 
   const WIDTH_DEFAULT = 'default';
   const WIDTH_FORM    = 'form';
   const WIDTH_FULL    = 'full';
 
   public function setMethod($method) {
     $this->method = $method;
     return $this;
   }
 
   public function setIsStandalone($is_standalone) {
     $this->isStandalone = $is_standalone;
     return $this;
   }
 
   public function setErrors(array $errors) {
     $this->errors = $errors;
     return $this;
   }
 
   public function getIsStandalone() {
     return $this->isStandalone;
   }
 
+  /**
+   * Set the URI associated to the Submit Button
+   *
+   * If you want a normal link and not any form submission,
+   * see also: setDisableWorkflowOnSubmit(false).
+   *
+   * @param string $uri
+   * @return self
+   */
   public function setSubmitURI($uri) {
     $this->submitURI = $uri;
     return $this;
   }
 
   public function setTitle($title) {
     $this->title = $title;
     return $this;
   }
 
   public function getTitle() {
     return $this->title;
   }
 
   public function setShortTitle($short_title) {
     $this->shortTitle = $short_title;
     return $this;
   }
 
   public function getShortTitle() {
     return $this->shortTitle;
   }
 
   public function setResizeY($resize_y) {
     $this->resizeY = $resize_y;
     return $this;
   }
 
   public function getResizeY() {
     return $this->resizeY;
   }
 
   public function setResizeX($resize_x) {
     $this->resizeX = $resize_x;
     return $this;
   }
 
   public function getResizeX() {
     return $this->resizeX;
   }
 
+  /**
+   * Add a Submit Button and specify its text
+   *
+   * If you want to associate an URI for this Button,
+   * see also: setSubmitURI().
+   *
+   * @param string $text Text shown for that button
+   * @return self
+   */
   public function addSubmitButton($text = null) {
     if (!$text) {
       $text = pht('Okay');
     }
 
     $this->submitButton = $text;
     return $this;
   }
 
   public function addCancelButton($uri, $text = null) {
     if (!$text) {
       $text = pht('Cancel');
     }
 
     $this->cancelURI = $uri;
     $this->cancelText = $text;
     return $this;
   }
 
   public function addFooter($footer) {
     $this->footers[] = $footer;
     return $this;
   }
 
   public function addHiddenInput($key, $value) {
     if (is_array($value)) {
       foreach ($value as $hidden_key => $hidden_value) {
         $this->hidden[] = array($key.'['.$hidden_key.']', $hidden_value);
       }
     } else {
       $this->hidden[] = array($key, $value);
     }
     return $this;
   }
 
   public function setClass($class) {
     $this->class = $class;
     return $this;
   }
 
   public function setFlush($flush) {
     $this->flush = $flush;
     return $this;
   }
 
   public function setRenderDialogAsDiv() {
     // TODO: This API is awkward.
     $this->renderAsForm = false;
     return $this;
   }
 
   public function setFormID($id) {
     $this->formID = $id;
     return $this;
   }
 
   public function setWidth($width) {
     $this->width = $width;
     return $this;
   }
 
   public function setObjectList(PHUIObjectItemListView $list) {
     $this->objectList = true;
     $box = id(new PHUIObjectBoxView())
       ->setObjectList($list);
     return $this->appendChild($box);
   }
 
   public function appendRemarkup($remarkup) {
     $viewer = $this->getViewer();
     $view = new PHUIRemarkupView($viewer, $remarkup);
 
     $view_tag = phutil_tag(
       'div',
       array(
         'class' => 'aphront-dialog-view-paragraph',
       ),
       $view);
 
     return $this->appendChild($view_tag);
   }
 
   public function appendParagraph($paragraph) {
     return $this->appendParagraphTag($paragraph);
   }
 
   public function appendCommand($command) {
     $command_tag = phutil_tag('tt', array(), $command);
     return $this->appendParagraphTag(
       $command_tag,
       'aphront-dialog-view-command');
   }
 
   private function appendParagraphTag($content, $classes = null) {
     if ($classes) {
       $classes = (array)$classes;
     } else {
       $classes = array();
     }
 
     array_unshift($classes, 'aphront-dialog-view-paragraph');
 
     $paragraph_tag = phutil_tag(
       'p',
       array(
         'class' => implode(' ', $classes),
       ),
       $content);
 
     return $this->appendChild($paragraph_tag);
   }
 
 
   public function appendList(array $items) {
     $listitems = array();
     foreach ($items as $item) {
       $listitems[] = phutil_tag(
         'li',
         array(
           'class' => 'remarkup-list-item',
         ),
         $item);
     }
     return $this->appendChild(
       phutil_tag(
         'ul',
         array(
           'class' => 'remarkup-list',
         ),
         $listitems));
   }
 
   public function appendForm(AphrontFormView $form) {
     return $this->appendChild($form->buildLayoutView());
   }
 
+  /**
+   * Enable or Disable a Workflow on Submit
+   *
+   * For example, if your Submit Button should be a normal link,
+   * without activating any Workflow, you can set false.
+   * @param bool $disable_workflow_on_submit
+   * @return self
+   */
   public function setDisableWorkflowOnSubmit($disable_workflow_on_submit) {
     $this->disableWorkflowOnSubmit = $disable_workflow_on_submit;
     return $this;
   }
 
   public function getDisableWorkflowOnSubmit() {
     return $this->disableWorkflowOnSubmit;
   }
 
   public function setDisableWorkflowOnCancel($disable_workflow_on_cancel) {
     $this->disableWorkflowOnCancel = $disable_workflow_on_cancel;
     return $this;
   }
 
   public function getDisableWorkflowOnCancel() {
     return $this->disableWorkflowOnCancel;
   }
 
   public function setValidationException(
     PhabricatorApplicationTransactionValidationException $ex = null) {
     $this->validationException = $ex;
     return $this;
   }
 
   public function render() {
     require_celerity_resource('aphront-dialog-view-css');
 
     $buttons = array();
     if ($this->submitButton) {
       $meta = array();
       if ($this->disableWorkflowOnSubmit) {
         $meta['disableWorkflow'] = true;
       }
 
       $buttons[] = javelin_tag(
         'button',
         array(
           'name' => '__submit__',
           'sigil' => '__default__',
           'type' => 'submit',
           'meta' => $meta,
         ),
         $this->submitButton);
     }
 
     if ($this->cancelURI) {
       $meta = array();
       if ($this->disableWorkflowOnCancel) {
         $meta['disableWorkflow'] = true;
       }
 
       $buttons[] = javelin_tag(
         'a',
         array(
           'href'  => $this->cancelURI,
           'class' => 'button button-grey',
           'name'  => '__cancel__',
           'sigil' => 'jx-workflow-button',
           'meta' => $meta,
         ),
         $this->cancelText);
     }
 
     if (!$this->hasViewer()) {
       throw new Exception(
         pht(
           'You must call %s when rendering an %s.',
           'setViewer()',
           __CLASS__));
     }
 
     $classes = array();
     $classes[] = 'aphront-dialog-view';
     $classes[] = $this->class;
     if ($this->flush) {
       $classes[] = 'aphront-dialog-flush';
     }
 
     switch ($this->width) {
       case self::WIDTH_FORM:
       case self::WIDTH_FULL:
         $classes[] = 'aphront-dialog-view-width-'.$this->width;
         break;
       case self::WIDTH_DEFAULT:
         break;
       default:
         throw new Exception(
           pht(
             "Unknown dialog width '%s'!",
             $this->width));
     }
 
     if ($this->isStandalone) {
       $classes[] = 'aphront-dialog-view-standalone';
     }
 
     if ($this->objectList) {
       $classes[] = 'aphront-dialog-object-list';
     }
 
     $attributes = array(
       'class'   => implode(' ', $classes),
       'sigil'   => 'jx-dialog',
       'role'    => 'dialog',
     );
 
     $form_attributes = array(
       'action'  => $this->submitURI,
       'method'  => $this->method,
       'id'      => $this->formID,
     );
 
     $hidden_inputs = array();
     $hidden_inputs[] = phutil_tag(
       'input',
       array(
         'type' => 'hidden',
         'name' => '__dialog__',
         'value' => '1',
       ));
 
     foreach ($this->hidden as $desc) {
       list($key, $value) = $desc;
       $hidden_inputs[] = javelin_tag(
         'input',
         array(
           'type' => 'hidden',
           'name' => $key,
           'value' => $value,
           'sigil' => 'aphront-dialog-application-input',
         ));
     }
 
     if (!$this->renderAsForm) {
       $buttons = array(
         phabricator_form(
           $this->getViewer(),
           $form_attributes,
           array_merge($hidden_inputs, $buttons)),
       );
     }
 
     $children = $this->renderChildren();
 
     $errors = $this->errors;
 
     $ex = $this->validationException;
     $exception_errors = null;
     if ($ex) {
       foreach ($ex->getErrors() as $error) {
         $errors[] = $error->getMessage();
       }
     }
 
     if ($errors) {
       $children = array(
         id(new PHUIInfoView())->setErrors($errors),
         $children,
       );
     }
 
     $header = new PHUIHeaderView();
     $header->setHeader($this->title);
 
     $footer = null;
     if ($this->footers) {
       $footer = phutil_tag(
         'div',
         array(
           'class' => 'aphront-dialog-foot',
         ),
         $this->footers);
     }
 
     $resize = null;
     if ($this->resizeX || $this->resizeY) {
       $resize = javelin_tag(
         'div',
         array(
           'class' => 'aphront-dialog-resize',
           'sigil' => 'jx-dialog-resize',
           'meta' => array(
             'resizeX' => $this->resizeX,
             'resizeY' => $this->resizeY,
           ),
         ));
     }
 
     $tail = null;
     if ($buttons || $footer) {
       $tail = phutil_tag(
         'div',
         array(
           'class' => 'aphront-dialog-tail grouped',
         ),
         array(
           $buttons,
           $footer,
           $resize,
         ));
     }
 
     $content = array(
       phutil_tag(
         'div',
         array(
           'class' => 'aphront-dialog-head',
         ),
         $header),
       phutil_tag('div',
         array(
           'class' => 'aphront-dialog-body grouped',
         ),
         $children),
       $tail,
     );
 
     if ($this->renderAsForm) {
       return phabricator_form(
         $this->getViewer(),
         $form_attributes + $attributes,
         array($hidden_inputs, $content));
     } else {
       return javelin_tag(
         'div',
         $attributes,
         $content);
     }
   }
 
 
 /* -(  AphrontResponseProducerInterface  )----------------------------------- */
 
 
   public function produceAphrontResponse() {
     return id(new AphrontDialogResponse())
       ->setDialog($this);
   }
 
 }