Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15399594
D8620.id20454.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
35 KB
Referenced Files
None
Subscribers
None
D8620.id20454.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D8620: Workboards - add column detail page
Attached
Detach File
Event Timeline
Log In to Comment