Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/project/controller/PhabricatorProjectBoardController.php b/src/applications/project/controller/PhabricatorProjectBoardController.php
index 18028a1bd0..04ab714eb0 100644
--- a/src/applications/project/controller/PhabricatorProjectBoardController.php
+++ b/src/applications/project/controller/PhabricatorProjectBoardController.php
@@ -1,13 +1,14 @@
<?php
abstract class PhabricatorProjectBoardController
extends PhabricatorProjectController {
- public function buildIconNavView(PhabricatorProject $project) {
- $id = $project->getID();
- $nav = parent::buildIconNavView($project);
- $nav->selectFilter(PhabricatorProject::PANEL_WORKBOARD);
- $nav->addClass('project-board-nav');
- return $nav;
+ protected function getProfileMenu() {
+ $menu = parent::getProfileMenu();
+
+ $menu->selectFilter(PhabricatorProject::PANEL_WORKBOARD);
+ $menu->addClass('project-board-nav');
+
+ return $menu;
}
}
diff --git a/src/applications/project/controller/PhabricatorProjectBoardViewController.php b/src/applications/project/controller/PhabricatorProjectBoardViewController.php
index 7408e67dd8..571d5b866d 100644
--- a/src/applications/project/controller/PhabricatorProjectBoardViewController.php
+++ b/src/applications/project/controller/PhabricatorProjectBoardViewController.php
@@ -1,789 +1,789 @@
<?php
final class PhabricatorProjectBoardViewController
extends PhabricatorProjectBoardController {
const BATCH_EDIT_ALL = 'all';
private $id;
private $slug;
private $handles;
private $queryKey;
private $filter;
private $sortKey;
private $showHidden;
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getUser();
$id = $request->getURIData('id');
$show_hidden = $request->getBool('hidden');
$this->showHidden = $show_hidden;
$project = id(new PhabricatorProjectQuery())
->setViewer($viewer)
->needImages(true);
$id = $request->getURIData('id');
$slug = $request->getURIData('slug');
if ($slug) {
$project->withSlugs(array($slug));
} else {
$project->withIDs(array($id));
}
$project = $project->executeOne();
if (!$project) {
return new Aphront404Response();
}
$this->setProject($project);
$this->id = $project->getID();
$sort_key = $request->getStr('order');
switch ($sort_key) {
case PhabricatorProjectColumn::ORDER_NATURAL:
case PhabricatorProjectColumn::ORDER_PRIORITY:
break;
default:
$sort_key = PhabricatorProjectColumn::DEFAULT_ORDER;
break;
}
$this->sortKey = $sort_key;
$column_query = id(new PhabricatorProjectColumnQuery())
->setViewer($viewer)
->withProjectPHIDs(array($project->getPHID()));
if (!$show_hidden) {
$column_query->withStatuses(
array(PhabricatorProjectColumn::STATUS_ACTIVE));
}
$columns = $column_query->execute();
$columns = mpull($columns, null, 'getSequence');
// TODO: Expand the checks here if we add the ability
// to hide the Backlog column
if (!$columns) {
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$project,
PhabricatorPolicyCapability::CAN_EDIT);
if (!$can_edit) {
return $this->noAccessDialog($project);
}
switch ($request->getStr('initialize-type')) {
case 'backlog-only':
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$column = PhabricatorProjectColumn::initializeNewColumn($viewer)
->setSequence(0)
->setProperty('isDefault', true)
->setProjectPHID($project->getPHID())
->save();
$column->attachProject($project);
$columns[0] = $column;
unset($unguarded);
break;
case 'import':
return id(new AphrontRedirectResponse())
->setURI(
$this->getApplicationURI('board/'.$project->getID().'/import/'));
break;
default:
return $this->initializeWorkboardDialog($project);
break;
}
}
ksort($columns);
$board_uri = $this->getApplicationURI('board/'.$project->getID().'/');
$engine = id(new ManiphestTaskSearchEngine())
->setViewer($viewer)
->setBaseURI($board_uri)
->setIsBoardView(true);
if ($request->isFormPost()) {
$saved = $engine->buildSavedQueryFromRequest($request);
$engine->saveQuery($saved);
$filter_form = id(new AphrontFormView())
->setUser($viewer);
$engine->buildSearchForm($filter_form, $saved);
if ($engine->getErrors()) {
return $this->newDialog()
->setWidth(AphrontDialogView::WIDTH_FULL)
->setTitle(pht('Advanced Filter'))
->appendChild($filter_form->buildLayoutView())
->setErrors($engine->getErrors())
->setSubmitURI($board_uri)
->addSubmitButton(pht('Apply Filter'))
->addCancelButton($board_uri);
}
return id(new AphrontRedirectResponse())->setURI(
$this->getURIWithState(
$engine->getQueryResultsPageURI($saved->getQueryKey())));
}
$query_key = $request->getURIData('queryKey');
if (!$query_key) {
$query_key = 'open';
}
$this->queryKey = $query_key;
$custom_query = null;
if ($engine->isBuiltinQuery($query_key)) {
$saved = $engine->buildSavedQueryFromBuiltin($query_key);
} else {
$saved = id(new PhabricatorSavedQueryQuery())
->setViewer($viewer)
->withQueryKeys(array($query_key))
->executeOne();
if (!$saved) {
return new Aphront404Response();
}
$custom_query = $saved;
}
if ($request->getURIData('filter')) {
$filter_form = id(new AphrontFormView())
->setUser($viewer);
$engine->buildSearchForm($filter_form, $saved);
return $this->newDialog()
->setWidth(AphrontDialogView::WIDTH_FULL)
->setTitle(pht('Advanced Filter'))
->appendChild($filter_form->buildLayoutView())
->setSubmitURI($board_uri)
->addSubmitButton(pht('Apply Filter'))
->addCancelButton($board_uri);
}
$task_query = $engine->buildQueryFromSavedQuery($saved);
$tasks = $task_query
->withEdgeLogicPHIDs(
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST,
PhabricatorQueryConstraint::OPERATOR_AND,
array($project->getPHID()))
->setOrder(ManiphestTaskQuery::ORDER_PRIORITY)
->setViewer($viewer)
->execute();
$tasks = mpull($tasks, null, 'getPHID');
if ($tasks) {
$positions = id(new PhabricatorProjectColumnPositionQuery())
->setViewer($viewer)
->withObjectPHIDs(mpull($tasks, 'getPHID'))
->withColumns($columns)
->execute();
$positions = mpull($positions, null, 'getObjectPHID');
} else {
$positions = array();
}
$task_map = array();
foreach ($tasks as $task) {
$task_phid = $task->getPHID();
if (empty($positions[$task_phid])) {
// This shouldn't normally be possible because we create positions on
// demand, but we might have raced as an object was removed from the
// board. Just drop the task if we don't have a position for it.
continue;
}
$position = $positions[$task_phid];
$task_map[$position->getColumnPHID()][] = $task_phid;
}
// If we're showing the board in "natural" order, sort columns by their
// column positions.
if ($this->sortKey == PhabricatorProjectColumn::ORDER_NATURAL) {
foreach ($task_map as $column_phid => $task_phids) {
$order = array();
foreach ($task_phids as $task_phid) {
if (isset($positions[$task_phid])) {
$order[$task_phid] = $positions[$task_phid]->getOrderingKey();
} else {
$order[$task_phid] = 0;
}
}
asort($order);
$task_map[$column_phid] = array_keys($order);
}
}
$task_can_edit_map = id(new PhabricatorPolicyFilter())
->setViewer($viewer)
->requireCapabilities(array(PhabricatorPolicyCapability::CAN_EDIT))
->apply($tasks);
// If this is a batch edit, select the editable tasks in the chosen column
// and ship the user into the batch editor.
$batch_edit = $request->getStr('batch');
if ($batch_edit) {
if ($batch_edit !== self::BATCH_EDIT_ALL) {
$column_id_map = mpull($columns, null, 'getID');
$batch_column = idx($column_id_map, $batch_edit);
if (!$batch_column) {
return new Aphront404Response();
}
$batch_task_phids = idx($task_map, $batch_column->getPHID(), array());
foreach ($batch_task_phids as $key => $batch_task_phid) {
if (empty($task_can_edit_map[$batch_task_phid])) {
unset($batch_task_phids[$key]);
}
}
$batch_tasks = array_select_keys($tasks, $batch_task_phids);
} else {
$batch_tasks = $task_can_edit_map;
}
if (!$batch_tasks) {
$cancel_uri = $this->getURIWithState($board_uri);
return $this->newDialog()
->setTitle(pht('No Editable Tasks'))
->appendParagraph(
pht(
'The selected column contains no visible tasks which you '.
'have permission to edit.'))
->addCancelButton($board_uri);
}
$batch_ids = mpull($batch_tasks, 'getID');
$batch_ids = implode(',', $batch_ids);
$batch_uri = new PhutilURI('/maniphest/batch/');
$batch_uri->setQueryParam('board', $this->id);
$batch_uri->setQueryParam('batch', $batch_ids);
return id(new AphrontRedirectResponse())
->setURI($batch_uri);
}
$board_id = celerity_generate_unique_node_id();
$board = id(new PHUIWorkboardView())
->setUser($viewer)
->setID($board_id);
$behavior_config = array(
'boardID' => $board_id,
'projectPHID' => $project->getPHID(),
'moveURI' => $this->getApplicationURI('move/'.$project->getID().'/'),
'createURI' => $this->getCreateURI(),
'order' => $this->sortKey,
);
$this->initBehavior(
'project-boards',
$behavior_config);
$this->handles = ManiphestTaskListView::loadTaskHandles($viewer, $tasks);
foreach ($columns as $column) {
$task_phids = idx($task_map, $column->getPHID(), array());
$column_tasks = array_select_keys($tasks, $task_phids);
$panel = id(new PHUIWorkpanelView())
->setHeader($column->getDisplayName())
->setSubHeader($column->getDisplayType())
->addSigil('workpanel');
$header_icon = $column->getHeaderIcon();
if ($header_icon) {
$panel->setHeaderIcon($header_icon);
}
if ($column->isHidden()) {
$panel->addClass('project-panel-hidden');
}
$column_menu = $this->buildColumnMenu($project, $column);
$panel->addHeaderAction($column_menu);
$tag_id = celerity_generate_unique_node_id();
$tag_content_id = celerity_generate_unique_node_id();
$count_tag = id(new PHUITagView())
->setType(PHUITagView::TYPE_SHADE)
->setShade(PHUITagView::COLOR_BLUE)
->setID($tag_id)
->setName(phutil_tag('span', array('id' => $tag_content_id), '-'))
->setStyle('display: none');
$panel->setHeaderTag($count_tag);
$cards = id(new PHUIObjectItemListView())
->setUser($viewer)
->setFlush(true)
->setAllowEmptyList(true)
->addSigil('project-column')
->setMetadata(
array(
'columnPHID' => $column->getPHID(),
'countTagID' => $tag_id,
'countTagContentID' => $tag_content_id,
'pointLimit' => $column->getPointLimit(),
));
foreach ($column_tasks 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);
$board->addPanel($panel);
}
$sort_menu = $this->buildSortMenu(
$viewer,
$sort_key);
$filter_menu = $this->buildFilterMenu(
$viewer,
$custom_query,
$engine,
$query_key);
$manage_menu = $this->buildManageMenu($project, $show_hidden);
$header_link = phutil_tag(
'a',
array(
'href' => $this->getApplicationURI('profile/'.$project->getID().'/'),
),
$project->getName());
$header = id(new PHUIHeaderView())
->setHeader($header_link)
->setUser($viewer)
->setNoBackground(true)
->addActionLink($sort_menu)
->addActionLink($filter_menu)
->addActionLink($manage_menu)
->setPolicyObject($project);
$header_box = id(new PHUIBoxView())
->appendChild($header)
->addClass('project-board-header');
$board_box = id(new PHUIBoxView())
->appendChild($board)
->addClass('project-board-wrapper');
- $nav = $this->buildIconNavView($project);
+ $nav = $this->getProfileMenu();
return $this->newPage()
->setTitle(pht('%s Board', $project->getName()))
->setPageObjectPHIDs(array($project->getPHID()))
->setShowFooter(false)
->setNavigation($nav)
->addQuicksandConfig(
array(
'boardConfig' => $behavior_config,
))
->appendChild(
array(
$header_box,
$board_box,
));
}
private function buildSortMenu(
PhabricatorUser $viewer,
$sort_key) {
$sort_icon = id(new PHUIIconView())
->setIconFont('fa-sort-amount-asc bluegrey');
$named = array(
PhabricatorProjectColumn::ORDER_NATURAL => pht('Natural'),
PhabricatorProjectColumn::ORDER_PRIORITY => pht('Sort by Priority'),
);
$base_uri = $this->getURIWithState();
$items = array();
foreach ($named as $key => $name) {
$is_selected = ($key == $sort_key);
if ($is_selected) {
$active_order = $name;
}
$item = id(new PhabricatorActionView())
->setIcon('fa-sort-amount-asc')
->setSelected($is_selected)
->setName($name);
$uri = $base_uri->alter('order', $key);
$item->setHref($uri);
$items[] = $item;
}
$sort_menu = id(new PhabricatorActionListView())
->setUser($viewer);
foreach ($items as $item) {
$sort_menu->addAction($item);
}
$sort_button = id(new PHUIButtonView())
->setText(pht('Sort: %s', $active_order))
->setIcon($sort_icon)
->setTag('a')
->setHref('#')
->addSigil('boards-dropdown-menu')
->setMetadata(
array(
'items' => hsprintf('%s', $sort_menu),
));
return $sort_button;
}
private function buildFilterMenu(
PhabricatorUser $viewer,
$custom_query,
PhabricatorApplicationSearchEngine $engine,
$query_key) {
$filter_icon = id(new PHUIIconView())
->setIconFont('fa-search-plus bluegrey');
$named = array(
'open' => pht('Open Tasks'),
'all' => pht('All Tasks'),
);
if ($viewer->isLoggedIn()) {
$named['assigned'] = pht('Assigned to Me');
}
if ($custom_query) {
$named[$custom_query->getQueryKey()] = pht('Custom Filter');
}
$items = array();
foreach ($named as $key => $name) {
$is_selected = ($key == $query_key);
if ($is_selected) {
$active_filter = $name;
}
$is_custom = false;
if ($custom_query) {
$is_custom = ($key == $custom_query->getQueryKey());
}
$item = id(new PhabricatorActionView())
->setIcon('fa-search')
->setSelected($is_selected)
->setName($name);
if ($is_custom) {
$uri = $this->getApplicationURI(
'board/'.$this->id.'/filter/query/'.$key.'/');
$item->setWorkflow(true);
} else {
$uri = $engine->getQueryResultsPageURI($key);
}
$uri = $this->getURIWithState($uri);
$item->setHref($uri);
$items[] = $item;
}
$items[] = id(new PhabricatorActionView())
->setIcon('fa-cog')
->setHref($this->getApplicationURI('board/'.$this->id.'/filter/'))
->setWorkflow(true)
->setName(pht('Advanced Filter...'));
$filter_menu = id(new PhabricatorActionListView())
->setUser($viewer);
foreach ($items as $item) {
$filter_menu->addAction($item);
}
$filter_button = id(new PHUIButtonView())
->setText(pht('Filter: %s', $active_filter))
->setIcon($filter_icon)
->setTag('a')
->setHref('#')
->addSigil('boards-dropdown-menu')
->setMetadata(
array(
'items' => hsprintf('%s', $filter_menu),
));
return $filter_button;
}
private function buildManageMenu(
PhabricatorProject $project,
$show_hidden) {
$request = $this->getRequest();
$viewer = $request->getUser();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$project,
PhabricatorPolicyCapability::CAN_EDIT);
$manage_icon = id(new PHUIIconView())
->setIconFont('fa-cog bluegrey');
$manage_items = array();
$manage_items[] = id(new PhabricatorActionView())
->setIcon('fa-plus')
->setName(pht('Add Column'))
->setHref($this->getApplicationURI('board/'.$this->id.'/edit/'))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit);
$manage_items[] = id(new PhabricatorActionView())
->setIcon('fa-exchange')
->setName(pht('Reorder Columns'))
->setHref($this->getApplicationURI('board/'.$this->id.'/reorder/'))
->setDisabled(!$can_edit)
->setWorkflow(true);
if ($show_hidden) {
$hidden_uri = $this->getURIWithState()
->setQueryParam('hidden', null);
$hidden_icon = 'fa-eye-slash';
$hidden_text = pht('Hide Hidden Columns');
} else {
$hidden_uri = $this->getURIWithState()
->setQueryParam('hidden', 'true');
$hidden_icon = 'fa-eye';
$hidden_text = pht('Show Hidden Columns');
}
$manage_items[] = id(new PhabricatorActionView())
->setIcon($hidden_icon)
->setName($hidden_text)
->setHref($hidden_uri);
$batch_edit_uri = $request->getRequestURI();
$batch_edit_uri->setQueryParam('batch', self::BATCH_EDIT_ALL);
$can_batch_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
PhabricatorApplication::getByClass('PhabricatorManiphestApplication'),
ManiphestBulkEditCapability::CAPABILITY);
$manage_items[] = id(new PhabricatorActionView())
->setIcon('fa-list-ul')
->setName(pht('Batch Edit Visible Tasks...'))
->setHref($batch_edit_uri)
->setDisabled(!$can_batch_edit);
$manage_menu = id(new PhabricatorActionListView())
->setUser($viewer);
foreach ($manage_items as $item) {
$manage_menu->addAction($item);
}
$manage_button = id(new PHUIButtonView())
->setText(pht('Manage Board'))
->setIcon($manage_icon)
->setTag('a')
->setHref('#')
->addSigil('boards-dropdown-menu')
->setMetadata(
array(
'items' => hsprintf('%s', $manage_menu),
));
return $manage_button;
}
private function buildColumnMenu(
PhabricatorProject $project,
PhabricatorProjectColumn $column) {
$request = $this->getRequest();
$viewer = $request->getUser();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$project,
PhabricatorPolicyCapability::CAN_EDIT);
$column_items = array();
$column_items[] = id(new PhabricatorActionView())
->setIcon('fa-plus')
->setName(pht('Create Task...'))
->setHref($this->getCreateURI())
->addSigil('column-add-task')
->setMetadata(
array(
'columnPHID' => $column->getPHID(),
));
$batch_edit_uri = $request->getRequestURI();
$batch_edit_uri->setQueryParam('batch', $column->getID());
$can_batch_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
PhabricatorApplication::getByClass('PhabricatorManiphestApplication'),
ManiphestBulkEditCapability::CAPABILITY);
$column_items[] = id(new PhabricatorActionView())
->setIcon('fa-list-ul')
->setName(pht('Batch Edit Tasks...'))
->setHref($batch_edit_uri)
->setDisabled(!$can_batch_edit);
$detail_uri = $this->getApplicationURI(
'board/'.$this->id.'/column/'.$column->getID().'/');
$column_items[] = id(new PhabricatorActionView())
->setIcon('fa-columns')
->setName(pht('Column Details'))
->setHref($detail_uri);
$can_hide = ($can_edit && !$column->isDefaultColumn());
$hide_uri = 'board/'.$this->id.'/hide/'.$column->getID().'/';
$hide_uri = $this->getApplicationURI($hide_uri);
$hide_uri = $this->getURIWithState($hide_uri);
if (!$column->isHidden()) {
$column_items[] = id(new PhabricatorActionView())
->setName(pht('Hide Column'))
->setIcon('fa-eye-slash')
->setHref($hide_uri)
->setDisabled(!$can_hide)
->setWorkflow(true);
} else {
$column_items[] = id(new PhabricatorActionView())
->setName(pht('Show Column'))
->setIcon('fa-eye')
->setHref($hide_uri)
->setDisabled(!$can_hide)
->setWorkflow(true);
}
$column_menu = id(new PhabricatorActionListView())
->setUser($viewer);
foreach ($column_items as $item) {
$column_menu->addAction($item);
}
$column_button = id(new PHUIIconView())
->setIconFont('fa-caret-down')
->setHref('#')
->addSigil('boards-dropdown-menu')
->setMetadata(
array(
'items' => hsprintf('%s', $column_menu),
));
return $column_button;
}
private function initializeWorkboardDialog(PhabricatorProject $project) {
$instructions = pht('This workboard has not been setup yet.');
$new_selector = id(new AphrontFormRadioButtonControl())
->setName('initialize-type')
->setValue('backlog-only')
->addButton(
'backlog-only',
pht('New Empty Board'),
pht('Create a new board with just a backlog column.'))
->addButton(
'import',
pht('Import Columns'),
pht('Import board columns from another project.'));
$dialog = id(new AphrontDialogView())
->setUser($this->getRequest()->getUser())
->setTitle(pht('New Workboard'))
->addSubmitButton('Continue')
->addCancelButton($this->getApplicationURI('view/'.$project->getID().'/'))
->appendParagraph($instructions)
->appendChild($new_selector);
return id(new AphrontDialogResponse())
->setDialog($dialog);
}
private function noAccessDialog(PhabricatorProject $project) {
$instructions = pht('This workboard has not been setup yet.');
$dialog = id(new AphrontDialogView())
->setUser($this->getRequest()->getUser())
->setTitle(pht('No Workboard'))
->addCancelButton($this->getApplicationURI('view/'.$project->getID().'/'))
->appendParagraph($instructions);
return id(new AphrontDialogResponse())
->setDialog($dialog);
}
/**
* Add current state parameters (like order and the visibility of hidden
* columns) to a URI.
*
* This allows actions which toggle or adjust one piece of state to keep
* the rest of the board state persistent. If no URI is provided, this method
* starts with the request URI.
*
* @param string|null URI to add state parameters to.
* @return PhutilURI URI with state parameters.
*/
private function getURIWithState($base = null) {
if ($base === null) {
$base = $this->getRequest()->getRequestURI();
}
$base = new PhutilURI($base);
if ($this->sortKey != PhabricatorProjectColumn::DEFAULT_ORDER) {
$base->setQueryParam('order', $this->sortKey);
} else {
$base->setQueryParam('order', null);
}
$base->setQueryParam('hidden', $this->showHidden ? 'true' : null);
return $base;
}
private function getCreateURI() {
$viewer = $this->getViewer();
// TODO: This should be cleaned up, but maybe we're going to make options
// for each column or board?
$edit_config = id(new ManiphestEditEngine())
->setViewer($viewer)
->loadDefaultEditConfiguration();
if ($edit_config) {
$form_key = $edit_config->getIdentifier();
$create_uri = "/maniphest/task/edit/form/{$form_key}/";
} else {
$create_uri = '/maniphest/task/edit/';
}
return $create_uri;
}
}
diff --git a/src/applications/project/controller/PhabricatorProjectColumnDetailController.php b/src/applications/project/controller/PhabricatorProjectColumnDetailController.php
index 01b520c6c3..ca25c089fb 100644
--- a/src/applications/project/controller/PhabricatorProjectColumnDetailController.php
+++ b/src/applications/project/controller/PhabricatorProjectColumnDetailController.php
@@ -1,122 +1,123 @@
<?php
final class PhabricatorProjectColumnDetailController
extends PhabricatorProjectBoardController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$project_id = $request->getURIData('projectID');
$project = id(new PhabricatorProjectQuery())
->setViewer($viewer)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
))
->withIDs(array($project_id))
->needImages(true)
->executeOne();
if (!$project) {
return new Aphront404Response();
}
$this->setProject($project);
$column = id(new PhabricatorProjectColumnQuery())
->setViewer($viewer)
->withIDs(array($id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
))
->executeOne();
if (!$column) {
return new Aphront404Response();
}
$timeline = $this->buildTransactionTimeline(
$column,
new PhabricatorProjectColumnTransactionQuery());
$timeline->setShouldTerminate(true);
$title = $column->getDisplayName();
$header = $this->buildHeaderView($column);
$actions = $this->buildActionView($column);
$properties = $this->buildPropertyView($column, $actions);
$box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
- $nav = $this->buildIconNavView($project);
- $nav->appendChild($box);
- $nav->appendChild($timeline);
-
- return $this->buildApplicationPage(
- $nav,
- array(
- 'title' => $title,
- ));
+ $nav = $this->getProfileMenu();
+
+ return $this->newPage()
+ ->setTitle($title)
+ ->setNavigation($nav)
+ ->appendChild(
+ array(
+ $box,
+ $timeline,
+ ));
}
private function buildHeaderView(PhabricatorProjectColumn $column) {
$viewer = $this->getRequest()->getUser();
$header = id(new PHUIHeaderView())
->setUser($viewer)
->setHeader($column->getDisplayName())
->setPolicyObject($column);
if ($column->isHidden()) {
$header->setStatus('fa-ban', 'dark', pht('Hidden'));
}
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())
->setUser($viewer);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$column,
PhabricatorPolicyCapability::CAN_EDIT);
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Column'))
->setIcon('fa-pencil')
->setHref($this->getApplicationURI($base_uri.'edit/'.$id.'/'))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
return $actions;
}
private function buildPropertyView(
PhabricatorProjectColumn $column,
PhabricatorActionListView $actions) {
$viewer = $this->getRequest()->getUser();
$properties = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($column)
->setActionList($actions);
$limit = $column->getPointLimit();
$properties->addProperty(
pht('Point Limit'),
$limit ? $limit : pht('No Limit'));
return $properties;
}
}
diff --git a/src/applications/project/controller/PhabricatorProjectColumnEditController.php b/src/applications/project/controller/PhabricatorProjectColumnEditController.php
index d59c3648fe..77f90b56cb 100644
--- a/src/applications/project/controller/PhabricatorProjectColumnEditController.php
+++ b/src/applications/project/controller/PhabricatorProjectColumnEditController.php
@@ -1,156 +1,154 @@
<?php
final class PhabricatorProjectColumnEditController
extends PhabricatorProjectBoardController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$project_id = $request->getURIData('projectID');
$project = id(new PhabricatorProjectQuery())
->setViewer($viewer)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->withIDs(array($project_id))
->needImages(true)
->executeOne();
if (!$project) {
return new Aphront404Response();
}
$this->setProject($project);
$is_new = ($id ? false : true);
if (!$is_new) {
$column = id(new PhabricatorProjectColumnQuery())
->setViewer($viewer)
->withIDs(array($id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$column) {
return new Aphront404Response();
}
} else {
$column = PhabricatorProjectColumn::initializeNewColumn($viewer);
}
$e_name = null;
$e_limit = null;
$v_limit = $column->getPointLimit();
$v_name = $column->getName();
$validation_exception = null;
$base_uri = '/board/'.$project_id.'/';
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/'.$id.'/');
}
if ($request->isFormPost()) {
$v_name = $request->getStr('name');
$v_limit = $request->getStr('limit');
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);
}
$xactions = array();
$type_name = PhabricatorProjectColumnTransaction::TYPE_NAME;
$xactions[] = id(new PhabricatorProjectColumnTransaction())
->setTransactionType($type_name)
->setNewValue($v_name);
$type_limit = PhabricatorProjectColumnTransaction::TYPE_LIMIT;
$xactions[] = id(new PhabricatorProjectColumnTransaction())
->setTransactionType($type_limit)
->setNewValue($v_limit);
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);
$e_limit = $ex->getShortMessage($type_limit);
$validation_exception = $ex;
}
}
$form = new AphrontFormView();
$form
->setUser($request->getUser())
->appendChild(
id(new AphrontFormTextControl())
->setValue($v_name)
->setLabel(pht('Name'))
->setName('name')
->setError($e_name)
->setCaption(
pht('This will be displayed as the header of the column.')))
->appendChild(
id(new AphrontFormTextControl())
->setValue($v_limit)
->setLabel(pht('Point Limit'))
->setName('limit')
->setError($e_limit)
->setCaption(
pht('Maximum number of points of tasks allowed in the column.')));
if ($is_new) {
$title = pht('Create Column');
$submit = pht('Create Column');
} else {
$title = pht('Edit %s', $column->getDisplayName());
$submit = pht('Save Column');
}
$form->appendChild(
id(new AphrontFormSubmitControl())
->setValue($submit)
->addCancelButton($view_uri));
$form_box = id(new PHUIObjectBoxView())
->setHeaderText($title)
->setValidationException($validation_exception)
->setForm($form);
- $nav = $this->buildIconNavView($project);
- $nav->appendChild($form_box);
-
- return $this->buildApplicationPage(
- $nav,
- array(
- 'title' => $title,
- ));
+ $nav = $this->getProfileMenu();
+
+ return $this->newPage()
+ ->setTitle($title)
+ ->setNavigation($nav)
+ ->appendChild($form_box);
}
}
diff --git a/src/applications/project/controller/PhabricatorProjectController.php b/src/applications/project/controller/PhabricatorProjectController.php
index c3e333f5fb..0729010480 100644
--- a/src/applications/project/controller/PhabricatorProjectController.php
+++ b/src/applications/project/controller/PhabricatorProjectController.php
@@ -1,147 +1,131 @@
<?php
abstract class PhabricatorProjectController extends PhabricatorController {
private $project;
+ private $profileMenu;
protected function setProject(PhabricatorProject $project) {
$this->project = $project;
return $this;
}
protected function getProject() {
return $this->project;
}
protected function loadProject() {
$viewer = $this->getViewer();
$request = $this->getRequest();
$id = $request->getURIData('id');
$slug = $request->getURIData('slug');
if ($slug) {
$normal_slug = PhabricatorSlug::normalizeProjectSlug($slug);
$is_abnormal = ($slug !== $normal_slug);
$normal_uri = "/tag/{$normal_slug}/";
} else {
$is_abnormal = false;
}
$query = id(new PhabricatorProjectQuery())
->setViewer($viewer)
->needMembers(true)
->needWatchers(true)
->needImages(true)
->needSlugs(true);
if ($slug) {
$query->withSlugs(array($slug));
} else {
$query->withIDs(array($id));
}
$policy_exception = null;
try {
$project = $query->executeOne();
} catch (PhabricatorPolicyException $ex) {
$policy_exception = $ex;
$project = null;
}
if (!$project) {
// This project legitimately does not exist, so just 404 the user.
if (!$policy_exception) {
return new Aphront404Response();
}
// Here, the project exists but the user can't see it. If they are
// using a non-canonical slug to view the project, redirect to the
// canonical slug. If they're already using the canonical slug, rethrow
// the exception to give them the policy error.
if ($is_abnormal) {
return id(new AphrontRedirectResponse())->setURI($normal_uri);
} else {
throw $policy_exception;
}
}
// The user can view the project, but is using a noncanonical slug.
// Redirect to the canonical slug.
$primary_slug = $project->getPrimarySlug();
if ($slug && ($slug !== $primary_slug)) {
$primary_uri = "/tag/{$primary_slug}/";
return id(new AphrontRedirectResponse())->setURI($primary_uri);
}
$this->setProject($project);
return null;
}
public function buildApplicationMenu() {
- return $this->buildSideNavView(true)->getMenu();
- }
+ $menu = $this->newApplicationMenu();
- public function buildSideNavView($for_app = false) {
- $project = $this->getProject();
+ $profile_menu = $this->getProfileMenu();
+ if ($profile_menu) {
+ $menu->setProfileMenu($profile_menu);
+ }
+ $menu->setSearchEngine(new PhabricatorProjectSearchEngine());
+ return $menu;
+ }
- $nav = new AphrontSideNavFilterView();
- $nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
+ protected function getProfileMenu() {
+ if (!$this->profileMenu) {
+ $project = $this->getProject();
+ if ($project) {
+ $viewer = $this->getViewer();
- $viewer = $this->getViewer();
+ $engine = id(new PhabricatorProfilePanelEngine())
+ ->setViewer($viewer)
+ ->setProfileObject($project);
- $id = null;
- if ($for_app) {
- if ($project) {
- $id = $project->getID();
- $nav->addFilter("profile/{$id}/", pht('Profile'));
- $nav->addFilter("board/{$id}/", pht('Workboard'));
- $nav->addFilter("members/{$id}/", pht('Members'));
- $nav->addFilter("feed/{$id}/", pht('Feed'));
+ $this->profileMenu = $engine->buildNavigation();
}
- $nav->addFilter('create', pht('Create Project'));
- }
-
- if (!$id) {
- id(new PhabricatorProjectSearchEngine())
- ->setViewer($viewer)
- ->addNavigationItems($nav->getMenu());
}
- $nav->selectFilter(null);
-
- return $nav;
- }
-
- public function buildIconNavView(PhabricatorProject $project) {
- $viewer = $this->getViewer();
-
- $engine = id(new PhabricatorProfilePanelEngine())
- ->setViewer($viewer)
- ->setProfileObject($project);
-
- return $engine->buildNavigation();
+ return $this->profileMenu;
}
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
$project = $this->getProject();
if ($project) {
$ancestors = $project->getAncestorProjects();
$ancestors = array_reverse($ancestors);
$ancestors[] = $project;
foreach ($ancestors as $ancestor) {
$crumbs->addTextCrumb(
$ancestor->getName(),
$ancestor->getURI());
}
}
return $crumbs;
}
}
diff --git a/src/applications/project/controller/PhabricatorProjectEditPictureController.php b/src/applications/project/controller/PhabricatorProjectEditPictureController.php
index eff471194f..8828bfae57 100644
--- a/src/applications/project/controller/PhabricatorProjectEditPictureController.php
+++ b/src/applications/project/controller/PhabricatorProjectEditPictureController.php
@@ -1,294 +1,297 @@
<?php
final class PhabricatorProjectEditPictureController
extends PhabricatorProjectController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$project = id(new PhabricatorProjectQuery())
->setViewer($viewer)
->withIDs(array($id))
->needImages(true)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$project) {
return new Aphront404Response();
}
+ $this->setProject($project);
+
$edit_uri = $this->getApplicationURI('profile/'.$project->getID().'/');
$view_uri = $this->getApplicationURI('profile/'.$project->getID().'/');
$supported_formats = PhabricatorFile::getTransformableImageFormats();
$e_file = true;
$errors = array();
if ($request->isFormPost()) {
$phid = $request->getStr('phid');
$is_default = false;
if ($phid == PhabricatorPHIDConstants::PHID_VOID) {
$phid = null;
$is_default = true;
} else if ($phid) {
$file = id(new PhabricatorFileQuery())
->setViewer($viewer)
->withPHIDs(array($phid))
->executeOne();
} else {
if ($request->getFileExists('picture')) {
$file = PhabricatorFile::newFromPHPUpload(
$_FILES['picture'],
array(
'authorPHID' => $viewer->getPHID(),
'canCDN' => true,
));
} else {
$e_file = pht('Required');
$errors[] = pht(
'You must choose a file when uploading a new project picture.');
}
}
if (!$errors && !$is_default) {
if (!$file->isTransformableImage()) {
$e_file = pht('Not Supported');
$errors[] = pht(
'This server only supports these image formats: %s.',
implode(', ', $supported_formats));
} else {
$xform = PhabricatorFileTransform::getTransformByKey(
PhabricatorFileThumbnailTransform::TRANSFORM_PROFILE);
$xformed = $xform->executeTransform($file);
}
}
if (!$errors) {
if ($is_default) {
$new_value = null;
} else {
$new_value = $xformed->getPHID();
}
$xactions = array();
$xactions[] = id(new PhabricatorProjectTransaction())
->setTransactionType(PhabricatorProjectTransaction::TYPE_IMAGE)
->setNewValue($new_value);
$editor = id(new PhabricatorProjectTransactionEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnMissingFields(true)
->setContinueOnNoEffect(true);
$editor->applyTransactions($project, $xactions);
return id(new AphrontRedirectResponse())->setURI($edit_uri);
}
}
$title = pht('Edit Project Picture');
$form = id(new PHUIFormLayoutView())
->setUser($viewer);
$default_image = PhabricatorFile::loadBuiltin($viewer, 'project.png');
$images = array();
$current = $project->getProfileImagePHID();
$has_current = false;
if ($current) {
$files = id(new PhabricatorFileQuery())
->setViewer($viewer)
->withPHIDs(array($current))
->execute();
if ($files) {
$file = head($files);
if ($file->isTransformableImage()) {
$has_current = true;
$images[$current] = array(
'uri' => $file->getBestURI(),
'tip' => pht('Current Picture'),
);
}
}
}
$images[PhabricatorPHIDConstants::PHID_VOID] = array(
'uri' => $default_image->getBestURI(),
'tip' => pht('Default Picture'),
);
require_celerity_resource('people-profile-css');
Javelin::initBehavior('phabricator-tooltips', array());
$buttons = array();
foreach ($images as $phid => $spec) {
$button = javelin_tag(
'button',
array(
'class' => 'grey profile-image-button',
'sigil' => 'has-tooltip',
'meta' => array(
'tip' => $spec['tip'],
'size' => 300,
),
),
phutil_tag(
'img',
array(
'height' => 50,
'width' => 50,
'src' => $spec['uri'],
)));
$button = array(
phutil_tag(
'input',
array(
'type' => 'hidden',
'name' => 'phid',
'value' => $phid,
)),
$button,
);
$button = phabricator_form(
$viewer,
array(
'class' => 'profile-image-form',
'method' => 'POST',
),
$button);
$buttons[] = $button;
}
if ($has_current) {
$form->appendChild(
id(new AphrontFormMarkupControl())
->setLabel(pht('Current Picture'))
->setValue(array_shift($buttons)));
}
$form->appendChild(
id(new AphrontFormMarkupControl())
->setLabel(pht('Use Picture'))
->setValue($buttons));
$launch_id = celerity_generate_unique_node_id();
$input_id = celerity_generate_unique_node_id();
Javelin::initBehavior(
'launch-icon-composer',
array(
'launchID' => $launch_id,
'inputID' => $input_id,
));
$compose_button = javelin_tag(
'button',
array(
'class' => 'grey',
'id' => $launch_id,
'sigil' => 'icon-composer',
),
pht('Choose Icon and Color...'));
$compose_input = javelin_tag(
'input',
array(
'type' => 'hidden',
'id' => $input_id,
'name' => 'phid',
));
$compose_form = phabricator_form(
$viewer,
array(
'class' => 'profile-image-form',
'method' => 'POST',
),
array(
$compose_input,
$compose_button,
));
$form->appendChild(
id(new AphrontFormMarkupControl())
->setLabel(pht('Quick Create'))
->setValue($compose_form));
$default_button = javelin_tag(
'button',
array(
'class' => 'grey',
),
pht('Use Project Icon'));
$default_input = javelin_tag(
'input',
array(
'type' => 'hidden',
'name' => 'projectPHID',
'value' => $project->getPHID(),
));
$default_form = phabricator_form(
$viewer,
array(
'class' => 'profile-image-form',
'method' => 'POST',
'action' => '/file/compose/',
),
array(
$default_input,
$default_button,
));
$form->appendChild(
id(new AphrontFormMarkupControl())
->setLabel(pht('Use Default'))
->setValue($default_form));
$upload_form = id(new AphrontFormView())
->setUser($viewer)
->setEncType('multipart/form-data')
->appendChild(
id(new AphrontFormFileControl())
->setName('picture')
->setLabel(pht('Upload Picture'))
->setError($e_file)
->setCaption(
pht('Supported formats: %s', implode(', ', $supported_formats))))
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton($edit_uri)
->setValue(pht('Upload Picture')));
$form_box = id(new PHUIObjectBoxView())
->setHeaderText($title)
->setFormErrors($errors)
->setForm($form);
$upload_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Upload New Picture'))
->setForm($upload_form);
- $nav = $this->buildIconNavView($project);
+ $nav = $this->getProfileMenu();
$nav->selectFilter(PhabricatorProject::PANEL_PROFILE);
- $nav->appendChild($form_box);
- $nav->appendChild($upload_box);
- return $this->buildApplicationPage(
- $nav,
- array(
- 'title' => $title,
- ));
+ return $this->newPage()
+ ->setTitle($title)
+ ->setNavigation($nav)
+ ->appendChild(
+ array(
+ $form_box,
+ $upload_box,
+ ));
}
}
diff --git a/src/applications/project/controller/PhabricatorProjectFeedController.php b/src/applications/project/controller/PhabricatorProjectFeedController.php
index 80bb3378e8..a108384088 100644
--- a/src/applications/project/controller/PhabricatorProjectFeedController.php
+++ b/src/applications/project/controller/PhabricatorProjectFeedController.php
@@ -1,62 +1,62 @@
<?php
final class PhabricatorProjectFeedController
extends PhabricatorProjectController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getUser();
$response = $this->loadProject();
if ($response) {
return $response;
}
$project = $this->getProject();
$id = $project->getID();
$stories = id(new PhabricatorFeedQuery())
->setViewer($viewer)
->setFilterPHIDs(
array(
$project->getPHID(),
))
->setLimit(50)
->execute();
$feed = $this->renderStories($stories);
$box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Project Activity'))
->appendChild($feed);
- $nav = $this->buildIconNavView($project);
+ $nav = $this->getProfileMenu();
$nav->selectFilter('feed');
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Feed'));
return $this->newPage()
->setNavigation($nav)
->setCrumbs($crumbs)
->setTitle(array($project->getName(), pht('Feed')))
->appendChild($box);
}
private function renderStories(array $stories) {
assert_instances_of($stories, 'PhabricatorFeedStory');
$builder = new PhabricatorFeedBuilder($stories);
$builder->setUser($this->getRequest()->getUser());
$builder->setShowHovercards(true);
$view = $builder->buildView();
return phutil_tag_div(
'profile-feed',
$view->render());
}
}
diff --git a/src/applications/project/controller/PhabricatorProjectListController.php b/src/applications/project/controller/PhabricatorProjectListController.php
index db4ef5b9f5..021cdfbe3f 100644
--- a/src/applications/project/controller/PhabricatorProjectListController.php
+++ b/src/applications/project/controller/PhabricatorProjectListController.php
@@ -1,36 +1,26 @@
<?php
final class PhabricatorProjectListController
extends PhabricatorProjectController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
- $viewer = $request->getViewer();
- $query_key = $request->getURIData('queryKey');
-
- $controller = id(new PhabricatorApplicationSearchController())
- ->setQueryKey($query_key)
- ->setSearchEngine(new PhabricatorProjectSearchEngine())
- ->setNavigation($this->buildSideNavView());
-
- return $this->delegateToController($controller);
- }
-
- public function buildApplicationMenu() {
- return $this->buildSideNavView(true)->getMenu();
+ return id(new PhabricatorProjectSearchEngine())
+ ->setController($this)
+ ->buildResponse();
}
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
id(new PhabricatorProjectEditEngine())
->setViewer($this->getViewer())
->addActionToCrumbs($crumbs);
return $crumbs;
}
}
diff --git a/src/applications/project/controller/PhabricatorProjectMembersEditController.php b/src/applications/project/controller/PhabricatorProjectMembersEditController.php
index f45d40d8ae..f485e2997a 100644
--- a/src/applications/project/controller/PhabricatorProjectMembersEditController.php
+++ b/src/applications/project/controller/PhabricatorProjectMembersEditController.php
@@ -1,154 +1,156 @@
<?php
final class PhabricatorProjectMembersEditController
extends PhabricatorProjectController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$project = id(new PhabricatorProjectQuery())
->setViewer($viewer)
->withIDs(array($id))
->needMembers(true)
->needImages(true)
->executeOne();
if (!$project) {
return new Aphront404Response();
}
+ $this->setProject($project);
+
$member_phids = $project->getMemberPHIDs();
if ($request->isFormPost()) {
$member_spec = array();
$remove = $request->getStr('remove');
if ($remove) {
$member_spec['-'] = array_fuse(array($remove));
}
$add_members = $request->getArr('phids');
if ($add_members) {
$member_spec['+'] = array_fuse($add_members);
}
$type_member = PhabricatorProjectProjectHasMemberEdgeType::EDGECONST;
$xactions = array();
$xactions[] = id(new PhabricatorProjectTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
->setMetadataValue('edge:type', $type_member)
->setNewValue($member_spec);
$editor = id(new PhabricatorProjectTransactionEditor($project))
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true)
->applyTransactions($project, $xactions);
return id(new AphrontRedirectResponse())
->setURI($request->getRequestURI());
}
$member_phids = array_reverse($member_phids);
$handles = $this->loadViewerHandles($member_phids);
$state = array();
foreach ($handles as $handle) {
$state[] = array(
'phid' => $handle->getPHID(),
'name' => $handle->getFullName(),
);
}
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$project,
PhabricatorPolicyCapability::CAN_EDIT);
$supports_edit = $project->supportsEditMembers();
$form_box = null;
$title = pht('Add Members');
if ($can_edit && $supports_edit) {
$header_name = pht('Edit Members');
$view_uri = $this->getApplicationURI('profile/'.$project->getID().'/');
$form = new AphrontFormView();
$form
->setUser($viewer)
->appendControl(
id(new AphrontFormTokenizerControl())
->setName('phids')
->setLabel(pht('Add Members'))
->setDatasource(new PhabricatorPeopleDatasource()))
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton($view_uri)
->setValue(pht('Add Members')));
$form_box = id(new PHUIObjectBoxView())
->setHeaderText($title)
->setForm($form);
}
$member_list = $this->renderMemberList($project, $handles);
- $nav = $this->buildIconNavView($project);
+ $nav = $this->getProfileMenu();
$nav->selectFilter(PhabricatorProject::PANEL_MEMBERS);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Members'));
return $this->newPage()
->setNavigation($nav)
->setCrumbs($crumbs)
->setTitle(array($project->getName(), $title))
->appendChild($form_box)
->appendChild($member_list);
}
private function renderMemberList(
PhabricatorProject $project,
array $handles) {
$request = $this->getRequest();
$viewer = $request->getUser();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$project,
PhabricatorPolicyCapability::CAN_EDIT);
$list = id(new PHUIObjectItemListView())
->setNoDataString(pht('This project does not have any members.'));
foreach ($handles as $handle) {
$remove_uri = $this->getApplicationURI(
'/members/'.$project->getID().'/remove/?phid='.$handle->getPHID());
$item = id(new PHUIObjectItemView())
->setHeader($handle->getFullName())
->setHref($handle->getURI())
->setImageURI($handle->getImageURI());
if ($can_edit) {
$item->addAction(
id(new PHUIListItemView())
->setIcon('fa-times')
->setName(pht('Remove'))
->setHref($remove_uri)
->setWorkflow(true));
}
$list->addItem($item);
}
$box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Members'))
->setObjectList($list);
return $box;
}
}
diff --git a/src/applications/project/controller/PhabricatorProjectMilestonesController.php b/src/applications/project/controller/PhabricatorProjectMilestonesController.php
index db0810f819..595035bdb7 100644
--- a/src/applications/project/controller/PhabricatorProjectMilestonesController.php
+++ b/src/applications/project/controller/PhabricatorProjectMilestonesController.php
@@ -1,92 +1,92 @@
<?php
final class PhabricatorProjectMilestonesController
extends PhabricatorProjectController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$response = $this->loadProject();
if ($response) {
return $response;
}
$project = $this->getProject();
$id = $project->getID();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$project,
PhabricatorPolicyCapability::CAN_EDIT);
$has_support = $project->supportsMilestones();
if ($has_support) {
$milestones = id(new PhabricatorProjectQuery())
->setViewer($viewer)
->withParentProjectPHIDs(array($project->getPHID()))
->needImages(true)
->withIsMilestone(true)
->setOrder('newest')
->execute();
} else {
$milestones = array();
}
$can_create = $can_edit && $has_support;
if ($project->getHasMilestones()) {
$button_text = pht('Create Next Milestone');
} else {
$button_text = pht('Add Milestones');
}
$header = id(new PHUIHeaderView())
->setHeader(pht('Milestones'))
->addActionLink(
id(new PHUIButtonView())
->setTag('a')
->setHref("/project/edit/?milestone={$id}")
->setIconFont('fa-plus')
->setDisabled(!$can_create)
->setWorkflow(!$can_create)
->setText($button_text));
$box = id(new PHUIObjectBoxView())
->setHeader($header);
if (!$has_support) {
$no_support = pht(
'This project is a milestone. Milestones can not have their own '.
'milestones.');
$info_view = id(new PHUIInfoView())
->setErrors(array($no_support))
->setSeverity(PHUIInfoView::SEVERITY_WARNING);
$box->setInfoView($info_view);
}
$box->setObjectList(
id(new PhabricatorProjectListView())
->setUser($viewer)
->setProjects($milestones)
->renderList());
- $nav = $this->buildIconNavView($project);
+ $nav = $this->getProfileMenu();
$nav->selectFilter(PhabricatorProject::PANEL_MILESTONES);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Milestones'));
return $this->newPage()
->setNavigation($nav)
->setCrumbs($crumbs)
->setTitle(array($project->getName(), pht('Milestones')))
->appendChild($box);
}
}
diff --git a/src/applications/project/controller/PhabricatorProjectProfileController.php b/src/applications/project/controller/PhabricatorProjectProfileController.php
index e588681ef5..a2eb5eebe6 100644
--- a/src/applications/project/controller/PhabricatorProjectProfileController.php
+++ b/src/applications/project/controller/PhabricatorProjectProfileController.php
@@ -1,230 +1,230 @@
<?php
final class PhabricatorProjectProfileController
extends PhabricatorProjectController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getUser();
$response = $this->loadProject();
if ($response) {
return $response;
}
$project = $this->getProject();
$id = $project->getID();
$picture = $project->getProfileImageURI();
$header = id(new PHUIHeaderView())
->setHeader($project->getName())
->setUser($viewer)
->setPolicyObject($project)
->setImage($picture);
if ($project->getStatus() == PhabricatorProjectStatus::STATUS_ACTIVE) {
$header->setStatus('fa-check', 'bluegrey', pht('Active'));
} else {
$header->setStatus('fa-ban', 'red', pht('Archived'));
}
$actions = $this->buildActionListView($project);
$properties = $this->buildPropertyListView($project, $actions);
$object_box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
$timeline = $this->buildTransactionTimeline(
$project,
new PhabricatorProjectTransactionQuery());
$timeline->setShouldTerminate(true);
- $nav = $this->buildIconNavView($project);
+ $nav = $this->getProfileMenu();
$nav->selectFilter(PhabricatorProject::PANEL_PROFILE);
$crumbs = $this->buildApplicationCrumbs();
return $this->newPage()
->setNavigation($nav)
->setCrumbs($crumbs)
->setTitle($project->getName())
->setPageObjectPHIDs(array($project->getPHID()))
->appendChild($object_box)
->appendChild($timeline);
}
private function buildActionListView(PhabricatorProject $project) {
$request = $this->getRequest();
$viewer = $request->getUser();
$id = $project->getID();
$view = id(new PhabricatorActionListView())
->setUser($viewer)
->setObject($project);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$project,
PhabricatorPolicyCapability::CAN_EDIT);
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Details'))
->setIcon('fa-pencil')
->setHref($this->getApplicationURI("edit/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Picture'))
->setIcon('fa-picture-o')
->setHref($this->getApplicationURI("picture/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
if ($project->isArchived()) {
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Activate Project'))
->setIcon('fa-check')
->setHref($this->getApplicationURI("archive/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(true));
} else {
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Archive Project'))
->setIcon('fa-ban')
->setHref($this->getApplicationURI("archive/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(true));
}
$can_lock = $can_edit && $this->hasApplicationCapability(
ProjectCanLockProjectsCapability::CAPABILITY);
if ($project->getIsMembershipLocked()) {
$lock_name = pht('Unlock Project');
$lock_icon = 'fa-unlock';
} else {
$lock_name = pht('Lock Project');
$lock_icon = 'fa-lock';
}
$view->addAction(
id(new PhabricatorActionView())
->setName($lock_name)
->setIcon($lock_icon)
->setHref($this->getApplicationURI("lock/{$id}/"))
->setDisabled(!$can_lock)
->setWorkflow(true));
$action = null;
if (!$project->isUserMember($viewer->getPHID())) {
$can_join = PhabricatorPolicyFilter::hasCapability(
$viewer,
$project,
PhabricatorPolicyCapability::CAN_JOIN);
$action = id(new PhabricatorActionView())
->setUser($viewer)
->setRenderAsForm(true)
->setHref('/project/update/'.$project->getID().'/join/')
->setIcon('fa-plus')
->setDisabled(!$can_join)
->setName(pht('Join Project'));
$view->addAction($action);
} else {
$action = id(new PhabricatorActionView())
->setWorkflow(true)
->setHref('/project/update/'.$project->getID().'/leave/')
->setIcon('fa-times')
->setName(pht('Leave Project...'));
$view->addAction($action);
if (!$project->isUserWatcher($viewer->getPHID())) {
$action = id(new PhabricatorActionView())
->setWorkflow(true)
->setHref('/project/watch/'.$project->getID().'/')
->setIcon('fa-eye')
->setName(pht('Watch Project'));
$view->addAction($action);
} else {
$action = id(new PhabricatorActionView())
->setWorkflow(true)
->setHref('/project/unwatch/'.$project->getID().'/')
->setIcon('fa-eye-slash')
->setName(pht('Unwatch Project'));
$view->addAction($action);
}
}
return $view;
}
private function buildPropertyListView(
PhabricatorProject $project,
PhabricatorActionListView $actions) {
$request = $this->getRequest();
$viewer = $request->getUser();
$view = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($project)
->setActionList($actions);
$hashtags = array();
foreach ($project->getSlugs() as $slug) {
$hashtags[] = id(new PHUITagView())
->setType(PHUITagView::TYPE_OBJECT)
->setName('#'.$slug->getSlug());
}
if ($hashtags) {
$view->addProperty(pht('Hashtags'), phutil_implode_html(' ', $hashtags));
}
$view->addProperty(
pht('Members'),
$project->getMemberPHIDs()
? $viewer
->renderHandleList($project->getMemberPHIDs())
->setAsInline(true)
: phutil_tag('em', array(), pht('None')));
$view->addProperty(
pht('Watchers'),
$project->getWatcherPHIDs()
? $viewer
->renderHandleList($project->getWatcherPHIDs())
->setAsInline(true)
: phutil_tag('em', array(), pht('None')));
$descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions(
$viewer,
$project);
$view->addProperty(
pht('Looks Like'),
$viewer->renderHandle($project->getPHID())->setAsTag(true));
$view->addProperty(
pht('Joinable By'),
$descriptions[PhabricatorPolicyCapability::CAN_JOIN]);
$field_list = PhabricatorCustomField::getObjectFields(
$project,
PhabricatorCustomField::ROLE_VIEW);
$field_list->appendFieldsToPropertyList($project, $viewer, $view);
return $view;
}
}
diff --git a/src/applications/project/controller/PhabricatorProjectSubprojectsController.php b/src/applications/project/controller/PhabricatorProjectSubprojectsController.php
index 50f3294c71..e512d12a6e 100644
--- a/src/applications/project/controller/PhabricatorProjectSubprojectsController.php
+++ b/src/applications/project/controller/PhabricatorProjectSubprojectsController.php
@@ -1,91 +1,91 @@
<?php
final class PhabricatorProjectSubprojectsController
extends PhabricatorProjectController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$response = $this->loadProject();
if ($response) {
return $response;
}
$project = $this->getProject();
$id = $project->getID();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$project,
PhabricatorPolicyCapability::CAN_EDIT);
$has_support = $project->supportsSubprojects();
if ($has_support) {
$subprojects = id(new PhabricatorProjectQuery())
->setViewer($viewer)
->withParentProjectPHIDs(array($project->getPHID()))
->needImages(true)
->withIsMilestone(false)
->execute();
} else {
$subprojects = array();
}
$can_create = $can_edit && $has_support;
if ($project->getHasSubprojects()) {
$button_text = pht('Create Subproject');
} else {
$button_text = pht('Add Subprojects');
}
$header = id(new PHUIHeaderView())
->setHeader(pht('Subprojects'))
->addActionLink(
id(new PHUIButtonView())
->setTag('a')
->setHref("/project/edit/?parent={$id}")
->setIconFont('fa-plus')
->setDisabled(!$can_create)
->setWorkflow(!$can_create)
->setText($button_text));
$box = id(new PHUIObjectBoxView())
->setHeader($header);
if (!$has_support) {
$no_support = pht(
'This project is a milestone. Milestones can not have subprojects.');
$info_view = id(new PHUIInfoView())
->setErrors(array($no_support))
->setSeverity(PHUIInfoView::SEVERITY_WARNING);
$box->setInfoView($info_view);
}
$box->setObjectList(
id(new PhabricatorProjectListView())
->setUser($viewer)
->setProjects($subprojects)
->renderList());
- $nav = $this->buildIconNavView($project);
+ $nav = $this->getProfileMenu();
$nav->selectFilter(PhabricatorProject::PANEL_SUBPROJECTS);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Subprojects'));
return $this->newPage()
->setNavigation($nav)
->setCrumbs($crumbs)
->setTitle(array($project->getName(), pht('Subprojects')))
->appendChild($box);
}
}
diff --git a/src/view/layout/PHUIApplicationMenuView.php b/src/view/layout/PHUIApplicationMenuView.php
index 624b1f982d..f542674de9 100644
--- a/src/view/layout/PHUIApplicationMenuView.php
+++ b/src/view/layout/PHUIApplicationMenuView.php
@@ -1,89 +1,109 @@
<?php
final class PHUIApplicationMenuView extends Phobject {
private $viewer;
private $crumbs;
private $searchEngine;
+ private $profileMenu;
private $items = array();
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
}
public function getViewer() {
return $this->viewer;
}
public function addLabel($name) {
$item = id(new PHUIListItemView())
->setName($name);
return $this->addItem($item);
}
public function addLink($name, $href) {
$item = id(new PHUIListItemView())
->setName($name)
->setHref($href);
return $this->addItem($item);
}
+ public function setProfileMenu(
+ AphrontSideNavFilterView $nav) {
+ $this->profileMenu = $nav;
+ return $this;
+ }
+
+ public function getProfileMenu() {
+ return $this->profileMenu;
+ }
+
public function addItem(PHUIListItemView $item) {
$this->items[] = $item;
return $this;
}
public function setSearchEngine(PhabricatorApplicationSearchEngine $engine) {
$this->searchEngine = $engine;
return $this;
}
public function getSearchEngine() {
return $this->searchEngine;
}
public function setCrumbs(PHUICrumbsView $crumbs) {
$this->crumbs = $crumbs;
return $this;
}
public function getCrumbs() {
return $this->crumbs;
}
public function buildListView() {
$viewer = $this->getViewer();
$view = id(new PHUIListView())
->setUser($viewer);
+ $profile_menu = $this->getProfileMenu();
+ if ($profile_menu) {
+ foreach ($profile_menu->getMenu()->getItems() as $item) {
+ $item = clone $item;
+ $item->setRenderNameAsTooltip(false);
+ $view->addMenuItem($item);
+ }
+ }
+
$crumbs = $this->getCrumbs();
if ($crumbs) {
$actions = $crumbs->getActions();
if ($actions) {
$view->newLabel(pht('Create'));
foreach ($crumbs->getActions() as $action) {
$view->addMenuItem($action);
}
}
}
$engine = $this->getSearchEngine();
if ($engine) {
$engine
->setViewer($viewer)
->addNavigationItems($view);
}
foreach ($this->items as $item) {
$view->addMenuItem($item);
}
return $view;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Aug 14, 4:21 PM (1 d, 4 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
193540
Default Alt Text
(71 KB)

Event Timeline