Christopher Johnson (WMDE) has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/261185

Change subject: fixes several upstream breaking changes current with 
10ed33052361be82cbc09884118d65ec0601bd55 27.12.2015 Bug: T122486
......................................................................

fixes several upstream breaking changes
current with 10ed33052361be82cbc09884118d65ec0601bd55 27.12.2015
Bug: T122486

Change-Id: I418d643a0949599ad507071fb14ffb3ff89bf102
---
M src/__phutil_library_map__.php
M src/application/SprintApplication.php
M src/controller/SprintController.php
A src/controller/SprintProjectController.php
M src/controller/SprintProjectProfileController.php
M src/controller/board/SprintBoardTaskEditController.php
A src/controller/board/SprintManiphestEditEngine.php
7 files changed, 654 insertions(+), 813 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/phabricator/extensions/Sprint 
refs/changes/85/261185/1

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
index 84de703..3fa5040 100644
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -61,7 +61,9 @@
     'SprintListController' => 'controller/SprintListController.php',
     'SprintListDataProvider' => 'storage/SprintListDataProvider.php',
     'SprintListTableView' => 'view/SprintListTableView.php',
+    'SprintManiphestEditEngine' => 
'controller/board/SprintManiphestEditEngine.php',
     'SprintPoints' => 'util/SprintPoints.php',
+    'SprintProjectController' => 'controller/SprintProjectController.php',
     'SprintProjectCustomField' => 'customfield/SprintProjectCustomField.php',
     'SprintProjectProfileController' => 
'controller/SprintProjectProfileController.php',
     'SprintProjectViewController' => 
'controller/SprintProjectViewController.php',
@@ -131,12 +133,14 @@
     'SprintIsSprintField' => 'SprintProjectCustomField',
     'SprintListController' => 'SprintController',
     'SprintListTableView' => 'Phobject',
+    'SprintManiphestEditEngine' => 'PhabricatorEditEngine',
     'SprintPoints' => 'Phobject',
+    'SprintProjectController' => 'SprintController',
     'SprintProjectCustomField' => array(
       'PhabricatorProjectCustomField',
       'PhabricatorStandardCustomFieldInterface',
     ),
-    'SprintProjectProfileController' => 'SprintController',
+    'SprintProjectProfileController' => 'SprintProjectController',
     'SprintProjectViewController' => 'SprintController',
     'SprintQuery' => 'SprintDAO',
     'SprintQueryTest' => 'SprintTestCase',
diff --git a/src/application/SprintApplication.php 
b/src/application/SprintApplication.php
index 336bb86..6d7a58b 100644
--- a/src/application/SprintApplication.php
+++ b/src/application/SprintApplication.php
@@ -72,8 +72,8 @@
             // all routes following point to default controllers
               'archive/(?P<id>[1-9]\d*)/'
               => 'PhabricatorProjectArchiveController',
-              'details/(?P<id>[1-9]\d*)/'
-              => 'PhabricatorProjectEditDetailsController',
+              $this->getEditRoutePattern('edit/')
+              => 'PhabricatorProjectEditController',
               'feed/(?P<id>[1-9]\d*)/'
               => 'PhabricatorProjectFeedController',
               'icon/(?P<id>[1-9]\d*)/'
