Page MenuHomePhabricator

D8620.id20454.diff
No OneTemporary

D8620.id20454.diff

diff --git a/resources/sql/autopatches/20140326.project.1.colxaction.sql b/resources/sql/autopatches/20140326.project.1.colxaction.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20140326.project.1.colxaction.sql
@@ -0,0 +1,21 @@
+CREATE TABLE {$NAMESPACE}_project.project_columntransaction (
+ id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ phid VARCHAR(64) NOT NULL COLLATE utf8_bin,
+ authorPHID VARCHAR(64) NOT NULL COLLATE utf8_bin,
+ objectPHID VARCHAR(64) NOT NULL COLLATE utf8_bin,
+ viewPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin,
+ editPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin,
+ commentPHID VARCHAR(64) COLLATE utf8_bin,
+ commentVersion INT UNSIGNED NOT NULL,
+ transactionType VARCHAR(32) NOT NULL COLLATE utf8_bin,
+ oldValue LONGTEXT NOT NULL COLLATE utf8_bin,
+ newValue LONGTEXT NOT NULL COLLATE utf8_bin,
+ contentSource LONGTEXT NOT NULL COLLATE utf8_bin,
+ metadata LONGTEXT NOT NULL COLLATE utf8_bin,
+ dateCreated INT UNSIGNED NOT NULL,
+ dateModified INT UNSIGNED NOT NULL,
+
+ UNIQUE KEY `key_phid` (phid),
+ KEY `key_object` (objectPHID)
+
+) ENGINE=InnoDB, COLLATE utf8_general_ci;
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1862,8 +1862,13 @@
'PhabricatorProjectBoardController' => 'applications/project/controller/PhabricatorProjectBoardController.php',
'PhabricatorProjectBoardDeleteController' => 'applications/project/controller/PhabricatorProjectBoardDeleteController.php',
'PhabricatorProjectBoardEditController' => 'applications/project/controller/PhabricatorProjectBoardEditController.php',
+ 'PhabricatorProjectBoardViewController' => 'applications/project/controller/PhabricatorProjectBoardViewController.php',
'PhabricatorProjectColumn' => 'applications/project/storage/PhabricatorProjectColumn.php',
+ 'PhabricatorProjectColumnDetailController' => 'applications/project/controller/PhabricatorProjectColumnDetailController.php',
'PhabricatorProjectColumnQuery' => 'applications/project/query/PhabricatorProjectColumnQuery.php',
+ 'PhabricatorProjectColumnTransaction' => 'applications/project/storage/PhabricatorProjectColumnTransaction.php',
+ 'PhabricatorProjectColumnTransactionEditor' => 'applications/project/editor/PhabricatorProjectColumnTransactionEditor.php',
+ 'PhabricatorProjectColumnTransactionQuery' => 'applications/project/query/PhabricatorProjectColumnTransactionQuery.php',
'PhabricatorProjectConfigOptions' => 'applications/project/config/PhabricatorProjectConfigOptions.php',
'PhabricatorProjectConfiguredCustomField' => 'applications/project/customfield/PhabricatorProjectConfiguredCustomField.php',
'PhabricatorProjectConstants' => 'applications/project/constants/PhabricatorProjectConstants.php',
@@ -4668,14 +4673,19 @@
),
'PhabricatorProjectArchiveController' => 'PhabricatorProjectController',
'PhabricatorProjectBoardController' => 'PhabricatorProjectController',
- 'PhabricatorProjectBoardDeleteController' => 'PhabricatorProjectController',
- 'PhabricatorProjectBoardEditController' => 'PhabricatorProjectController',
+ 'PhabricatorProjectBoardDeleteController' => 'PhabricatorProjectBoardController',
+ 'PhabricatorProjectBoardEditController' => 'PhabricatorProjectBoardController',
+ 'PhabricatorProjectBoardViewController' => 'PhabricatorProjectBoardController',
'PhabricatorProjectColumn' =>
array(
0 => 'PhabricatorProjectDAO',
1 => 'PhabricatorPolicyInterface',
),
+ 'PhabricatorProjectColumnDetailController' => 'PhabricatorProjectBoardController',
'PhabricatorProjectColumnQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
+ 'PhabricatorProjectColumnTransaction' => 'PhabricatorApplicationTransaction',
+ 'PhabricatorProjectColumnTransactionEditor' => 'PhabricatorApplicationTransactionEditor',
+ 'PhabricatorProjectColumnTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhabricatorProjectConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorProjectConfiguredCustomField' =>
array(
diff --git a/src/applications/project/application/PhabricatorApplicationProject.php b/src/applications/project/application/PhabricatorApplicationProject.php
--- a/src/applications/project/application/PhabricatorApplicationProject.php
+++ b/src/applications/project/application/PhabricatorApplicationProject.php
@@ -51,12 +51,14 @@
'picture/(?P<id>[1-9]\d*)/' =>
'PhabricatorProjectEditPictureController',
'create/' => 'PhabricatorProjectCreateController',
- 'board/(?P<id>[1-9]\d*)/' => 'PhabricatorProjectBoardController',
+ 'board/(?P<id>[1-9]\d*)/' => 'PhabricatorProjectBoardViewController',
'move/(?P<id>[1-9]\d*)/' => 'PhabricatorProjectMoveController',
'board/(?P<projectID>[1-9]\d*)/edit/(?:(?P<id>\d+)/)?'
=> 'PhabricatorProjectBoardEditController',
'board/(?P<projectID>[1-9]\d*)/delete/(?:(?P<id>\d+)/)?'
=> 'PhabricatorProjectBoardDeleteController',
+ 'board/(?P<projectID>[1-9]\d*)/column/(?:(?P<id>\d+)/)?'
+ => 'PhabricatorProjectColumnDetailController',
'update/(?P<id>[1-9]\d*)/(?P<action>[^/]+)/'
=> 'PhabricatorProjectUpdateController',
'history/(?P<id>[1-9]\d*)/' => 'PhabricatorProjectHistoryController',
diff --git a/src/applications/project/controller/PhabricatorProjectBoardController.php b/src/applications/project/controller/PhabricatorProjectBoardController.php
--- a/src/applications/project/controller/PhabricatorProjectBoardController.php
+++ b/src/applications/project/controller/PhabricatorProjectBoardController.php
@@ -1,219 +1,24 @@
<?php
-final class PhabricatorProjectBoardController
+abstract class PhabricatorProjectBoardController
extends PhabricatorProjectController {
- private $id;
- private $handles;
+ private $project;
- public function shouldAllowPublic() {
- return true;
+ protected function setProject(PhabricatorProject $project) {
+ $this->project = $project;
+ return $this;
}
-
- public function willProcessRequest(array $data) {
- $this->id = $data['id'];
+ protected function getProject() {
+ return $this->project;
}
- public function processRequest() {
- $request = $this->getRequest();
- $viewer = $request->getUser();
-
- $project = id(new PhabricatorProjectQuery())
- ->setViewer($viewer)
- ->needImages(true)
- ->withIDs(array($this->id))
- ->executeOne();
- if (!$project) {
- return new Aphront404Response();
- }
-
- $columns = id(new PhabricatorProjectColumnQuery())
- ->setViewer($viewer)
- ->withProjectPHIDs(array($project->getPHID()))
- ->withStatuses(array(PhabricatorProjectColumn::STATUS_ACTIVE))
- ->execute();
-
- $columns = mpull($columns, null, 'getSequence');
-
- // If there's no default column, create one now.
- if (empty($columns[0])) {
- $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
- $column = PhabricatorProjectColumn::initializeNewColumn($viewer)
- ->setSequence(0)
- ->setProjectPHID($project->getPHID())
- ->save();
- $column->attachProject($project);
- $columns[0] = $column;
- unset($unguarded);
- }
-
- ksort($columns);
-
- $tasks = id(new ManiphestTaskQuery())
- ->setViewer($viewer)
- ->withAllProjects(array($project->getPHID()))
- ->withStatuses(ManiphestTaskStatus::getOpenStatusConstants())
- ->setOrderBy(ManiphestTaskQuery::ORDER_PRIORITY)
- ->execute();
- $tasks = mpull($tasks, null, 'getPHID');
- $task_phids = array_keys($tasks);
-
- if ($task_phids) {
- $edge_type = PhabricatorEdgeConfig::TYPE_OBJECT_HAS_COLUMN;
- $edge_query = id(new PhabricatorEdgeQuery())
- ->withSourcePHIDs($task_phids)
- ->withEdgeTypes(array($edge_type))
- ->withDestinationPHIDs(mpull($columns, 'getPHID'));
- $edge_query->execute();
- }
-
- $task_map = array();
- $default_phid = $columns[0]->getPHID();
- foreach ($tasks as $task) {
- $task_phid = $task->getPHID();
- $column_phids = $edge_query->getDestinationPHIDs(array($task_phid));
-
- $column_phid = head($column_phids);
- $column_phid = nonempty($column_phid, $default_phid);
-
- $task_map[$column_phid][] = $task_phid;
- }
-
- $task_can_edit_map = id(new PhabricatorPolicyFilter())
- ->setViewer($viewer)
- ->requireCapabilities(array(PhabricatorPolicyCapability::CAN_EDIT))
- ->apply($tasks);
-
- $board_id = celerity_generate_unique_node_id();
-
- $board = id(new PHUIWorkboardView())
- ->setUser($viewer)
- ->setFluidishLayout(true)
- ->setID($board_id);
-
- $this->initBehavior(
- 'project-boards',
- array(
- 'boardID' => $board_id,
- 'projectPHID' => $project->getPHID(),
- 'moveURI' => $this->getApplicationURI('move/'.$project->getID().'/'),
- 'createURI' => '/maniphest/task/create/',
- ));
-
- $this->handles = ManiphestTaskListView::loadTaskHandles($viewer, $tasks);
-
- foreach ($columns as $column) {
- $panel = id(new PHUIWorkpanelView())
- ->setHeader($column->getDisplayName())
- ->setHeaderColor($column->getHeaderColor());
- if (!$column->isDefaultColumn()) {
- $panel->setEditURI('edit/'.$column->getID().'/');
- }
- $panel->setHeaderAction(id(new PHUIIconView())
- ->setSpriteSheet(PHUIIconView::SPRITE_ACTIONS)
- ->setSpriteIcon('new-grey')
- ->setHref('/maniphest/task/create/')
- ->addSigil('column-add-task')
- ->setMetadata(
- array('columnPHID' => $column->getPHID())));
-
- $cards = id(new PHUIObjectItemListView())
- ->setUser($viewer)
- ->setCards(true)
- ->setFlush(true)
- ->setAllowEmptyList(true)
- ->addSigil('project-column')
- ->setMetadata(
- array(
- 'columnPHID' => $column->getPHID(),
- ));
- $task_phids = idx($task_map, $column->getPHID(), array());
- foreach (array_select_keys($tasks, $task_phids) as $task) {
- $owner = null;
- if ($task->getOwnerPHID()) {
- $owner = $this->handles[$task->getOwnerPHID()];
- }
- $can_edit = idx($task_can_edit_map, $task->getPHID(), false);
- $cards->addItem(id(new ProjectBoardTaskCard())
- ->setViewer($viewer)
- ->setTask($task)
- ->setOwner($owner)
- ->setCanEdit($can_edit)
- ->getItem());
- }
- $panel->setCards($cards);
-
- if (!$task_phids) {
- $cards->addClass('project-column-empty');
- }
-
- $board->addPanel($panel);
- }
-
- $crumbs = $this->buildApplicationCrumbs();
+ protected function buildApplicationCrumbs() {
+ $project = $this->getProject();
+ $crumbs = parent::buildApplicationCrumbs();
$crumbs->addTextCrumb(
$project->getName(),
$this->getApplicationURI('view/'.$project->getID().'/'));
- $crumbs->addTextCrumb(pht('Board'));
-
- $can_edit = PhabricatorPolicyFilter::hasCapability(
- $viewer,
- $project,
- PhabricatorPolicyCapability::CAN_EDIT);
-
- $actions = id(new PhabricatorActionListView())
- ->setUser($viewer)
- ->addAction(
- id(new PhabricatorActionView())
- ->setName(pht('Add Column'))
- ->setHref($this->getApplicationURI('board/'.$this->id.'/edit/'))
- ->setIcon('create')
- ->setDisabled(!$can_edit)
- ->setWorkflow(!$can_edit))
- ->addAction(
- id(new PhabricatorActionView())
- ->setName(pht('Delete Column'))
- ->setHref($this->getApplicationURI('board/'.$this->id.'/delete/'))
- ->setIcon('delete')
- ->setDisabled(!$can_edit)
- ->setWorkflow(!$can_edit));
-
- $plist = id(new PHUIPropertyListView());
-
- // TODO: Need this to get actions to render.
- $plist->addProperty(
- pht('Project Boards'),
- phutil_tag(
- 'em',
- array(),
- pht(
- 'This feature is beta, but should mostly work.')));
- $plist->setActionList($actions);
-
- $header = id(new PHUIHeaderView())
- ->setHeader($project->getName())
- ->setUser($viewer)
- ->setImage($project->getProfileImageURI())
- ->setPolicyObject($project);
-
- $box = id(new PHUIObjectBoxView())
- ->setHeader($header)
- ->addPropertyList($plist);
-
- $board_box = id(new PHUIBoxView())
- ->appendChild($board)
- ->addMargin(PHUI::MARGIN_LARGE);
-
- return $this->buildApplicationPage(
- array(
- $crumbs,
- $box,
- $board_box,
- ),
- array(
- 'title' => pht('%s Board', $project->getName()),
- 'device' => true,
- ));
+ return $crumbs;
}
-
}
diff --git a/src/applications/project/controller/PhabricatorProjectBoardDeleteController.php b/src/applications/project/controller/PhabricatorProjectBoardDeleteController.php
--- a/src/applications/project/controller/PhabricatorProjectBoardDeleteController.php
+++ b/src/applications/project/controller/PhabricatorProjectBoardDeleteController.php
@@ -1,7 +1,7 @@
<?php
final class PhabricatorProjectBoardDeleteController
- extends PhabricatorProjectController {
+ extends PhabricatorProjectBoardController {
private $id;
private $projectID;
@@ -14,7 +14,6 @@
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
-
$project = id(new PhabricatorProjectQuery())
->setViewer($viewer)
->requireCapabilities(
@@ -28,89 +27,89 @@
if (!$project) {
return new Aphront404Response();
}
+ $this->setProject($project);
- $columns = id(new PhabricatorProjectColumnQuery())
+ $column = id(new PhabricatorProjectColumnQuery())
->setViewer($viewer)
- ->withProjectPHIDs(array($project->getPHID()))
- ->withStatuses(array(PhabricatorProjectColumn::STATUS_ACTIVE))
+ ->withIDs(array($this->id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT))
- ->execute();
-
- if (!$columns) {
+ ->executeOne();
+ if (!$column) {
return new Aphront404Response();
}
- $columns = mpull($columns, null, 'getSequence');
- $columns = mfilter($columns, 'isDefaultColumn', true);
- ksort($columns);
- $options = mpull($columns, 'getName', 'getPHID');
-
- $view_uri = $this->getApplicationURI('/board/'.$this->projectID.'/');
$error_view = null;
- if ($request->isFormPost()) {
- $columns = mpull($columns, null, 'getPHID');
- $column_phid = $request->getStr('columnPHID');
- $column = $columns[$column_phid];
-
- $has_task_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
- $column_phid,
- PhabricatorEdgeConfig::TYPE_COLUMN_HAS_OBJECT);
-
- if ($has_task_phids) {
- $error_view = id(new AphrontErrorView())
- ->setTitle(pht('Column has Tasks!'))
- ->setErrors(array(pht('A column can not be deleted if it has tasks '.
- 'in it. Please remove the tasks and try '.
- 'again.')));
+ $column_phid = $column->getPHID();
+ $has_task_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
+ $column_phid,
+ PhabricatorEdgeConfig::TYPE_COLUMN_HAS_OBJECT);
+
+ if ($has_task_phids) {
+ $error_view = id(new AphrontErrorView())
+ ->setTitle(pht('Column has Tasks!'));
+ if ($column->isDeleted()) {
+ $error_view->setErrors(array(pht(
+ 'A column can not be activated if it has tasks '.
+ 'in it. Please remove the tasks and try again.')));
} else {
- $column->setStatus(PhabricatorProjectColumn::STATUS_DELETED);
- $column->save();
+ $error_view->setErrors(array(pht(
+ 'A column can not be deleted if it has tasks '.
+ 'in it. Please remove the tasks and try again.')));
+ }
+ }
+
+ $view_uri = $this->getApplicationURI(
+ '/board/'.$this->projectID.'/column/'.$this->id.'/');
- return id(new AphrontRedirectResponse())->setURI($view_uri);
+ if ($request->isFormPost() && !$error_view) {
+ if ($column->isDeleted()) {
+ $new_status = PhabricatorProjectColumn::STATUS_ACTIVE;
+ } else {
+ $new_status = PhabricatorProjectColumn::STATUS_DELETED;
}
+
+ $type_status = PhabricatorProjectColumnTransaction::TYPE_STATUS;
+ $xactions = array(id(new PhabricatorProjectColumnTransaction())
+ ->setTransactionType($type_status)
+ ->setNewValue($new_status));
+
+ $editor = id(new PhabricatorProjectColumnTransactionEditor())
+ ->setActor($viewer)
+ ->setContinueOnNoEffect(true)
+ ->setContentSourceFromRequest($request)
+ ->applyTransactions($column, $xactions);
+
+ return id(new AphrontRedirectResponse())->setURI($view_uri);
}
- $form = id(new AphrontFormView())
- ->setUser($viewer)
- ->appendChild($error_view)
- ->appendChild(id(new AphrontFormSelectControl())
- ->setName('columnPHID')
- ->setValue(head_key($options))
- ->setOptions($options)
- ->setLabel(pht('Column')));
-
- $title = pht('Delete Column');
+ if ($column->isDeleted()) {
+ $title = pht('Activate Column');
+ } else {
+ $title = pht('Delete Column');
+ }
$submit = $title;
+ if ($error_view) {
+ $body = $error_view;
+ } else if ($column->isDeleted()) {
+ $body = pht('Are you sure you want to activate this column?');
+ } else {
+ $body = pht('Are you sure you want to delete this column?');
+ }
+
+ $dialog = id(new AphrontDialogView())
+ ->setUser($viewer)
+ ->setWidth(AphrontDialogView::WIDTH_FORM)
+ ->setTitle($title)
+ ->appendChild($body)
+ ->setDisableWorkflowOnCancel(true)
+ ->addSubmitButton($title)
+ ->addCancelButton($view_uri);
+
+ return id(new AphrontDialogResponse())
+ ->setDialog($dialog);
- $form->appendChild(
- id(new AphrontFormSubmitControl())
- ->setValue($submit)
- ->addCancelButton($view_uri));
-
- $crumbs = $this->buildApplicationCrumbs();
- $crumbs->addTextCrumb(
- $project->getName(),
- $this->getApplicationURI('view/'.$project->getID().'/'));
- $crumbs->addTextCrumb(
- pht('Board'),
- $this->getApplicationURI('board/'.$project->getID().'/'));
- $crumbs->addTextCrumb($title);
-
- $form_box = id(new PHUIObjectBoxView())
- ->setHeaderText($title)
- ->setForm($form);
-
- return $this->buildApplicationPage(
- array(
- $crumbs,
- $form_box,
- ),
- array(
- 'title' => $title,
- 'device' => true,
- ));
}
}
diff --git a/src/applications/project/controller/PhabricatorProjectBoardEditController.php b/src/applications/project/controller/PhabricatorProjectBoardEditController.php
--- a/src/applications/project/controller/PhabricatorProjectBoardEditController.php
+++ b/src/applications/project/controller/PhabricatorProjectBoardEditController.php
@@ -1,7 +1,7 @@
<?php
final class PhabricatorProjectBoardEditController
- extends PhabricatorProjectController {
+ extends PhabricatorProjectBoardController {
private $id;
private $projectID;
@@ -28,6 +28,7 @@
if (!$project) {
return new Aphront404Response();
}
+ $this->setProject($project);
$is_new = ($this->id ? false : true);
@@ -48,21 +49,18 @@
$column = PhabricatorProjectColumn::initializeNewColumn($viewer);
}
- $errors = array();
- $e_name = true;
- $error_view = null;
- $view_uri = $this->getApplicationURI('/board/'.$this->projectID.'/');
+ $e_name = null;
+ $validation_exception = null;
+ $base_uri = '/board/'.$this->projectID.'/';
+ if ($is_new) {
+ // we want to go back to the board
+ $view_uri = $this->getApplicationURI($base_uri);
+ } else {
+ $view_uri = $this->getApplicationURI($base_uri.'column/'.$this->id.'/');
+ }
if ($request->isFormPost()) {
$new_name = $request->getStr('name');
- $column->setName($new_name);
-
- if (!strlen($column->getName())) {
- $errors[] = pht('Column name is required.');
- $e_name = pht('Required');
- } else {
- $e_name = null;
- }
if ($is_new) {
$column->setProjectPHID($project->getPHID());
@@ -81,9 +79,21 @@
$column->setSequence($new_sequence);
}
- if (!$errors) {
- $column->save();
+ $type_name = PhabricatorProjectColumnTransaction::TYPE_NAME;
+ $xactions = array(id(new PhabricatorProjectColumnTransaction())
+ ->setTransactionType($type_name)
+ ->setNewValue($new_name));
+
+ try {
+ $editor = id(new PhabricatorProjectColumnTransactionEditor())
+ ->setActor($viewer)
+ ->setContinueOnNoEffect(true)
+ ->setContentSourceFromRequest($request)
+ ->applyTransactions($column, $xactions);
return id(new AphrontRedirectResponse())->setURI($view_uri);
+ } catch (PhabricatorApplicationTransactionValidationException $ex) {
+ $e_name = $ex->getShortMessage($type_name);
+ $validation_exception = $ex;
}
}
@@ -113,16 +123,13 @@
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(
- $project->getName(),
- $this->getApplicationURI('view/'.$project->getID().'/'));
- $crumbs->addTextCrumb(
pht('Board'),
$this->getApplicationURI('board/'.$project->getID().'/'));
$crumbs->addTextCrumb($title);
$form_box = id(new PHUIObjectBoxView())
->setHeaderText($title)
- ->setFormErrors($errors)
+ ->setValidationException($validation_exception)
->setForm($form);
return $this->buildApplicationPage(
diff --git a/src/applications/project/controller/PhabricatorProjectBoardController.php b/src/applications/project/controller/PhabricatorProjectBoardViewController.php
copy from src/applications/project/controller/PhabricatorProjectBoardController.php
copy to src/applications/project/controller/PhabricatorProjectBoardViewController.php
--- a/src/applications/project/controller/PhabricatorProjectBoardController.php
+++ b/src/applications/project/controller/PhabricatorProjectBoardViewController.php
@@ -1,7 +1,7 @@
<?php
-final class PhabricatorProjectBoardController
- extends PhabricatorProjectController {
+final class PhabricatorProjectBoardViewController
+ extends PhabricatorProjectBoardController {
private $id;
private $handles;
@@ -26,6 +26,7 @@
if (!$project) {
return new Aphront404Response();
}
+ $this->setProject($project);
$columns = id(new PhabricatorProjectColumnQuery())
->setViewer($viewer)
@@ -107,7 +108,7 @@
->setHeader($column->getDisplayName())
->setHeaderColor($column->getHeaderColor());
if (!$column->isDefaultColumn()) {
- $panel->setEditURI('edit/'.$column->getID().'/');
+ $panel->setEditURI('column/'.$column->getID().'/');
}
$panel->setHeaderAction(id(new PHUIIconView())
->setSpriteSheet(PHUIIconView::SPRITE_ACTIONS)
@@ -151,9 +152,6 @@
}
$crumbs = $this->buildApplicationCrumbs();
- $crumbs->addTextCrumb(
- $project->getName(),
- $this->getApplicationURI('view/'.$project->getID().'/'));
$crumbs->addTextCrumb(pht('Board'));
$can_edit = PhabricatorPolicyFilter::hasCapability(
@@ -169,13 +167,6 @@
->setHref($this->getApplicationURI('board/'.$this->id.'/edit/'))
->setIcon('create')
->setDisabled(!$can_edit)
- ->setWorkflow(!$can_edit))
- ->addAction(
- id(new PhabricatorActionView())
- ->setName(pht('Delete Column'))
- ->setHref($this->getApplicationURI('board/'.$this->id.'/delete/'))
- ->setIcon('delete')
- ->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$plist = id(new PHUIPropertyListView());
diff --git a/src/applications/project/controller/PhabricatorProjectColumnDetailController.php b/src/applications/project/controller/PhabricatorProjectColumnDetailController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/project/controller/PhabricatorProjectColumnDetailController.php
@@ -0,0 +1,165 @@
+<?php
+
+final class PhabricatorProjectColumnDetailController
+ extends PhabricatorProjectBoardController {
+
+ private $id;
+ private $projectID;
+
+ public function willProcessRequest(array $data) {
+ $this->projectID = $data['projectID'];
+ $this->id = idx($data, 'id');
+ }
+
+ public function processRequest() {
+ $request = $this->getRequest();
+ $viewer = $request->getUser();
+
+ $project = id(new PhabricatorProjectQuery())
+ ->setViewer($viewer)
+ ->requireCapabilities(
+ array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ ))
+ ->withIDs(array($this->projectID))
+ ->executeOne();
+
+ if (!$project) {
+ return new Aphront404Response();
+ }
+ $this->setProject($project);
+
+ $column = id(new PhabricatorProjectColumnQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($this->id))
+ ->requireCapabilities(
+ array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ ))
+ ->executeOne();
+ if (!$column) {
+ return new Aphront404Response();
+ }
+
+ $xactions = id(new PhabricatorProjectColumnTransactionQuery())
+ ->setViewer($viewer)
+ ->withObjectPHIDs(array($column->getPHID()))
+ ->execute();
+
+ $engine = id(new PhabricatorMarkupEngine())
+ ->setViewer($viewer);
+
+ $timeline = id(new PhabricatorApplicationTransactionView())
+ ->setUser($viewer)
+ ->setObjectPHID($column->getPHID())
+ ->setTransactions($xactions);
+
+ $title = pht('%s', $column->getName());
+ $crumbs = $this->buildApplicationCrumbs();
+ $crumbs->addTextCrumb(
+ pht('Board'),
+ $this->getApplicationURI('board/'.$project->getID().'/'));
+ $crumbs->addTextCrumb($title);
+
+ $header = $this->buildHeaderView($column);
+ $actions = $this->buildActionView($column);
+ $properties = $this->buildPropertyView($column, $actions);
+
+ $box = id(new PHUIObjectBoxView())
+ ->setHeader($header)
+ ->addPropertyList($properties);
+
+ return $this->buildApplicationPage(
+ array(
+ $crumbs,
+ $box,
+ $timeline,
+ ),
+ array(
+ 'title' => $title,
+ 'device' => true,
+ ));
+ }
+
+ private function buildHeaderView(PhabricatorProjectColumn $column) {
+ $viewer = $this->getRequest()->getUser();
+
+ $header = id(new PHUIHeaderView())
+ ->setUser($viewer)
+ ->setHeader($column->getName())
+ ->setPolicyObject($column);
+
+ if ($column->isDeleted()) {
+ $header->setStatus('reject', 'red', pht('Deleted'));
+ }
+
+ return $header;
+ }
+
+ private function buildActionView(PhabricatorProjectColumn $column) {
+ $viewer = $this->getRequest()->getUser();
+
+ $id = $column->getID();
+ $project_id = $this->getProject()->getID();
+ $base_uri = '/board/'.$project_id.'/';
+
+ $actions = id(new PhabricatorActionListView())
+ ->setObjectURI($this->getApplicationURI($base_uri.'column/'.$id.'/'))
+ ->setUser($viewer);
+
+ $can_edit = PhabricatorPolicyFilter::hasCapability(
+ $viewer,
+ $column,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
+ $actions->addAction(
+ id(new PhabricatorActionView())
+ ->setName(pht('Edit column'))
+ ->setIcon('edit')
+ ->setHref($this->getApplicationURI($base_uri.'edit/'.$id.'/'))
+ ->setDisabled(!$can_edit)
+ ->setWorkflow(!$can_edit));
+
+ if (!$column->isDeleted()) {
+ $actions->addAction(
+ id(new PhabricatorActionView())
+ ->setName(pht('Delete column'))
+ ->setIcon('delete')
+ ->setHref($this->getApplicationURI($base_uri.'delete/'.$id.'/'))
+ ->setDisabled(!$can_edit)
+ ->setWorkflow(true));
+ } else {
+ $actions->addAction(
+ id(new PhabricatorActionView())
+ ->setName(pht('Activate column'))
+ ->setIcon('enable')
+ ->setHref($this->getApplicationURI($base_uri.'delete/'.$id.'/'))
+ ->setDisabled(!$can_edit)
+ ->setWorkflow(true));
+ }
+
+ return $actions;
+ }
+
+ private function buildPropertyView(
+ PhabricatorProjectColumn $column,
+ PhabricatorActionListView $actions) {
+ $viewer = $this->getRequest()->getUser();
+
+ $properties = id(new PHUIPropertyListView())
+ ->setUser($viewer)
+ ->setObject($column)
+ ->setActionList($actions);
+
+ $descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions(
+ $viewer,
+ $column);
+
+ $properties->addProperty(
+ pht('Editable By'),
+ $descriptions[PhabricatorPolicyCapability::CAN_EDIT]);
+
+ return $properties;
+ }
+
+}
diff --git a/src/applications/project/editor/PhabricatorProjectColumnTransactionEditor.php b/src/applications/project/editor/PhabricatorProjectColumnTransactionEditor.php
new file mode 100644
--- /dev/null
+++ b/src/applications/project/editor/PhabricatorProjectColumnTransactionEditor.php
@@ -0,0 +1,118 @@
+<?php
+
+final class PhabricatorProjectColumnTransactionEditor
+ extends PhabricatorApplicationTransactionEditor {
+
+ public function getTransactionTypes() {
+ $types = parent::getTransactionTypes();
+
+ $types[] = PhabricatorProjectColumnTransaction::TYPE_NAME;
+ $types[] = PhabricatorProjectColumnTransaction::TYPE_STATUS;
+
+ return $types;
+ }
+
+ protected function getCustomTransactionOldValue(
+ PhabricatorLiskDAO $object,
+ PhabricatorApplicationTransaction $xaction) {
+
+ switch ($xaction->getTransactionType()) {
+ case PhabricatorProjectColumnTransaction::TYPE_NAME:
+ return $object->getName();
+ case PhabricatorProjectColumnTransaction::TYPE_STATUS:
+ return $object->getStatus();
+ }
+
+ return parent::getCustomTransactionOldValue($object, $xaction);
+ }
+
+ protected function getCustomTransactionNewValue(
+ PhabricatorLiskDAO $object,
+ PhabricatorApplicationTransaction $xaction) {
+
+ switch ($xaction->getTransactionType()) {
+ case PhabricatorProjectColumnTransaction::TYPE_NAME:
+ case PhabricatorProjectColumnTransaction::TYPE_STATUS:
+ return $xaction->getNewValue();
+ }
+
+ return parent::getCustomTransactionNewValue($object, $xaction);
+ }
+
+ protected function applyCustomInternalTransaction(
+ PhabricatorLiskDAO $object,
+ PhabricatorApplicationTransaction $xaction) {
+
+ switch ($xaction->getTransactionType()) {
+ case PhabricatorProjectColumnTransaction::TYPE_NAME:
+ $object->setName($xaction->getNewValue());
+ return;
+ case PhabricatorProjectColumnTransaction::TYPE_STATUS:
+ $object->setStatus($xaction->getNewValue());
+ return;
+ }
+
+ return parent::applyCustomInternalTransaction($object, $xaction);
+ }
+
+ protected function applyCustomExternalTransaction(
+ PhabricatorLiskDAO $object,
+ PhabricatorApplicationTransaction $xaction) {
+
+ switch ($xaction->getTransactionType()) {
+ case PhabricatorProjectColumnTransaction::TYPE_NAME:
+ case PhabricatorProjectColumnTransaction::TYPE_STATUS:
+ return;
+ }
+
+ return parent::applyCustomExternalTransaction($object, $xaction);
+ }
+
+ protected function validateTransaction(
+ PhabricatorLiskDAO $object,
+ $type,
+ array $xactions) {
+
+ $errors = parent::validateTransaction($object, $type, $xactions);
+
+ switch ($type) {
+ case PhabricatorProjectColumnTransaction::TYPE_NAME:
+ $missing = $this->validateIsEmptyTextField(
+ $object->getName(),
+ $xactions);
+
+ if ($missing) {
+ $error = new PhabricatorApplicationTransactionValidationError(
+ $type,
+ pht('Required'),
+ pht('Column name is required.'),
+ nonempty(last($xactions), null));
+
+ $error->setIsMissingFieldError(true);
+ $errors[] = $error;
+ }
+ break;
+ }
+
+ return $errors;
+ }
+
+
+ protected function requireCapabilities(
+ PhabricatorLiskDAO $object,
+ PhabricatorApplicationTransaction $xaction) {
+
+ switch ($xaction->getTransactionType()) {
+ case PhabricatorProjectColumnTransaction::TYPE_NAME:
+ case PhabricatorProjectColumnTransaction::TYPE_STATUS:
+ PhabricatorPolicyFilter::requireCapability(
+ $this->requireActor(),
+ $object,
+ PhabricatorPolicyCapability::CAN_EDIT);
+ return;
+ }
+
+ return parent::requireCapabilities($object, $xaction);
+ }
+
+}
diff --git a/src/applications/project/query/PhabricatorProjectColumnTransactionQuery.php b/src/applications/project/query/PhabricatorProjectColumnTransactionQuery.php
new file mode 100644
--- /dev/null
+++ b/src/applications/project/query/PhabricatorProjectColumnTransactionQuery.php
@@ -0,0 +1,10 @@
+<?php
+
+final class PhabricatorProjectColumnTransactionQuery
+ extends PhabricatorApplicationTransactionQuery {
+
+ public function getTemplateApplicationTransaction() {
+ return new PhabricatorProjectColumnTransaction();
+ }
+
+}
diff --git a/src/applications/project/storage/PhabricatorProjectColumn.php b/src/applications/project/storage/PhabricatorProjectColumn.php
--- a/src/applications/project/storage/PhabricatorProjectColumn.php
+++ b/src/applications/project/storage/PhabricatorProjectColumn.php
@@ -44,6 +44,10 @@
return ($this->getSequence() == 0);
}
+ public function isDeleted() {
+ return ($this->getStatus() == self::STATUS_DELETED);
+ }
+
public function getDisplayName() {
if ($this->isDefaultColumn()) {
return pht('Backlog');
diff --git a/src/applications/project/storage/PhabricatorProjectColumnTransaction.php b/src/applications/project/storage/PhabricatorProjectColumnTransaction.php
new file mode 100644
--- /dev/null
+++ b/src/applications/project/storage/PhabricatorProjectColumnTransaction.php
@@ -0,0 +1,52 @@
+<?php
+
+final class PhabricatorProjectColumnTransaction
+ extends PhabricatorApplicationTransaction {
+
+ const TYPE_NAME = 'project:col:name';
+ const TYPE_STATUS = 'project:col:status';
+
+ public function getApplicationName() {
+ return 'project';
+ }
+
+ public function getApplicationTransactionType() {
+ return PhabricatorProjectPHIDTypeColumn::TYPECONST;
+ }
+
+ public function getTitle() {
+ $old = $this->getOldValue();
+ $new = $this->getNewValue();
+ $author_handle = $this->renderHandleLink($this->getAuthorPHID());
+
+ switch ($this->getTransactionType()) {
+ case PhabricatorProjectColumnTransaction::TYPE_NAME:
+ if (!strlen($old)) {
+ return pht(
+ '%s created this column.',
+ $author_handle);
+ } else {
+ return pht(
+ '%s renamed this column from "%s" to "%s".',
+ $author_handle,
+ $old,
+ $new);
+ }
+ case PhabricatorProjectColumnTransaction::TYPE_STATUS:
+ switch ($new) {
+ case PhabricatorProjectColumn::STATUS_ACTIVE:
+ return pht(
+ '%s activated this column.',
+ $author_handle);
+ case PhabricatorProjectColumn::STATUS_DELETED:
+ return pht(
+ '%s deleted this column.',
+ $author_handle);
+ }
+ break;
+ }
+
+ return parent::getTitle();
+ }
+
+}

File Metadata

Mime Type
text/plain
Expires
Tue, Mar 18, 5:23 AM (6 d, 15 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7224075
Default Alt Text
D8620.id20454.diff (35 KB)

Event Timeline