mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-29 10:12:41 +01:00
Workboards - add column detail page
Summary: followup to D8544. This ends up creating an editor + transactions to get the job done. Test Plan: made a column - saw a nice created transaction. edited the name - saw a nice name edit. deleted the column - saw a deleted transaction, updated "deleted" ui, and hte action change to activate. "Activated" the column and saw a transaction and updated UI. Tried to delete a column with tasks in it and got an error. Reviewers: epriestley Reviewed By: epriestley Subscribers: epriestley, Korvin Differential Revision: https://secure.phabricator.com/D8620
This commit is contained in:
parent
543a313387
commit
655ac9927f
12 changed files with 698 additions and 295 deletions
21
resources/sql/autopatches/20140326.project.1.colxaction.sql
Normal file
21
resources/sql/autopatches/20140326.project.1.colxaction.sql
Normal file
|
@ -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;
|
|
@ -1862,8 +1862,13 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorProjectBoardController' => 'applications/project/controller/PhabricatorProjectBoardController.php',
|
'PhabricatorProjectBoardController' => 'applications/project/controller/PhabricatorProjectBoardController.php',
|
||||||
'PhabricatorProjectBoardDeleteController' => 'applications/project/controller/PhabricatorProjectBoardDeleteController.php',
|
'PhabricatorProjectBoardDeleteController' => 'applications/project/controller/PhabricatorProjectBoardDeleteController.php',
|
||||||
'PhabricatorProjectBoardEditController' => 'applications/project/controller/PhabricatorProjectBoardEditController.php',
|
'PhabricatorProjectBoardEditController' => 'applications/project/controller/PhabricatorProjectBoardEditController.php',
|
||||||
|
'PhabricatorProjectBoardViewController' => 'applications/project/controller/PhabricatorProjectBoardViewController.php',
|
||||||
'PhabricatorProjectColumn' => 'applications/project/storage/PhabricatorProjectColumn.php',
|
'PhabricatorProjectColumn' => 'applications/project/storage/PhabricatorProjectColumn.php',
|
||||||
|
'PhabricatorProjectColumnDetailController' => 'applications/project/controller/PhabricatorProjectColumnDetailController.php',
|
||||||
'PhabricatorProjectColumnQuery' => 'applications/project/query/PhabricatorProjectColumnQuery.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',
|
'PhabricatorProjectConfigOptions' => 'applications/project/config/PhabricatorProjectConfigOptions.php',
|
||||||
'PhabricatorProjectConfiguredCustomField' => 'applications/project/customfield/PhabricatorProjectConfiguredCustomField.php',
|
'PhabricatorProjectConfiguredCustomField' => 'applications/project/customfield/PhabricatorProjectConfiguredCustomField.php',
|
||||||
'PhabricatorProjectConstants' => 'applications/project/constants/PhabricatorProjectConstants.php',
|
'PhabricatorProjectConstants' => 'applications/project/constants/PhabricatorProjectConstants.php',
|
||||||
|
@ -4668,14 +4673,19 @@ phutil_register_library_map(array(
|
||||||
),
|
),
|
||||||
'PhabricatorProjectArchiveController' => 'PhabricatorProjectController',
|
'PhabricatorProjectArchiveController' => 'PhabricatorProjectController',
|
||||||
'PhabricatorProjectBoardController' => 'PhabricatorProjectController',
|
'PhabricatorProjectBoardController' => 'PhabricatorProjectController',
|
||||||
'PhabricatorProjectBoardDeleteController' => 'PhabricatorProjectController',
|
'PhabricatorProjectBoardDeleteController' => 'PhabricatorProjectBoardController',
|
||||||
'PhabricatorProjectBoardEditController' => 'PhabricatorProjectController',
|
'PhabricatorProjectBoardEditController' => 'PhabricatorProjectBoardController',
|
||||||
|
'PhabricatorProjectBoardViewController' => 'PhabricatorProjectBoardController',
|
||||||
'PhabricatorProjectColumn' =>
|
'PhabricatorProjectColumn' =>
|
||||||
array(
|
array(
|
||||||
0 => 'PhabricatorProjectDAO',
|
0 => 'PhabricatorProjectDAO',
|
||||||
1 => 'PhabricatorPolicyInterface',
|
1 => 'PhabricatorPolicyInterface',
|
||||||
),
|
),
|
||||||
|
'PhabricatorProjectColumnDetailController' => 'PhabricatorProjectBoardController',
|
||||||
'PhabricatorProjectColumnQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
'PhabricatorProjectColumnQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||||
|
'PhabricatorProjectColumnTransaction' => 'PhabricatorApplicationTransaction',
|
||||||
|
'PhabricatorProjectColumnTransactionEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||||
|
'PhabricatorProjectColumnTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||||
'PhabricatorProjectConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
'PhabricatorProjectConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||||
'PhabricatorProjectConfiguredCustomField' =>
|
'PhabricatorProjectConfiguredCustomField' =>
|
||||||
array(
|
array(
|
||||||
|
|
|
@ -51,12 +51,14 @@ final class PhabricatorApplicationProject extends PhabricatorApplication {
|
||||||
'picture/(?P<id>[1-9]\d*)/' =>
|
'picture/(?P<id>[1-9]\d*)/' =>
|
||||||
'PhabricatorProjectEditPictureController',
|
'PhabricatorProjectEditPictureController',
|
||||||
'create/' => 'PhabricatorProjectCreateController',
|
'create/' => 'PhabricatorProjectCreateController',
|
||||||
'board/(?P<id>[1-9]\d*)/' => 'PhabricatorProjectBoardController',
|
'board/(?P<id>[1-9]\d*)/' => 'PhabricatorProjectBoardViewController',
|
||||||
'move/(?P<id>[1-9]\d*)/' => 'PhabricatorProjectMoveController',
|
'move/(?P<id>[1-9]\d*)/' => 'PhabricatorProjectMoveController',
|
||||||
'board/(?P<projectID>[1-9]\d*)/edit/(?:(?P<id>\d+)/)?'
|
'board/(?P<projectID>[1-9]\d*)/edit/(?:(?P<id>\d+)/)?'
|
||||||
=> 'PhabricatorProjectBoardEditController',
|
=> 'PhabricatorProjectBoardEditController',
|
||||||
'board/(?P<projectID>[1-9]\d*)/delete/(?:(?P<id>\d+)/)?'
|
'board/(?P<projectID>[1-9]\d*)/delete/(?:(?P<id>\d+)/)?'
|
||||||
=> 'PhabricatorProjectBoardDeleteController',
|
=> 'PhabricatorProjectBoardDeleteController',
|
||||||
|
'board/(?P<projectID>[1-9]\d*)/column/(?:(?P<id>\d+)/)?'
|
||||||
|
=> 'PhabricatorProjectColumnDetailController',
|
||||||
'update/(?P<id>[1-9]\d*)/(?P<action>[^/]+)/'
|
'update/(?P<id>[1-9]\d*)/(?P<action>[^/]+)/'
|
||||||
=> 'PhabricatorProjectUpdateController',
|
=> 'PhabricatorProjectUpdateController',
|
||||||
'history/(?P<id>[1-9]\d*)/' => 'PhabricatorProjectHistoryController',
|
'history/(?P<id>[1-9]\d*)/' => 'PhabricatorProjectHistoryController',
|
||||||
|
|
|
@ -1,219 +1,24 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
final class PhabricatorProjectBoardController
|
abstract class PhabricatorProjectBoardController
|
||||||
extends PhabricatorProjectController {
|
extends PhabricatorProjectController {
|
||||||
|
|
||||||
private $id;
|
private $project;
|
||||||
private $handles;
|
|
||||||
|
|
||||||
public function shouldAllowPublic() {
|
protected function setProject(PhabricatorProject $project) {
|
||||||
return true;
|
$this->project = $project;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
protected function getProject() {
|
||||||
|
return $this->project;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function willProcessRequest(array $data) {
|
protected function buildApplicationCrumbs() {
|
||||||
$this->id = $data['id'];
|
$project = $this->getProject();
|
||||||
}
|
$crumbs = parent::buildApplicationCrumbs();
|
||||||
|
|
||||||
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();
|
|
||||||
$crumbs->addTextCrumb(
|
$crumbs->addTextCrumb(
|
||||||
$project->getName(),
|
$project->getName(),
|
||||||
$this->getApplicationURI('view/'.$project->getID().'/'));
|
$this->getApplicationURI('view/'.$project->getID().'/'));
|
||||||
$crumbs->addTextCrumb(pht('Board'));
|
return $crumbs;
|
||||||
|
|
||||||
$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,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
final class PhabricatorProjectBoardDeleteController
|
final class PhabricatorProjectBoardDeleteController
|
||||||
extends PhabricatorProjectController {
|
extends PhabricatorProjectBoardController {
|
||||||
|
|
||||||
private $id;
|
private $id;
|
||||||
private $projectID;
|
private $projectID;
|
||||||
|
@ -14,7 +14,6 @@ final class PhabricatorProjectBoardDeleteController
|
||||||
public function processRequest() {
|
public function processRequest() {
|
||||||
$request = $this->getRequest();
|
$request = $this->getRequest();
|
||||||
$viewer = $request->getUser();
|
$viewer = $request->getUser();
|
||||||
|
|
||||||
$project = id(new PhabricatorProjectQuery())
|
$project = id(new PhabricatorProjectQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->requireCapabilities(
|
->requireCapabilities(
|
||||||
|
@ -28,89 +27,89 @@ final class PhabricatorProjectBoardDeleteController
|
||||||
if (!$project) {
|
if (!$project) {
|
||||||
return new Aphront404Response();
|
return new Aphront404Response();
|
||||||
}
|
}
|
||||||
|
$this->setProject($project);
|
||||||
|
|
||||||
$columns = id(new PhabricatorProjectColumnQuery())
|
$column = id(new PhabricatorProjectColumnQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withProjectPHIDs(array($project->getPHID()))
|
->withIDs(array($this->id))
|
||||||
->withStatuses(array(PhabricatorProjectColumn::STATUS_ACTIVE))
|
|
||||||
->requireCapabilities(
|
->requireCapabilities(
|
||||||
array(
|
array(
|
||||||
PhabricatorPolicyCapability::CAN_VIEW,
|
PhabricatorPolicyCapability::CAN_VIEW,
|
||||||
PhabricatorPolicyCapability::CAN_EDIT))
|
PhabricatorPolicyCapability::CAN_EDIT))
|
||||||
->execute();
|
->executeOne();
|
||||||
|
if (!$column) {
|
||||||
if (!$columns) {
|
|
||||||
return new Aphront404Response();
|
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;
|
$error_view = null;
|
||||||
if ($request->isFormPost()) {
|
$column_phid = $column->getPHID();
|
||||||
$columns = mpull($columns, null, 'getPHID');
|
$has_task_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
|
||||||
$column_phid = $request->getStr('columnPHID');
|
$column_phid,
|
||||||
$column = $columns[$column_phid];
|
PhabricatorEdgeConfig::TYPE_COLUMN_HAS_OBJECT);
|
||||||
|
|
||||||
$has_task_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
|
if ($has_task_phids) {
|
||||||
$column_phid,
|
$error_view = id(new AphrontErrorView())
|
||||||
PhabricatorEdgeConfig::TYPE_COLUMN_HAS_OBJECT);
|
->setTitle(pht('Column has Tasks!'));
|
||||||
|
if ($column->isDeleted()) {
|
||||||
if ($has_task_phids) {
|
$error_view->setErrors(array(pht(
|
||||||
$error_view = id(new AphrontErrorView())
|
'A column can not be activated if it has tasks '.
|
||||||
->setTitle(pht('Column has Tasks!'))
|
'in it. Please remove the tasks and try again.')));
|
||||||
->setErrors(array(pht('A column can not be deleted if it has tasks '.
|
|
||||||
'in it. Please remove the tasks and try '.
|
|
||||||
'again.')));
|
|
||||||
} else {
|
} else {
|
||||||
$column->setStatus(PhabricatorProjectColumn::STATUS_DELETED);
|
$error_view->setErrors(array(pht(
|
||||||
$column->save();
|
'A column can not be deleted if it has tasks '.
|
||||||
|
'in it. Please remove the tasks and try again.')));
|
||||||
return id(new AphrontRedirectResponse())->setURI($view_uri);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$form = id(new AphrontFormView())
|
$view_uri = $this->getApplicationURI(
|
||||||
->setUser($viewer)
|
'/board/'.$this->projectID.'/column/'.$this->id.'/');
|
||||||
->appendChild($error_view)
|
|
||||||
->appendChild(id(new AphrontFormSelectControl())
|
|
||||||
->setName('columnPHID')
|
|
||||||
->setValue(head_key($options))
|
|
||||||
->setOptions($options)
|
|
||||||
->setLabel(pht('Column')));
|
|
||||||
|
|
||||||
$title = pht('Delete Column');
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($column->isDeleted()) {
|
||||||
|
$title = pht('Activate Column');
|
||||||
|
} else {
|
||||||
|
$title = pht('Delete Column');
|
||||||
|
}
|
||||||
$submit = $title;
|
$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?');
|
||||||
|
}
|
||||||
|
|
||||||
$form->appendChild(
|
$dialog = id(new AphrontDialogView())
|
||||||
id(new AphrontFormSubmitControl())
|
->setUser($viewer)
|
||||||
->setValue($submit)
|
->setWidth(AphrontDialogView::WIDTH_FORM)
|
||||||
->addCancelButton($view_uri));
|
->setTitle($title)
|
||||||
|
->appendChild($body)
|
||||||
|
->setDisableWorkflowOnCancel(true)
|
||||||
|
->addSubmitButton($title)
|
||||||
|
->addCancelButton($view_uri);
|
||||||
|
|
||||||
$crumbs = $this->buildApplicationCrumbs();
|
return id(new AphrontDialogResponse())
|
||||||
$crumbs->addTextCrumb(
|
->setDialog($dialog);
|
||||||
$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,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
final class PhabricatorProjectBoardEditController
|
final class PhabricatorProjectBoardEditController
|
||||||
extends PhabricatorProjectController {
|
extends PhabricatorProjectBoardController {
|
||||||
|
|
||||||
private $id;
|
private $id;
|
||||||
private $projectID;
|
private $projectID;
|
||||||
|
@ -28,6 +28,7 @@ final class PhabricatorProjectBoardEditController
|
||||||
if (!$project) {
|
if (!$project) {
|
||||||
return new Aphront404Response();
|
return new Aphront404Response();
|
||||||
}
|
}
|
||||||
|
$this->setProject($project);
|
||||||
|
|
||||||
$is_new = ($this->id ? false : true);
|
$is_new = ($this->id ? false : true);
|
||||||
|
|
||||||
|
@ -48,21 +49,18 @@ final class PhabricatorProjectBoardEditController
|
||||||
$column = PhabricatorProjectColumn::initializeNewColumn($viewer);
|
$column = PhabricatorProjectColumn::initializeNewColumn($viewer);
|
||||||
}
|
}
|
||||||
|
|
||||||
$errors = array();
|
$e_name = null;
|
||||||
$e_name = true;
|
$validation_exception = null;
|
||||||
$error_view = null;
|
$base_uri = '/board/'.$this->projectID.'/';
|
||||||
$view_uri = $this->getApplicationURI('/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()) {
|
if ($request->isFormPost()) {
|
||||||
$new_name = $request->getStr('name');
|
$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) {
|
if ($is_new) {
|
||||||
$column->setProjectPHID($project->getPHID());
|
$column->setProjectPHID($project->getPHID());
|
||||||
|
@ -81,9 +79,21 @@ final class PhabricatorProjectBoardEditController
|
||||||
$column->setSequence($new_sequence);
|
$column->setSequence($new_sequence);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$errors) {
|
$type_name = PhabricatorProjectColumnTransaction::TYPE_NAME;
|
||||||
$column->save();
|
$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);
|
return id(new AphrontRedirectResponse())->setURI($view_uri);
|
||||||
|
} catch (PhabricatorApplicationTransactionValidationException $ex) {
|
||||||
|
$e_name = $ex->getShortMessage($type_name);
|
||||||
|
$validation_exception = $ex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,9 +122,6 @@ final class PhabricatorProjectBoardEditController
|
||||||
->addCancelButton($view_uri));
|
->addCancelButton($view_uri));
|
||||||
|
|
||||||
$crumbs = $this->buildApplicationCrumbs();
|
$crumbs = $this->buildApplicationCrumbs();
|
||||||
$crumbs->addTextCrumb(
|
|
||||||
$project->getName(),
|
|
||||||
$this->getApplicationURI('view/'.$project->getID().'/'));
|
|
||||||
$crumbs->addTextCrumb(
|
$crumbs->addTextCrumb(
|
||||||
pht('Board'),
|
pht('Board'),
|
||||||
$this->getApplicationURI('board/'.$project->getID().'/'));
|
$this->getApplicationURI('board/'.$project->getID().'/'));
|
||||||
|
@ -122,7 +129,7 @@ final class PhabricatorProjectBoardEditController
|
||||||
|
|
||||||
$form_box = id(new PHUIObjectBoxView())
|
$form_box = id(new PHUIObjectBoxView())
|
||||||
->setHeaderText($title)
|
->setHeaderText($title)
|
||||||
->setFormErrors($errors)
|
->setValidationException($validation_exception)
|
||||||
->setForm($form);
|
->setForm($form);
|
||||||
|
|
||||||
return $this->buildApplicationPage(
|
return $this->buildApplicationPage(
|
||||||
|
|
|
@ -0,0 +1,210 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorProjectBoardViewController
|
||||||
|
extends PhabricatorProjectBoardController {
|
||||||
|
|
||||||
|
private $id;
|
||||||
|
private $handles;
|
||||||
|
|
||||||
|
public function shouldAllowPublic() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function willProcessRequest(array $data) {
|
||||||
|
$this->id = $data['id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
$this->setProject($project);
|
||||||
|
|
||||||
|
$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('column/'.$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();
|
||||||
|
$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));
|
||||||
|
|
||||||
|
$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,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorProjectColumnTransactionQuery
|
||||||
|
extends PhabricatorApplicationTransactionQuery {
|
||||||
|
|
||||||
|
public function getTemplateApplicationTransaction() {
|
||||||
|
return new PhabricatorProjectColumnTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -44,6 +44,10 @@ final class PhabricatorProjectColumn
|
||||||
return ($this->getSequence() == 0);
|
return ($this->getSequence() == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isDeleted() {
|
||||||
|
return ($this->getStatus() == self::STATUS_DELETED);
|
||||||
|
}
|
||||||
|
|
||||||
public function getDisplayName() {
|
public function getDisplayName() {
|
||||||
if ($this->isDefaultColumn()) {
|
if ($this->isDefaultColumn()) {
|
||||||
return pht('Backlog');
|
return pht('Backlog');
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue