diff --git a/src/applications/project/controller/PhabricatorProjectBoardController.php b/src/applications/project/controller/PhabricatorProjectBoardController.php index 5a5b0422ee..d3bbd4af0f 100644 --- a/src/applications/project/controller/PhabricatorProjectBoardController.php +++ b/src/applications/project/controller/PhabricatorProjectBoardController.php @@ -1,149 +1,160 @@ id = $data['id']; } public function processRequest() { $request = $this->getRequest(); $viewer = $request->getUser(); $project = id(new PhabricatorProjectQuery()) ->setViewer($viewer) ->withIDs(array($this->id)) ->executeOne(); if (!$project) { return new Aphront404Response(); } $columns = id(new PhabricatorProjectColumnQuery()) ->setViewer($viewer) ->withProjectPHIDs(array($project->getPHID())) ->execute(); - msort($columns, 'getSequence'); + $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())) ->withStatus(ManiphestTaskQuery::STATUS_OPEN) ->setOrderBy(ManiphestTaskQuery::ORDER_PRIORITY) ->execute(); $tasks = mpull($tasks, null, 'getPHID'); - // TODO: This is so made up. $task_map = array(); + $default_phid = $columns[0]->getPHID(); + foreach ($tasks as $task) { - if ($columns) { - $random_column = $columns[array_rand($columns)]->getPHID(); - } else { - $random_column = 0; - } - $task_map[$random_column][] = $task->getPHID(); + $task_map[$default_phid][] = $task->getPHID(); } $board = id(new PHUIWorkboardView()) ->setUser($viewer) ->setFluidishLayout(true); foreach ($columns as $column) { $panel = id(new PHUIWorkpanelView()) - ->setHeader($column->getName()) + ->setHeader($column->getDisplayName()) + ->setHeaderColor($column->getHeaderColor()) ->setEditURI('edit/'.$column->getID().'/'); $cards = id(new PHUIObjectItemListView()) ->setUser($viewer) ->setCards(true) ->setFlush(true); $task_phids = idx($task_map, $column->getPHID(), array()); foreach (array_select_keys($tasks, $task_phids) as $task) { $cards->addItem($this->renderTaskCard($task)); } $panel->setCards($cards); $board->addPanel($panel); } $crumbs = $this->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/Milestone/Sprint')) ->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('Ignore'), pht('This Property')); $plist->setActionList($actions); $header = id(new PHUIObjectBoxView()) ->setHeaderText($project->getName()) ->addPropertyList($plist); $board_box = id(new PHUIBoxView()) ->appendChild($board) ->addMargin(PHUI::MARGIN_LARGE); return $this->buildApplicationPage( array( $crumbs, $header, $board_box, ), array( - 'title' => pht('Board'), + 'title' => pht('%s Board', $project->getName()), 'device' => true, )); } private function renderTaskCard(ManiphestTask $task) { $request = $this->getRequest(); $viewer = $request->getUser(); $color_map = ManiphestTaskPriority::getColorMap(); $bar_color = idx($color_map, $task->getPriority(), 'grey'); // TODO: Batch this earlier on. $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, $task, PhabricatorPolicyCapability::CAN_EDIT); return id(new PHUIObjectItemView()) ->setObjectName('T'.$task->getID()) ->setHeader($task->getTitle()) ->setGrippable($can_edit) ->setHref('/T'.$task->getID()) ->addAction( id(new PHUIListItemView()) ->setName(pht('Edit')) ->setIcon('edit') ->setHref('/maniphest/task/edit/'.$task->getID().'/') ->setWorkflow(true)) ->setBarColor($bar_color); } } diff --git a/src/applications/project/controller/PhabricatorProjectBoardEditController.php b/src/applications/project/controller/PhabricatorProjectBoardEditController.php index 266609e259..83f3e4abce 100644 --- a/src/applications/project/controller/PhabricatorProjectBoardEditController.php +++ b/src/applications/project/controller/PhabricatorProjectBoardEditController.php @@ -1,138 +1,138 @@ 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, PhabricatorPolicyCapability::CAN_EDIT, )) ->withIDs(array($this->projectID)) ->executeOne(); if (!$project) { return new Aphront404Response(); } $is_new = ($this->id ? false : true); if (!$is_new) { $column = id(new PhabricatorProjectColumnQuery()) ->setViewer($viewer) ->withIDs(array($this->id)) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )) ->executeOne(); if (!$column) { return new Aphront404Response(); } } else { - $column = new PhabricatorProjectColumn(); + $column = PhabricatorProjectColumn::initializeNewColumn($viewer); } $errors = array(); $e_name = true; $error_view = null; $view_uri = $this->getApplicationURI('/board/'.$this->projectID.'/'); 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()); $column->attachProject($project); $columns = id(new PhabricatorProjectColumnQuery()) ->setViewer($viewer) ->withProjectPHIDs(array($project->getPHID())) ->execute(); $new_sequence = 1; if ($columns) { $values = mpull($columns, 'getSequence'); $new_sequence = max($values) + 1; } $column->setSequence($new_sequence); } if (!$errors) { $column->save(); return id(new AphrontRedirectResponse())->setURI($view_uri); } } $form = new AphrontFormView(); $form->setUser($request->getUser()) ->appendChild( id(new AphrontFormTextControl()) ->setValue($column->getName()) ->setLabel(pht('Name')) ->setName('name') ->setError($e_name) ->setCaption( pht('This will be displayed as the header of the column.'))); if ($is_new) { $title = pht('Create Column'); $submit = pht('Create Column'); } else { $title = pht('Edit %s', $column->getName()); $submit = pht('Save Column'); } $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) ->setFormErrors($errors) ->setForm($form); return $this->buildApplicationPage( array( $crumbs, $form_box, ), array( 'title' => $title, 'device' => true, )); } } diff --git a/src/applications/project/storage/PhabricatorProjectColumn.php b/src/applications/project/storage/PhabricatorProjectColumn.php index 8d9c32af0d..79474aef0c 100644 --- a/src/applications/project/storage/PhabricatorProjectColumn.php +++ b/src/applications/project/storage/PhabricatorProjectColumn.php @@ -1,58 +1,81 @@ setName(''); + } + public function getConfiguration() { return array( self::CONFIG_AUX_PHID => true, ) + parent::getConfiguration(); } public function generatePHID() { return PhabricatorPHID::generateNewPHID( PhabricatorProjectPHIDTypeColumn::TYPECONST); } public function attachProject(PhabricatorProject $project) { $this->project = $project; return $this; } public function getProject() { return $this->assertAttached($this->project); } + public function isDefaultColumn() { + return ($this->getSequence() == 0); + } + + public function getDisplayName() { + if ($this->isDefaultColumn()) { + return pht('Backlog'); + } + return $this->getName(); + } + + public function getHeaderColor() { + if ($this->isDefaultColumn()) { + return PhabricatorActionHeaderView::HEADER_DARK_GREY; + } + return PhabricatorActionHeaderView::HEADER_GREY; + } + /* -( PhabricatorPolicyInterface )----------------------------------------- */ public function getCapabilities() { return array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, ); } public function getPolicy($capability) { return $this->getProject()->getPolicy($capability); } public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { return $this->getProject()->hasAutomaticCapability( $capability, $viewer); } public function describeAutomaticCapability($capability) { return pht('Users must be able to see a project to see its board.'); } } diff --git a/src/view/phui/PHUIWorkpanelView.php b/src/view/phui/PHUIWorkpanelView.php index d67a8eb1e5..eef1d48645 100644 --- a/src/view/phui/PHUIWorkpanelView.php +++ b/src/view/phui/PHUIWorkpanelView.php @@ -1,79 +1,85 @@ cards[] = $cards; return $this; } public function setHeader($header) { $this->header = $header; return $this; } public function setEditURI($edit_uri) { $this->editURI = $edit_uri; return $this; } public function setFooterAction(PHUIListItemView $footer_action) { $this->footerAction = $footer_action; return $this; } + public function setHeaderColor($header_color) { + $this->headerColor = $header_color; + return $this; + } + public function render() { require_celerity_resource('phui-workpanel-view-css'); $footer = ''; if ($this->footerAction) { $footer_tag = $this->footerAction; $footer = phutil_tag( 'ul', array( 'class' => 'phui-workpanel-footer-action mst ps' ), $footer_tag); } $header_edit = id(new PHUIIconView()) ->setSpriteSheet(PHUIIconView::SPRITE_ACTIONS) ->setSpriteIcon('settings-grey') ->setHref($this->editURI); $header = id(new PhabricatorActionHeaderView()) ->setHeaderTitle($this->header) - ->setHeaderColor(PhabricatorActionHeaderView::HEADER_GREY) + ->setHeaderColor($this->headerColor) ->addAction($header_edit); $body = phutil_tag( 'div', array( 'class' => 'phui-workpanel-body' ), $this->cards); $view = phutil_tag( 'div', array( 'class' => 'phui-workpanel-view-inner', ), array( $header, $body, $footer, )); return phutil_tag( 'div', array( 'class' => 'phui-workpanel-view' ), $view); } }