diff --git a/src/controller/SprintController.php 
b/src/controller/SprintController.php
index 143fc2f..e202a3f 100644
--- a/src/controller/SprintController.php
+++ b/src/controller/SprintController.php
@@ -24,7 +24,7 @@
   }
 
   public function buildApplicationMenu() {
-      return $this->buildSideNavView($this->getUser(),
+      return $this->buildSprintNavView($this->getUser(),
           $this->setApplicationURI(), true)->getMenu();
   }
 
@@ -45,7 +45,7 @@
   /**
    * @param PhutilURI $uri
    */
-  public function buildSideNavView($viewer, $uri, $for_app = false) {
+  public function buildSprintNavView($viewer, $uri, $for_app = false) {
     $request = $this->getRequest();
     $id = $request->getURIData('id');
     $slug = $request->getURIData('slug');
diff --git a/src/controller/SprintProjectController.php 
b/src/controller/SprintProjectController.php
new file mode 100644
index 0000000..a3f628f
--- /dev/null
+++ b/src/controller/SprintProjectController.php
@@ -0,0 +1,181 @@
+<?php
+
+abstract class SprintProjectController extends SprintController {
+
+  private $project;
+
+  protected function setProject(PhabricatorProject $project) {
+    $this->project = $project;
+    return $this;
+  }
+
+  protected function getProject() {
+    return $this->project;
+  }
+
+  protected function loadProject() {
+    $viewer = $this->getViewer();
+    $request = $this->getRequest();
+
+    $id = $request->getURIData('id');
+    $slug = $request->getURIData('slug');
+
+    if ($slug) {
+      $normal_slug = PhabricatorSlug::normalizeProjectSlug($slug);
+      $is_abnormal = ($slug !== $normal_slug);
+      $normal_uri = "/tag/{$normal_slug}/";
+    } else {
+      $is_abnormal = false;
+    }
+
+    $query = id(new PhabricatorProjectQuery())
+        ->setViewer($viewer)
+        ->needMembers(true)
+        ->needWatchers(true)
+        ->needImages(true)
+        ->needSlugs(true);
+
+    if ($slug) {
+      $query->withSlugs(array($slug));
+    } else {
+      $query->withIDs(array($id));
+    }
+
+    $policy_exception = null;
+    try {
+      $project = $query->executeOne();
+    } catch (PhabricatorPolicyException $ex) {
+      $policy_exception = $ex;
+      $project = null;
+    }
+
+    if (!$project) {
+      // This project legitimately does not exist, so just 404 the user.
+      if (!$policy_exception) {
+        return new Aphront404Response();
+      }
+
+      // Here, the project exists but the user can't see it. If they are
+      // using a non-canonical slug to view the project, redirect to the
+      // canonical slug. If they're already using the canonical slug, rethrow
+      // the exception to give them the policy error.
+      if ($is_abnormal) {
+        return id(new AphrontRedirectResponse())->setURI($normal_uri);
+      } else {
+        throw $policy_exception;
+      }
+    }
+
+    // The user can view the project, but is using a noncanonical slug.
+    // Redirect to the canonical slug.
+    $primary_slug = $project->getPrimarySlug();
+    if ($slug && ($slug !== $primary_slug)) {
+      $primary_uri = "/tag/{$primary_slug}/";
+      return id(new AphrontRedirectResponse())->setURI($primary_uri);
+    }
+
+    $this->setProject($project);
+
+    return null;
+  }
+
+  public function buildApplicationMenu() {
+    return $this->buildSideNavView(true)->getMenu();
+  }
+
+  public function buildSideNavView($for_app = false) {
+    $project = $this->getProject();
+
+    $nav = new AphrontSideNavFilterView();
+    $nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
+
+    $viewer = $this->getViewer();
+
+    $id = null;
+    if ($for_app) {
+      if ($project) {
+        $id = $project->getID();
+        $nav->addFilter("profile/{$id}/", pht('Profile'));
+        $nav->addFilter("board/{$id}/", pht('Workboard'));
+        $nav->addFilter("members/{$id}/", pht('Members'));
+        $nav->addFilter("feed/{$id}/", pht('Feed'));
+        $nav->addFilter("details/{$id}/", pht('Edit Details'));
+      }
+      $nav->addFilter('create', pht('Create Project'));
+    }
+
+    if (!$id) {
+      id(new PhabricatorProjectSearchEngine())
+          ->setViewer($viewer)
+          ->addNavigationItems($nav->getMenu());
+    }
+
+    $nav->selectFilter(null);
+
+    return $nav;
+  }
+
+  public function buildIconNavView(PhabricatorProject $project) {
+    $this->setProject($project);
+    $viewer = $this->getViewer();
+    $id = $project->getID();
+    $picture = $project->getProfileImageURI();
+    $name = $project->getName();
+
+    $columns = id(new PhabricatorProjectColumnQuery())
+        ->setViewer($viewer)
+        ->withProjectPHIDs(array($project->getPHID()))
+        ->execute();
+    if ($columns) {
+      $board_icon = 'fa-columns';
+    } else {
+      $board_icon = 'fa-columns grey';
+    }
+
+    $nav = new AphrontSideNavFilterView();
+    $nav->setIconNav(true);
+    $nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
+    $nav->addIcon("profile/{$id}/", $name, null, $picture);
+
+    $class = 'PhabricatorManiphestApplication';
+    if (PhabricatorApplication::isClassInstalledForViewer($class, $viewer)) {
+      $phid = $project->getPHID();
+      $nav->addIcon("board/{$id}/", pht('Workboard'), $board_icon);
+      $query_uri = urisprintf(
+          '/maniphest/?statuses=open()&projects=%s#R',
+          $phid);
+      $nav->addIcon(null, pht('Open Tasks'), 'fa-anchor', null, $query_uri);
+    }
+
+    $nav->addIcon("feed/{$id}/", pht('Feed'), 'fa-newspaper-o');
+    $nav->addIcon("members/{$id}/", pht('Members'), 'fa-group');
+    $nav->addIcon("details/{$id}/", pht('Edit Details'), 'fa-pencil');
+
+    if (PhabricatorEnv::getEnvConfig('phabricator.show-prototypes')) {
+      $nav->addIcon("subprojects/{$id}/", pht('Subprojects'), 'fa-sitemap');
+      $nav->addIcon("milestones/{$id}/", pht('Milestones'), 'fa-map-marker');
+    }
+
+
+    return $nav;
+  }
+
+  protected function buildApplicationCrumbs() {
+    $crumbs = parent::buildApplicationCrumbs();
+
+    $project = $this->getProject();
+    if ($project) {
+      $ancestors = $project->getAncestorProjects();
+      $ancestors = array_reverse($ancestors);
+      $ancestors[] = $project;
+      foreach ($ancestors as $ancestor) {
+        $crumbs->addTextCrumb(
+            $project->getName(),
+            $project->getURI());
+      }
+    }
+
+    return $crumbs;
+  }
+
+}
diff --git a/src/controller/SprintProjectProfileController.php 
b/src/controller/SprintProjectProfileController.php
index 05f32a0..4bdd02e 100644
--- a/src/controller/SprintProjectProfileController.php
+++ b/src/controller/SprintProjectProfileController.php
@@ -1,7 +1,7 @@
 <?php
 
 final class SprintProjectProfileController
-  extends SprintController {
+  extends SprintProjectController {
 
   public function shouldAllowPublic() {
     return true;
@@ -10,27 +10,13 @@
   public function handleRequest(AphrontRequest $request) {
     $viewer = $request->getViewer();
 
-    $query = id(new PhabricatorProjectQuery())
-      ->setViewer($viewer)
-      ->needMembers(true)
-      ->needWatchers(true)
-      ->needImages(true)
-      ->needSlugs(true);
-    $id = $request->getURIData('id');
-    $slug = $request->getURIData('slug');
-    if ($slug) {
-      $query->withSlugs(array($slug));
-    } else {
-      $query->withIDs(array($id));
+    $response = $this->loadProject();
+    if ($response) {
+      return $response;
     }
-    $project = $query->executeOne();
-    if (!$project) {
-      return new Aphront404Response();
-    }
-    if ($slug && $slug != $project->getPrimarySlug()) {
-      return id(new AphrontRedirectResponse())
-        ->setURI('/tag/'.$project->getPrimarySlug().'/');
-    }
+
+    $project = $this->getProject();
+    $id = $project->getID();
 
     $picture = $project->getProfileImageURI();
 
@@ -60,15 +46,15 @@
 
     $nav = $this->buildIconNavView($project);
     $nav->selectFilter("profile/{$id}/");
-    $nav->appendChild($object_box);
-    $nav->appendChild($timeline);
+    $crumbs = $this->buildApplicationCrumbs();
 
-    return $this->buildApplicationPage(
-        $nav,
-      array(
-        'title' => $project->getName(),
-        'pageObjects' => array($project->getPHID()),
-      ));
+    return $this->newPage()
+        ->setNavigation($nav)
+        ->setCrumbs($crumbs)
+        ->setTitle($project->getName())
+        ->setPageObjectPHIDs(array($project->getPHID()))
+        ->appendChild($object_box)
+        ->appendChild($timeline);
   }
 
   private function buildActionListView(PhabricatorProject $project) {
@@ -79,8 +65,7 @@
 
     $view = id(new PhabricatorActionListView())
       ->setUser($viewer)
-      ->setObject($project)
-      ->setObjectURI($request->getRequestURI());
+      ->setObject($project);
 
     $can_edit = PhabricatorPolicyFilter::hasCapability(
       $viewer,
@@ -91,7 +76,7 @@
         id(new PhabricatorActionView())
             ->setName(pht('Edit Details'))
             ->setIcon('fa-pencil')
-            ->setHref($this->getApplicationURI("details/{$id}/")));
+            ->setHref($this->getApplicationURI("edit/{$id}/")));
 
     $view->addAction(
         id(new PhabricatorActionView())
@@ -217,5 +202,70 @@
     return $view;
   }
 
+  protected function loadProject() {
+    $viewer = $this->getViewer();
+    $request = $this->getRequest();
+
+    $id = $request->getURIData('id');
+    $slug = $request->getURIData('slug');
+
+    if ($slug) {
+      $normal_slug = PhabricatorSlug::normalizeProjectSlug($slug);
+      $is_abnormal = ($slug !== $normal_slug);
+      $normal_uri = "/tag/{$normal_slug}/";
+    } else {
+      $is_abnormal = false;
+    }
+
+    $query = id(new PhabricatorProjectQuery())
+        ->setViewer($viewer)
+        ->needMembers(true)
+        ->needWatchers(true)
+        ->needImages(true)
+        ->needSlugs(true);
+
+    if ($slug) {
+      $query->withSlugs(array($slug));
+    } else {
+      $query->withIDs(array($id));
+    }
+
+    $policy_exception = null;
+    try {
+      $project = $query->executeOne();
+    } catch (PhabricatorPolicyException $ex) {
+      $policy_exception = $ex;
+      $project = null;
+    }
+
+    if (!$project) {
+      // This project legitimately does not exist, so just 404 the user.
+      if (!$policy_exception) {
+        return new Aphront404Response();
+      }
+
+      // Here, the project exists but the user can't see it. If they are
+      // using a non-canonical slug to view the project, redirect to the
+      // canonical slug. If they're already using the canonical slug, rethrow
+      // the exception to give them the policy error.
+      if ($is_abnormal) {
+        return id(new AphrontRedirectResponse())->setURI($normal_uri);
+      } else {
+        throw $policy_exception;
+      }
+    }
+
+    // The user can view the project, but is using a noncanonical slug.
+    // Redirect to the canonical slug.
+    $primary_slug = $project->getPrimarySlug();
+    if ($slug && ($slug !== $primary_slug)) {
+      $primary_uri = "/tag/{$primary_slug}/";
+      return id(new AphrontRedirectResponse())->setURI($primary_uri);
+    }
+
+    $this->setProject($project);
+
+    return null;
+  }
 
 }
diff --git a/src/controller/board/SprintBoardTaskEditController.php 
b/src/controller/board/SprintBoardTaskEditController.php
index 60529d4..755e95f 100644
--- a/src/controller/board/SprintBoardTaskEditController.php
+++ b/src/controller/board/SprintBoardTaskEditController.php
@@ -3,782 +3,13 @@
 final class SprintBoardTaskEditController extends ManiphestController {
 
   public function handleRequest(AphrontRequest $request) {
-    $viewer = $this->getViewer();
-    $id = $request->getURIData('id');
-
-    $response_type = $request->getStr('responseType', 'task');
-    $order = $request->getStr('order', 
PhabricatorProjectColumn::DEFAULT_ORDER);
-
-    $can_edit_assign = $this->hasApplicationCapability(
-      ManiphestEditAssignCapability::CAPABILITY);
-    $can_edit_policies = $this->hasApplicationCapability(
-      ManiphestEditPoliciesCapability::CAPABILITY);
-    $can_edit_priority = $this->hasApplicationCapability(
-      ManiphestEditPriorityCapability::CAPABILITY);
-    $can_edit_projects = $this->hasApplicationCapability(
-      ManiphestEditProjectsCapability::CAPABILITY);
-    $can_edit_status = $this->hasApplicationCapability(
-      ManiphestEditStatusCapability::CAPABILITY);
-    $can_create_projects = PhabricatorPolicyFilter::hasCapability(
-        $viewer,
-        PhabricatorApplication::getByClass('PhabricatorProjectApplication'),
-        ProjectCreateProjectsCapability::CAPABILITY);
-
-    $parent_task = null;
-    $template_id = null;
-
-    if ($id) {
-      $task = id(new ManiphestTaskQuery())
-        ->setViewer($viewer)
-        ->requireCapabilities(
-          array(
-            PhabricatorPolicyCapability::CAN_VIEW,
-            PhabricatorPolicyCapability::CAN_EDIT,
-          ))
-        ->withIDs(array($id))
-        ->needSubscriberPHIDs(true)
-        ->needProjectPHIDs(true)
-        ->executeOne();
-      if (!$task) {
-        return new Aphront404Response();
-      }
-    } else {
-      $task = ManiphestTask::initializeNewTask($viewer);
-
-      // We currently do not allow you to set the task status when creating
-      // a new task, although now that statuses are custom it might make
-      // sense.
-      $can_edit_status = false;
-
-      // These allow task creation with defaults.
-      if (!$request->isFormPost()) {
-        $task->setTitle($request->getStr('title'));
-
-        if ($can_edit_projects) {
-          $projects = $request->getStr('projects');
-          if ($projects) {
-            $tokens = $request->getStrList('projects');
-
-            $type_project = PhabricatorProjectProjectPHIDType::TYPECONST;
-            foreach ($tokens as $key => $token) {
-              if (phid_get_type($token) == $type_project) {
-                // If this is formatted like a PHID, leave it as-is.
-                continue;
-              }
-
-              if (preg_match('/^#/', $token)) {
-                // If this already has a "#", leave it as-is.
-                continue;
-              }
-
-              // Add a "#" prefix.
-              $tokens[$key] = '#'.$token;
-            }
-
-            $default_projects = id(new PhabricatorObjectQuery())
-              ->setViewer($viewer)
-              ->withNames($tokens)
-              ->execute();
-            $default_projects = mpull($default_projects, 'getPHID');
-
-            if ($default_projects) {
-              $task->attachProjectPHIDs($default_projects);
-            }
-          }
-        }
-
-        if ($can_edit_priority) {
-          $priority = $request->getInt('priority');
-          if ($priority !== null) {
-            $priority_map = ManiphestTaskPriority::getTaskPriorityMap();
-            if (isset($priority_map[$priority])) {
-                $task->setPriority($priority);
-            }
-          }
-        }
-
-        $task->setDescription($request->getStr('description'));
-
-        if ($can_edit_assign) {
-          $assign = $request->getStr('assign');
-          if (strlen($assign)) {
-            $assign_user = id(new PhabricatorPeopleQuery())
-              ->setViewer($viewer)
-              ->withUsernames(array($assign))
-              ->executeOne();
-            if (!$assign_user) {
-              $assign_user = id(new PhabricatorPeopleQuery())
-                ->setViewer($viewer)
-                ->withPHIDs(array($assign))
-                ->executeOne();
-            }
-
-            if ($assign_user) {
-              $task->setOwnerPHID($assign_user->getPHID());
-            }
-          }
-        }
-      }
-
-      $template_id = $request->getInt('template');
-
-      // You can only have a parent task if you're creating a new task.
-      $parent_id = $request->getInt('parent');
-      if (strlen($parent_id)) {
-        $parent_task = id(new ManiphestTaskQuery())
-          ->setViewer($viewer)
-          ->withIDs(array($parent_id))
-          ->executeOne();
-        if (!$parent_task) {
-          return new Aphront404Response();
-        }
-        if (!$template_id) {
-          $template_id = $parent_id;
-        }
-      }
-    }
-
-    $errors = array();
-    $e_title = true;
-
-    $field_list = PhabricatorCustomField::getObjectFields(
-      $task,
-      PhabricatorCustomField::ROLE_EDIT);
-    $field_list->setViewer($viewer);
-    $field_list->readFieldsFromStorage($task);
-
-    $aux_fields = $field_list->getFields();
-
-    $v_space = $task->getSpacePHID();
-
-    if ($request->isFormPost()) {
-      $changes = array();
-
-      $new_title = $request->getStr('title');
-      $new_desc = $request->getStr('description');
-      $new_status = $request->getStr('status');
-      $v_space = $request->getStr('spacePHID');
-
-      if (!$task->getID()) {
-        $workflow = 'create';
-      } else {
-        $workflow = '';
-      }
-
-      $changes[ManiphestTransaction::TYPE_TITLE] = $new_title;
-      $changes[ManiphestTransaction::TYPE_DESCRIPTION] = $new_desc;
-
-      if ($can_edit_status) {
-        $changes[ManiphestTransaction::TYPE_STATUS] = $new_status;
-      } else if (!$task->getID()) {
-        // Create an initial status transaction for the burndown chart.
-        // TODO: We can probably remove this once Facts comes online.
-        $changes[ManiphestTransaction::TYPE_STATUS] = $task->getStatus();
-      }
-
-      $owner_tokenizer = $request->getArr('assigned_to');
-      $owner_phid = reset($owner_tokenizer);
-
-      if (!strlen($new_title)) {
-        $e_title = pht('Required');
-        $errors[] = pht('Title is required.');
-      }
-
-      $old_values = array();
-      foreach ($aux_fields as $aux_arr_key => $aux_field) {
-        // TODO: This should be buildFieldTransactionsFromRequest() once we
-        // switch to ApplicationTransactions properly.
-
-        $aux_old_value = $aux_field->getOldValueForApplicationTransactions();
-        $aux_field->readValueFromRequest($request);
-        $aux_new_value = $aux_field->getNewValueForApplicationTransactions();
-
-        // TODO: We're faking a call to the ApplicaitonTransaction validation
-        // logic here. We need valid objects to pass, but they aren't used
-        // in a meaningful way. For now, build User objects. Once the Maniphest
-        // objects exist, this will switch over automatically. This is a big
-        // hack but shouldn't be long for this world.
-        $placeholder_editor = id(new PhabricatorUserProfileEditor())
-            ->setActor($viewer);
-
-        $field_errors = $aux_field->validateApplicationTransactions(
-          $placeholder_editor,
-          PhabricatorTransactions::TYPE_CUSTOMFIELD,
-          array(
-            id(new ManiphestTransaction())
-              ->setOldValue($aux_old_value)
-              ->setNewValue($aux_new_value),
-          ));
-
-        foreach ($field_errors as $error) {
-          $errors[] = $error->getMessage();
-        }
-
-        $old_values[$aux_field->getFieldKey()] = $aux_old_value;
-      }
-
-      if ($errors) {
-        $task->setTitle($new_title);
-        $task->setDescription($new_desc);
-        $task->setPriority($request->getInt('priority'));
-        $task->setOwnerPHID($owner_phid);
-        $task->attachSubscriberPHIDs($request->getArr('cc'));
-        $task->attachProjectPHIDs($request->getArr('projects'));
-      } else {
-
-        if ($can_edit_priority) {
-          $changes[ManiphestTransaction::TYPE_PRIORITY] =
-            $request->getInt('priority');
-        }
-        if ($can_edit_assign) {
-          $changes[ManiphestTransaction::TYPE_OWNER] = $owner_phid;
-        }
-
-        $changes[PhabricatorTransactions::TYPE_SUBSCRIBERS] =
-          array('=' => $request->getArr('cc'));
-
-        if ($can_edit_projects) {
-          $projects = $request->getArr('projects');
-          $changes[PhabricatorTransactions::TYPE_EDGE] =
-            $projects;
-          $column_phid = $request->getStr('columnPHID');
-          // allow for putting a task in a project column at creation -only-
-          if (!$task->getID() && $column_phid && $projects) {
-            $column = id(new PhabricatorProjectColumnQuery())
-              ->setViewer($viewer)
-              ->withProjectPHIDs($projects)
-              ->withPHIDs(array($column_phid))
-              ->executeOne();
-            if ($column) {
-              $changes[ManiphestTransaction::TYPE_PROJECT_COLUMN] =
-                array(
-                  'new' => array(
-                    'projectPHID' => $column->getProjectPHID(),
-                    'columnPHIDs' => array($column_phid),
-                  ),
-                  'old' => array(
-                    'projectPHID' => $column->getProjectPHID(),
-                    'columnPHIDs' => array(),
-                  ),
-                );
-            }
-          }
-        }
-
-        if ($can_edit_policies) {
-          $changes[PhabricatorTransactions::TYPE_SPACE] = $v_space;
-          $changes[PhabricatorTransactions::TYPE_VIEW_POLICY] =
-            $request->getStr('viewPolicy');
-          $changes[PhabricatorTransactions::TYPE_EDIT_POLICY] =
-            $request->getStr('editPolicy');
-        }
-
-        $template = new ManiphestTransaction();
-        $transactions = array();
-
-        foreach ($changes as $type => $value) {
-          $transaction = clone $template;
-          $transaction->setTransactionType($type);
-          if ($type == ManiphestTransaction::TYPE_PROJECT_COLUMN) {
-            $transaction->setNewValue($value['new']);
-            $transaction->setOldValue($value['old']);
-          } else if ($type == PhabricatorTransactions::TYPE_EDGE) {
-            $project_type =
-              PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
-            $transaction
-              ->setMetadataValue('edge:type', $project_type)
-              ->setNewValue(
-                array(
-                  '=' => array_fuse($value),
-                ));
-          } else {
-            $transaction->setNewValue($value);
-          }
-          $transactions[] = $transaction;
-        }
-
-        if ($aux_fields) {
-          foreach ($aux_fields as $aux_field) {
-            $transaction = clone $template;
-            $transaction->setTransactionType(
-              PhabricatorTransactions::TYPE_CUSTOMFIELD);
-            $aux_key = $aux_field->getFieldKey();
-            $transaction->setMetadataValue('customfield:key', $aux_key);
-            $old = idx($old_values, $aux_key);
-            $new = $aux_field->getNewValueForApplicationTransactions();
-
-            $transaction->setOldValue($old);
-            $transaction->setNewValue($new);
-
-            $transactions[] = $transaction;
-          }
-        }
-
-        if ($transactions) {
-          $is_new = !$task->getID();
-
-          $event = new PhabricatorEvent(
-            PhabricatorEventType::TYPE_MANIPHEST_WILLEDITTASK,
-            array(
-              'task'          => $task,
-              'new'           => $is_new,
-              'transactions'  => $transactions,
-            ));
-          $event->setUser($viewer);
-          $event->setAphrontRequest($request);
-          PhutilEventEngine::dispatchEvent($event);
-
-          $task = $event->getValue('task');
-          $transactions = $event->getValue('transactions');
-
-          $editor = id(new ManiphestTransactionEditor())
-            ->setActor($viewer)
-            ->setContentSourceFromRequest($request)
-            ->setContinueOnNoEffect(true)
-            ->applyTransactions($task, $transactions);
-
-          $event = new PhabricatorEvent(
-            PhabricatorEventType::TYPE_MANIPHEST_DIDEDITTASK,
-            array(
-              'task'          => $task,
-              'new'           => $is_new,
-              'transactions'  => $transactions,
-            ));
-          $event->setUser($viewer);
-          $event->setAphrontRequest($request);
-          PhutilEventEngine::dispatchEvent($event);
-        }
-
-
-        if ($parent_task) {
-          // TODO: This should be transactional now.
-          id(new PhabricatorEdgeEditor())
-            ->addEdge(
-              $parent_task->getPHID(),
-                ManiphestTaskDependsOnTaskEdgeType::EDGECONST,
-              $task->getPHID())
-            ->save();
-          $workflow = $parent_task->getID();
-        }
-
-        if ($request->isAjax()) {
-          switch ($response_type) {
-            case 'card':
-              $owner = null;
-              if ($task->getOwnerPHID()) {
-                $owner = id(new PhabricatorHandleQuery())
-                  ->setViewer($viewer)
-                  ->withPHIDs(array($task->getOwnerPHID()))
-                  ->executeOne();
-              }
-
-              $project = $this->getSprintProjectforTask($viewer, $projects);
-
-              $tasks = id(new SprintBoardTaskCard())
-                ->setViewer($viewer)
-                ->setProject($project)
-                ->setTask($task)
-                ->setOwner($owner)
-                ->setCanEdit(true)
-                ->getItem();
-
-              $column = id(new PhabricatorProjectColumnQuery())
-                ->setViewer($viewer)
-                ->withPHIDs(array($request->getStr('columnPHID')))
-                ->executeOne();
-              if (!$column) {
-                return new Aphront404Response();
-              }
-
-              // re-load projects for accuracy as they are not re-loaded via
-              // the editor
-              $project_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
-                  $task->getPHID(),
-                  PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
-              $task->attachProjectPHIDs($project_phids);
-              $remove_from_board = false;
-              if (!in_array($column->getProjectPHID(), $project_phids)) {
-                $remove_from_board = true;
-              }
-
-              $positions = id(new PhabricatorProjectColumnPositionQuery())
-                ->setViewer($viewer)
-                ->withColumns(array($column))
-                ->execute();
-              $task_phids = mpull($positions, 'getObjectPHID');
-
-              $column_tasks = id(new ManiphestTaskQuery())
-                ->setViewer($viewer)
-                ->withPHIDs($task_phids)
-                ->execute();
-
-              if ($order == PhabricatorProjectColumn::ORDER_NATURAL) {
-                // TODO: This is a little bit awkward, because PHP and JS use
-                // slightly different sort order parameters to achieve the same
-                // effect. It would be good to unify this a bit at some point.
-                $sort_map = array();
-                foreach ($positions as $position) {
-                  $sort_map[$position->getObjectPHID()] = array(
-                    -$position->getSequence(),
-                    $position->getID(),
-                  );
-                }
-              } else {
-                $sort_map = mpull(
-                  $column_tasks,
-                  'getPrioritySortVector',
-                  'getPHID');
-              }
-
-              $data = array(
-                'sortMap' => $sort_map,
-                'removeFromBoard' => $remove_from_board,
-              );
-              break;
-            case 'task':
-            default:
-              $tasks = $this->renderSingleTask($task);
-              $data = array();
-              break;
-          }
-          return id(new AphrontAjaxResponse())->setContent(
-            array(
-              'tasks' => $tasks,
-              'data' => $data,
-            ));
-        }
-
-        $redirect_uri = '/T'.$task->getID();
-
-        if ($workflow) {
-          $redirect_uri .= '?workflow='.$workflow;
-        }
-
-        return id(new AphrontRedirectResponse())
-          ->setURI($redirect_uri);
-      }
-    } else {
-      if (!$task->getID()) {
-        $task->attachSubscriberPHIDs(array(
-          $viewer->getPHID(),
-        ));
-        if ($template_id) {
-          $template_task = id(new ManiphestTaskQuery())
-            ->setViewer($viewer)
-            ->withIDs(array($template_id))
-            ->needSubscriberPHIDs(true)
-            ->needProjectPHIDs(true)
-            ->executeOne();
-          if ($template_task) {
-            $cc_phids = array_unique(array_merge(
-              $template_task->getSubscriberPHIDs(),
-              array($viewer->getPHID())));
-            $task->attachSubscriberPHIDs($cc_phids);
-            $task->attachProjectPHIDs($template_task->getProjectPHIDs());
-            $task->setOwnerPHID($template_task->getOwnerPHID());
-            $task->setPriority($template_task->getPriority());
-            $task->setViewPolicy($template_task->getViewPolicy());
-            $task->setEditPolicy($template_task->getEditPolicy());
-
-            $v_space = $template_task->getSpacePHID();
-
-            $template_fields = PhabricatorCustomField::getObjectFields(
-              $template_task,
-              PhabricatorCustomField::ROLE_EDIT);
-
-            $fields = $template_fields->getFields();
-            foreach ($fields as $key => $field) {
-              if (!$field->shouldCopyWhenCreatingSimilarTask()) {
-                unset($fields[$key]);
-              }
-              if (empty($aux_fields[$key])) {
-                unset($fields[$key]);
-              }
-            }
-
-            if ($fields) {
-              id(new PhabricatorCustomFieldList($fields))
-                ->setViewer($viewer)
-                ->readFieldsFromStorage($template_task);
-
-              foreach ($fields as $key => $field) {
-                $aux_fields[$key]->setValueFromStorage(
-                  $field->getValueForStorage());
-              }
-            }
-          }
-        }
-      }
-    }
-
-    $error_view = null;
-    if ($errors) {
-      $error_view = new PHUIInfoView();
-      $error_view->setErrors($errors);
-    }
-
-    $priority_map = ManiphestTaskPriority::getTaskPriorityMap();
-
-    if ($task->getOwnerPHID()) {
-      $assigned_value = array($task->getOwnerPHID());
-    } else {
-      $assigned_value = array();
-    }
-
-    if ($task->getSubscriberPHIDs()) {
-      $cc_value = $task->getSubscriberPHIDs();
-    } else {
-      $cc_value = array();
-    }
-
-    if ($task->getProjectPHIDs()) {
-      $projects_value = $task->getProjectPHIDs();
-    } else {
-      $projects_value = array();
-    }
-
-    $cancel_id = nonempty($task->getID(), $template_id);
-    if ($cancel_id) {
-      $cancel_uri = '/T'.$cancel_id;
-    } else {
-      $cancel_uri = '/maniphest/';
-    }
-
-    if ($task->getID()) {
-      $button_name = pht('Save Task');
-      $header_name = pht('Edit Task');
-    } else if ($parent_task) {
-      $cancel_uri = '/T'.$parent_task->getID();
-      $button_name = pht('Create Task');
-      $header_name = pht('Create New Subtask');
-    } else {
-      $button_name = pht('Create Task');
-      $header_name = pht('Create New Task');
-    }
-
-    require_celerity_resource('maniphest-task-edit-css');
-
-    $project_tokenizer_id = celerity_generate_unique_node_id();
-
-    $form = new AphrontFormView();
-    $form
-      ->setUser($viewer)
-      ->addHiddenInput('template', $template_id)
-      ->addHiddenInput('responseType', $response_type)
-      ->addHiddenInput('order', $order)
-      ->addHiddenInput('ungrippable', $request->getStr('ungrippable'))
-      ->addHiddenInput('columnPHID', $request->getStr('columnPHID'));
-
-    if ($parent_task) {
-      $form
-        ->appendChild(
-          id(new AphrontFormStaticControl())
-            ->setLabel(pht('Parent Task'))
-            ->setValue($viewer->renderHandle($parent_task->getPHID())))
-        ->addHiddenInput('parent', $parent_task->getID());
-    }
-
-    $form
-      ->appendChild(
-        id(new AphrontFormTextAreaControl())
-          ->setLabel(pht('Title'))
-          ->setName('title')
-          ->setError($e_title)
-          ->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_SHORT)
-          ->setValue($task->getTitle()));
-
-    if ($can_edit_status) {
-      // See T4819.
-      $status_map = ManiphestTaskStatus::getTaskStatusMap();
-      $dup_status = ManiphestTaskStatus::getDuplicateStatus();
-
-      if ($task->getStatus() != $dup_status) {
-        unset($status_map[$dup_status]);
-      }
-
-      $form
-        ->appendChild(
-          id(new AphrontFormSelectControl())
-            ->setLabel(pht('Status'))
-            ->setName('status')
-            ->setValue($task->getStatus())
-            ->setOptions($status_map));
-    }
-
-    $policies = id(new PhabricatorPolicyQuery())
-      ->setViewer($viewer)
-      ->setObject($task)
-      ->execute();
-
-    if ($can_edit_assign) {
-      $form->appendControl(
-        id(new AphrontFormTokenizerControl())
-          ->setLabel(pht('Assigned To'))
-          ->setName('assigned_to')
-          ->setValue($assigned_value)
-          ->setUser($viewer)
-          ->setDatasource(new PhabricatorPeopleDatasource())
-          ->setLimit(1));
-    }
-
-    $form
-      ->appendControl(
-        id(new AphrontFormTokenizerControl())
-          ->setLabel(pht('CC'))
-          ->setName('cc')
-          ->setValue($cc_value)
-          ->setUser($viewer)
-          ->setDatasource(new PhabricatorMetaMTAMailableDatasource()));
-
-    if ($can_edit_priority) {
-      $form
-        ->appendChild(
-          id(new AphrontFormSelectControl())
-            ->setLabel(pht('Priority'))
-            ->setName('priority')
-            ->setOptions($priority_map)
-            ->setValue($task->getPriority()));
-    }
-
-    if ($can_edit_policies) {
-      $form
-        ->appendChild(
-          id(new AphrontFormPolicyControl())
-            ->setUser($viewer)
-            ->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
-            ->setPolicyObject($task)
-            ->setPolicies($policies)
-            ->setSpacePHID($v_space)
-            ->setName('viewPolicy'))
-        ->appendChild(
-          id(new AphrontFormPolicyControl())
-            ->setUser($viewer)
-            ->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
-            ->setPolicyObject($task)
-            ->setPolicies($policies)
-            ->setName('editPolicy'));
-    }
-
-    if ($can_edit_projects) {
-      $caption = null;
-      if ($can_create_projects) {
-        $caption = javelin_tag(
-            'a',
-            array(
-                'href'        => '/project/create/',
-                'mustcapture' => true,
-                'sigil'       => 'project-create',
-            ),
-            pht('Create New Project'));
-      }
-      $form
-        ->appendControl(
-          id(new AphrontFormTokenizerControl())
-            ->setLabel(pht('Projects'))
-            ->setName('projects')
-            ->setValue($projects_value)
-            ->setID($project_tokenizer_id)
-            ->setCaption($caption)
-            ->setDatasource(new PhabricatorProjectDatasource()));
-    }
-
-    $field_list->appendFieldsToForm($form);
-
-    require_celerity_resource('phui-info-view-css');
-
-    Javelin::initBehavior('project-create', array(
-      'tokenizerID' => $project_tokenizer_id,
-    ));
-
-    $description_control = id(new PhabricatorRemarkupControl())
-      ->setLabel(pht('Description'))
-      ->setName('description')
-      ->setID('description-textarea')
-      ->setValue($task->getDescription())
-      ->setUser($viewer);
-
-    $form
-      ->appendChild($description_control);
-
-    if ($request->isAjax()) {
-      $dialog = id(new AphrontDialogView())
-        ->setUser($viewer)
-        ->setWidth(AphrontDialogView::WIDTH_FULL)
-        ->setTitle($header_name)
-        ->appendChild(
-          array(
-            $error_view,
-            $form->buildLayoutView(),
-          ))
-        ->addCancelButton($cancel_uri)
-        ->addSubmitButton($button_name);
-      return id(new AphrontDialogResponse())->setDialog($dialog);
-    }
-
-    $form
-      ->appendChild(
-        id(new AphrontFormSubmitControl())
-          ->addCancelButton($cancel_uri)
-          ->setValue($button_name));
-
-    $form_box = id(new PHUIObjectBoxView())
-      ->setHeaderText($header_name)
-      ->setFormErrors($errors)
-      ->setForm($form);
-
-    $preview = id(new PHUIRemarkupPreviewPanel())
-      ->setHeader(pht('Description Preview'))
-      ->setControlID('description-textarea')
-      ->setPreviewURI($this->getApplicationURI('task/descriptionpreview/'));
-
-    if ($task->getID()) {
-      $page_objects = array($task->getPHID());
-    } else {
-      $page_objects = array();
-    }
-
-    $crumbs = $this->buildApplicationCrumbs();
-
-    if ($task->getID()) {
-      $crumbs->addTextCrumb('T'.$task->getID(), '/T'.$task->getID());
-    }
-
-    $crumbs->addTextCrumb($header_name);
-
-    $title = $header_name;
-
-    return $this->newPage()
-        ->setTitle($title)
-        ->setCrumbs($crumbs)
-        ->setPageObjectPHIDs($page_objects)
-        ->appendChild(
-            array(
-                $form_box,
-                $preview,
-            ));
-  }
-
-  private function getSprintProjectforTask($viewer, $projects) {
-    $project = null;
-
-    if ($projects) {
-      $query = id(new PhabricatorProjectQuery())
-          ->setViewer($viewer)
-          ->withPHIDs($projects);
-    } else {
-      return null;
-    }
-
-    $projects = $query->execute();
-
-    foreach ($projects as $project) {
-      $sprintquery = id(new SprintQuery())
-          ->setPHID($project->getPHID());
-      if ($sprintquery->getIsSprint()) {
-        return $project;
-      }
-    }
-
+    return id(new SprintManiphestEditEngine())
+        ->setController($this)
+        ->addContextParameter('ungrippable')
+        ->addContextParameter('responseType')
+        ->addContextParameter('columnPHID')
+        ->addContextParameter('order')
+        ->buildResponse();
   }
 
 }
diff --git a/src/controller/board/SprintManiphestEditEngine.php 
b/src/controller/board/SprintManiphestEditEngine.php
new file mode 100644
index 0000000..cce13c9
--- /dev/null
+++ b/src/controller/board/SprintManiphestEditEngine.php
@@ -0,0 +1,375 @@
+<?php
+
+final class SprintManiphestEditEngine
+    extends PhabricatorEditEngine {
+
+  const ENGINECONST = 'sprint.maniphest.task';
+
+  public function getEngineName() {
+    return pht('Maniphest Tasks');
+  }
+
+  public function getSummaryHeader() {
+    return pht('Configure Maniphest Task Forms');
+  }
+
+  public function getSummaryText() {
+    return pht('Configure how users create and edit tasks.');
+  }
+
+  public function getEngineApplicationClass() {
+    return 'PhabricatorManiphestApplication';
+  }
+
+  protected function newEditableObject() {
+    return ManiphestTask::initializeNewTask($this->getViewer());
+  }
+
+  protected function newObjectQuery() {
+    return id(new ManiphestTaskQuery());
+  }
+
+  protected function getObjectCreateTitleText($object) {
+    return pht('Create New Task');
+  }
+
+  protected function getObjectEditTitleText($object) {
+    return pht('Edit %s %s', $object->getMonogram(), $object->getTitle());
+  }
+
+  protected function getObjectEditShortText($object) {
+    return $object->getMonogram();
+  }
+
+  protected function getObjectCreateShortText() {
+    return pht('Create Task');
+  }
+
+  protected function getEditorURI() {
+    return $this->getApplication()->getApplicationURI('task/edit/');
+  }
+
+  protected function getCommentViewHeaderText($object) {
+    return pht('Weigh In');
+  }
+
+  protected function getCommentViewButtonText($object) {
+    return pht('Set Sail for Adventure');
+  }
+
+  protected function getObjectViewURI($object) {
+    return '/'.$object->getMonogram();
+  }
+
+  protected function buildCustomEditFields($object) {
+    $status_map = $this->getTaskStatusMap($object);
+    $priority_map = $this->getTaskPriorityMap($object);
+
+    if ($object->isClosed()) {
+      $default_status = ManiphestTaskStatus::getDefaultStatus();
+    } else {
+      $default_status = ManiphestTaskStatus::getDefaultClosedStatus();
+    }
+
+    if ($object->getOwnerPHID()) {
+      $owner_value = array($object->getOwnerPHID());
+    } else {
+      $owner_value = array($this->getViewer()->getPHID());
+    }
+
+    return array(
+        id(new PhabricatorHandlesEditField())
+            ->setKey('parent')
+            ->setLabel(pht('Parent Task'))
+            ->setDescription(pht('Task to make this a subtask of.'))
+            ->setConduitDescription(pht('Create as a subtask of another 
task.'))
+            ->setConduitTypeDescription(pht('PHID of the parent task.'))
+            ->setAliases(array('parentPHID'))
+            ->setTransactionType(ManiphestTransaction::TYPE_PARENT)
+            ->setHandleParameterType(new ManiphestTaskListHTTPParameterType())
+            ->setSingleValue(null)
+            ->setIsReorderable(false)
+            ->setIsDefaultable(false)
+            ->setIsLockable(false),
+        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)
+            ->setSingleValue(null)
+            ->setIsInvisible(true)
+            ->setIsReorderable(false)
+            ->setIsDefaultable(false)
+            ->setIsLockable(false),
+        id(new PhabricatorTextEditField())
+            ->setKey('title')
+            ->setLabel(pht('Title'))
+            ->setDescription(pht('Name of the task.'))
+            ->setConduitDescription(pht('Rename the task.'))
+            ->setConduitTypeDescription(pht('New task name.'))
+            ->setTransactionType(ManiphestTransaction::TYPE_TITLE)
+            ->setIsRequired(true)
+            ->setValue($object->getTitle()),
+        id(new PhabricatorUsersEditField())
+            ->setKey('owner')
+            ->setAliases(array('ownerPHID', 'assign', 'assigned'))
+            ->setLabel(pht('Assigned To'))
+            ->setDescription(pht('User who is responsible for the task.'))
+            ->setConduitDescription(pht('Reassign the task.'))
+            ->setConduitTypeDescription(
+                pht('New task owner, or `null` to unassign.'))
+            ->setTransactionType(ManiphestTransaction::TYPE_OWNER)
+            ->setIsCopyable(true)
+            ->setSingleValue($object->getOwnerPHID())
+            ->setCommentActionLabel(pht('Assign / Claim'))
+            ->setCommentActionValue($owner_value),
+        id(new PhabricatorSelectEditField())
+            ->setKey('status')
+            ->setLabel(pht('Status'))
+            ->setDescription(pht('Status of the task.'))
+            ->setConduitDescription(pht('Change the task status.'))
+            ->setConduitTypeDescription(pht('New task status constant.'))
+            ->setTransactionType(ManiphestTransaction::TYPE_STATUS)
+            ->setIsCopyable(true)
+            ->setValue($object->getStatus())
+            ->setOptions($status_map)
+            ->setCommentActionLabel(pht('Change Status'))
+            ->setCommentActionValue($default_status),
+        id(new PhabricatorSelectEditField())
+            ->setKey('priority')
+            ->setLabel(pht('Priority'))
+            ->setDescription(pht('Priority of the task.'))
+            ->setConduitDescription(pht('Change the priority of the task.'))
+            ->setConduitTypeDescription(pht('New task priority constant.'))
+            ->setTransactionType(ManiphestTransaction::TYPE_PRIORITY)
+            ->setIsCopyable(true)
+            ->setValue($object->getPriority())
+            ->setOptions($priority_map)
+            ->setCommentActionLabel(pht('Change Priority')),
+        id(new PhabricatorRemarkupEditField())
+            ->setKey('description')
+            ->setLabel(pht('Description'))
+            ->setDescription(pht('Task description.'))
+            ->setConduitDescription(pht('Update the task description.'))
+            ->setConduitTypeDescription(pht('New task description.'))
+            ->setTransactionType(ManiphestTransaction::TYPE_DESCRIPTION)
+            ->setValue($object->getDescription())
+            ->setPreviewPanel(
+                id(new PHUIRemarkupPreviewPanel())
+                    ->setHeader(pht('Description Preview'))),
+    );
+  }
+
+  private function getTaskStatusMap(ManiphestTask $task) {
+    $status_map = ManiphestTaskStatus::getTaskStatusMap();
+
+    $current_status = $task->getStatus();
+
+    // If the current status is something we don't recognize (maybe an older
+    // status which was deleted), put a dummy entry in the status map so that
+    // saving the form doesn't destroy any data by accident.
+    if (idx($status_map, $current_status) === null) {
+      $status_map[$current_status] = pht('<Unknown: %s>', $current_status);
+    }
+
+    $dup_status = ManiphestTaskStatus::getDuplicateStatus();
+    foreach ($status_map as $status => $status_name) {
+      // Always keep the task's current status.
+      if ($status == $current_status) {
+        continue;
+      }
+
+      // Don't allow tasks to be changed directly into "Closed, Duplicate"
+      // status. Instead, you have to merge them. See T4819.
+      if ($status == $dup_status) {
+        unset($status_map[$status]);
+        continue;
+      }
+
+      // Don't let new or existing tasks be moved into a disabled status.
+      if (ManiphestTaskStatus::isDisabledStatus($status)) {
+        unset($status_map[$status]);
+        continue;
+      }
+    }
+
+    return $status_map;
+  }
+
+  private function getTaskPriorityMap(ManiphestTask $task) {
+    $priority_map = ManiphestTaskPriority::getTaskPriorityMap();
+    $current_priority = $task->getPriority();
+
+    // If the current value isn't a legitimate one, put it in the dropdown
+    // anyway so saving the form doesn't cause a side effects.
+    if (idx($priority_map, $current_priority) === null) {
+      $priority_map[$current_priority] = pht(
+          '<Unknown: %s>',
+          $current_priority);
+    }
+
+    foreach ($priority_map as $priority => $priority_name) {
+      // Always keep the current priority.
+      if ($priority == $current_priority) {
+        continue;
+      }
+
+      if (ManiphestTaskPriority::isDisabledPriority($priority)) {
+        unset($priority_map[$priority]);
+        continue;
+      }
+    }
+
+    return $priority_map;
+  }
+
+  protected function newEditResponse(
+      AphrontRequest $request,
+      $object,
+      array $xactions) {
+
+    if ($request->isAjax()) {
+      // Reload the task to make sure we pick up the final task state.
+      $viewer = $this->getViewer();
+      $task = id(new ManiphestTaskQuery())
+          ->setViewer($viewer)
+          ->withIDs(array($object->getID()))
+          ->needSubscriberPHIDs(true)
+          ->needProjectPHIDs(true)
+          ->executeOne();
+
+      switch ($request->getStr('responseType')) {
+        case 'card':
+          return $this->buildCardResponse($task);
+        default:
+          return $this->buildListResponse($task);
+      }
+
+    }
+
+    return parent::newEditResponse($request, $object, $xactions);
+  }
+
+  private function buildListResponse(ManiphestTask $task) {
+    $controller = $this->getController();
+
+    $payload = array(
+        'tasks' => $controller->renderSingleTask($task),
+        'data' => array(),
+    );
+
+    return id(new AphrontAjaxResponse())->setContent($payload);
+  }
+
+  private function buildCardResponse(ManiphestTask $task) {
+    $controller = $this->getController();
+    $request = $controller->getRequest();
+    $viewer = $request->getViewer();
+
+    $column_phid = $request->getStr('columnPHID');
+    $order = $request->getStr('order');
+
+    $column = id(new PhabricatorProjectColumnQuery())
+        ->setViewer($viewer)
+        ->withPHIDs(array($column_phid))
+        ->executeOne();
+    if (!$column) {
+      return new Aphront404Response();
+    }
+
+    // If the workboard's project has been removed from the card's project
+    // list, we are going to remove it from the board completely.
+    $project_map = array_fuse($task->getProjectPHIDs());
+    $remove_card = empty($project_map[$column->getProjectPHID()]);
+
+    $positions = id(new PhabricatorProjectColumnPositionQuery())
+        ->setViewer($viewer)
+        ->withColumns(array($column))
+        ->execute();
+    $task_phids = mpull($positions, 'getObjectPHID');
+
+    $column_tasks = id(new ManiphestTaskQuery())
+        ->setViewer($viewer)
+        ->withPHIDs($task_phids)
+        ->execute();
+
+    if ($order == PhabricatorProjectColumn::ORDER_NATURAL) {
+      // TODO: This is a little bit awkward, because PHP and JS use
+      // slightly different sort order parameters to achieve the same
+      // effect. It would be good to unify this a bit at some point.
+      $sort_map = array();
+      foreach ($positions as $position) {
+        $sort_map[$position->getObjectPHID()] = array(
+            -$position->getSequence(),
+            $position->getID(),
+        );
+      }
+    } else {
+      $sort_map = mpull(
+          $column_tasks,
+          'getPrioritySortVector',
+          'getPHID');
+    }
+
+    $data = array(
+        'removeFromBoard' => $remove_card,
+        'sortMap' => $sort_map,
+    );
+
+    // TODO: This should just use HandlePool once we get through the EditEngine
+    // transition.
+    $owner = null;
+    if ($task->getOwnerPHID()) {
+      $owner = id(new PhabricatorHandleQuery())
+          ->setViewer($viewer)
+          ->withPHIDs(array($task->getOwnerPHID()))
+          ->executeOne();
+    }
+
+    $projects = $request->getArr('projectPHIDs');
+    $project = $this->getSprintProjectforTask($viewer, $projects);
+
+    $tasks = id(new SprintBoardTaskCard())
+        ->setViewer($viewer)
+        ->setProject($project)
+        ->setTask($task)
+        ->setOwner($owner)
+        ->setCanEdit(true)
+        ->getItem();
+
+    $payload = array(
+        'tasks' => $tasks,
+        'data' => $data,
+    );
+
+    return id(new AphrontAjaxResponse())->setContent($payload);
+  }
+
+  private function getSprintProjectforTask($viewer, $projects) {
+    $project = null;
+
+    if ($projects) {
+      $query = id(new PhabricatorProjectQuery())
+          ->setViewer($viewer)
+          ->withPHIDs($projects);
+    } else {
+      return null;
+    }
+
+    $projects = $query->execute();
+
+    foreach ($projects as $project) {
+      $sprintquery = id(new SprintQuery())
+          ->setPHID($project->getPHID());
+      if ($sprintquery->getIsSprint()) {
+        return $project;
+      }
+    }
+
+  }
+
+
+}

-- 
To view, visit https://gerrit.wikimedia.org/r/261185
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I418d643a0949599ad507071fb14ffb3ff89bf102
Gerrit-PatchSet: 1
Gerrit-Project: phabricator/extensions/Sprint
Gerrit-Branch: master
Gerrit-Owner: Christopher Johnson (WMDE) <christopher.john...@wikimedia.de>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to