mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-13 02:12:41 +01:00
Add a basic progress bar for milestones
Summary: Ref T4427. This kind of works. Test Plan: {F1100578} Reviewers: chad Reviewed By: chad Maniphest Tasks: T4427 Differential Revision: https://secure.phabricator.com/D15221
This commit is contained in:
parent
f84130f9cd
commit
0782652a80
12 changed files with 293 additions and 31 deletions
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
return array(
|
return array(
|
||||||
'names' => array(
|
'names' => array(
|
||||||
'core.pkg.css' => 'b4a7e275',
|
'core.pkg.css' => 'bef9c7cb',
|
||||||
'core.pkg.js' => '17380dd3',
|
'core.pkg.js' => '17380dd3',
|
||||||
'darkconsole.pkg.js' => 'e7393ebb',
|
'darkconsole.pkg.js' => 'e7393ebb',
|
||||||
'differential.pkg.css' => '2de124c9',
|
'differential.pkg.css' => '2de124c9',
|
||||||
|
@ -146,10 +146,10 @@ return array(
|
||||||
'rsrc/css/phui/phui-object-item-list-view.css' => '8f443e8b',
|
'rsrc/css/phui/phui-object-item-list-view.css' => '8f443e8b',
|
||||||
'rsrc/css/phui/phui-pager.css' => 'bea33d23',
|
'rsrc/css/phui/phui-pager.css' => 'bea33d23',
|
||||||
'rsrc/css/phui/phui-pinboard-view.css' => '2495140e',
|
'rsrc/css/phui/phui-pinboard-view.css' => '2495140e',
|
||||||
'rsrc/css/phui/phui-profile-menu.css' => '4a243229',
|
'rsrc/css/phui/phui-profile-menu.css' => '2d5f0c75',
|
||||||
'rsrc/css/phui/phui-property-list-view.css' => '27b2849e',
|
'rsrc/css/phui/phui-property-list-view.css' => '27b2849e',
|
||||||
'rsrc/css/phui/phui-remarkup-preview.css' => '1a8f2591',
|
'rsrc/css/phui/phui-remarkup-preview.css' => '1a8f2591',
|
||||||
'rsrc/css/phui/phui-segment-bar-view.css' => '728e4d19',
|
'rsrc/css/phui/phui-segment-bar-view.css' => '52e7e529',
|
||||||
'rsrc/css/phui/phui-spacing.css' => '042804d6',
|
'rsrc/css/phui/phui-spacing.css' => '042804d6',
|
||||||
'rsrc/css/phui/phui-status.css' => '888cedb8',
|
'rsrc/css/phui/phui-status.css' => '888cedb8',
|
||||||
'rsrc/css/phui/phui-tag-view.css' => '9d5d4400',
|
'rsrc/css/phui/phui-tag-view.css' => '9d5d4400',
|
||||||
|
@ -823,10 +823,10 @@ return array(
|
||||||
'phui-object-item-list-view-css' => '8f443e8b',
|
'phui-object-item-list-view-css' => '8f443e8b',
|
||||||
'phui-pager-css' => 'bea33d23',
|
'phui-pager-css' => 'bea33d23',
|
||||||
'phui-pinboard-view-css' => '2495140e',
|
'phui-pinboard-view-css' => '2495140e',
|
||||||
'phui-profile-menu-css' => '4a243229',
|
'phui-profile-menu-css' => '2d5f0c75',
|
||||||
'phui-property-list-view-css' => '27b2849e',
|
'phui-property-list-view-css' => '27b2849e',
|
||||||
'phui-remarkup-preview-css' => '1a8f2591',
|
'phui-remarkup-preview-css' => '1a8f2591',
|
||||||
'phui-segment-bar-view-css' => '728e4d19',
|
'phui-segment-bar-view-css' => '52e7e529',
|
||||||
'phui-spacing-css' => '042804d6',
|
'phui-spacing-css' => '042804d6',
|
||||||
'phui-status-list-view-css' => '888cedb8',
|
'phui-status-list-view-css' => '888cedb8',
|
||||||
'phui-tag-view-css' => '9d5d4400',
|
'phui-tag-view-css' => '9d5d4400',
|
||||||
|
|
|
@ -2933,6 +2933,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorProjectOrUserFunctionDatasource' => 'applications/project/typeahead/PhabricatorProjectOrUserFunctionDatasource.php',
|
'PhabricatorProjectOrUserFunctionDatasource' => 'applications/project/typeahead/PhabricatorProjectOrUserFunctionDatasource.php',
|
||||||
'PhabricatorProjectPHIDResolver' => 'applications/phid/resolver/PhabricatorProjectPHIDResolver.php',
|
'PhabricatorProjectPHIDResolver' => 'applications/phid/resolver/PhabricatorProjectPHIDResolver.php',
|
||||||
'PhabricatorProjectPanelController' => 'applications/project/controller/PhabricatorProjectPanelController.php',
|
'PhabricatorProjectPanelController' => 'applications/project/controller/PhabricatorProjectPanelController.php',
|
||||||
|
'PhabricatorProjectPointsProfilePanel' => 'applications/project/profilepanel/PhabricatorProjectPointsProfilePanel.php',
|
||||||
'PhabricatorProjectProfileController' => 'applications/project/controller/PhabricatorProjectProfileController.php',
|
'PhabricatorProjectProfileController' => 'applications/project/controller/PhabricatorProjectProfileController.php',
|
||||||
'PhabricatorProjectProfilePanelEngine' => 'applications/project/engine/PhabricatorProjectProfilePanelEngine.php',
|
'PhabricatorProjectProfilePanelEngine' => 'applications/project/engine/PhabricatorProjectProfilePanelEngine.php',
|
||||||
'PhabricatorProjectProjectHasMemberEdgeType' => 'applications/project/edge/PhabricatorProjectProjectHasMemberEdgeType.php',
|
'PhabricatorProjectProjectHasMemberEdgeType' => 'applications/project/edge/PhabricatorProjectProjectHasMemberEdgeType.php',
|
||||||
|
@ -7364,6 +7365,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorProjectOrUserFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
'PhabricatorProjectOrUserFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||||
'PhabricatorProjectPHIDResolver' => 'PhabricatorPHIDResolver',
|
'PhabricatorProjectPHIDResolver' => 'PhabricatorPHIDResolver',
|
||||||
'PhabricatorProjectPanelController' => 'PhabricatorProjectController',
|
'PhabricatorProjectPanelController' => 'PhabricatorProjectController',
|
||||||
|
'PhabricatorProjectPointsProfilePanel' => 'PhabricatorProfilePanel',
|
||||||
'PhabricatorProjectProfileController' => 'PhabricatorProjectController',
|
'PhabricatorProjectProfileController' => 'PhabricatorProjectController',
|
||||||
'PhabricatorProjectProfilePanelEngine' => 'PhabricatorProfilePanelEngine',
|
'PhabricatorProjectProfilePanelEngine' => 'PhabricatorProfilePanelEngine',
|
||||||
'PhabricatorProjectProjectHasMemberEdgeType' => 'PhabricatorEdgeType',
|
'PhabricatorProjectProjectHasMemberEdgeType' => 'PhabricatorEdgeType',
|
||||||
|
|
|
@ -307,28 +307,45 @@ final class ManiphestEditEngine
|
||||||
// currently leave the card where it was but should really move it to the
|
// currently leave the card where it was but should really move it to the
|
||||||
// proper new column.
|
// proper new column.
|
||||||
|
|
||||||
|
$board_phid = $column->getProjectPHID();
|
||||||
|
|
||||||
$descendant_projects = id(new PhabricatorProjectQuery())
|
$descendant_projects = id(new PhabricatorProjectQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withAncestorProjectPHIDs(array($column->getProjectPHID()))
|
->withAncestorProjectPHIDs(array($column->getProjectPHID()))
|
||||||
->execute();
|
->execute();
|
||||||
$board_phids = mpull($descendant_projects, 'getPHID', 'getPHID');
|
$board_phids = mpull($descendant_projects, 'getPHID', 'getPHID');
|
||||||
$board_phids[$column->getProjectPHID()] = $column->getProjectPHID();
|
$board_phids[$board_phid] = $board_phid;
|
||||||
|
|
||||||
$project_map = array_fuse($task->getProjectPHIDs());
|
$project_map = array_fuse($task->getProjectPHIDs());
|
||||||
$remove_card = !array_intersect_key($board_phids, $project_map);
|
$remove_card = !array_intersect_key($board_phids, $project_map);
|
||||||
|
|
||||||
$positions = id(new PhabricatorProjectColumnPositionQuery())
|
// TODO: Maybe the caller should pass a list of visible task PHIDs so we
|
||||||
|
// know which ones we need to reorder? This is a HUGE overfetch.
|
||||||
|
$objects = id(new ManiphestTaskQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withEdgeLogicPHIDs(
|
||||||
|
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST,
|
||||||
|
PhabricatorQueryConstraint::OPERATOR_ANCESTOR,
|
||||||
|
array($board_phids))
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withBoardPHIDs(array($column->getProjectPHID()))
|
|
||||||
->withColumnPHIDs(array($column->getPHID()))
|
|
||||||
->execute();
|
->execute();
|
||||||
$task_phids = mpull($positions, 'getObjectPHID');
|
$objects = mpull($objects, null, 'getPHID');
|
||||||
|
|
||||||
$column_tasks = id(new ManiphestTaskQuery())
|
$layout_engine = id(new PhabricatorBoardLayoutEngine())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withPHIDs($task_phids)
|
->setBoardPHIDs(array($board_phid))
|
||||||
->needProjectPHIDs(true)
|
->setObjectPHIDs(array_keys($objects))
|
||||||
->execute();
|
->executeLayout();
|
||||||
|
|
||||||
|
$positions = $layout_engine->getColumnObjectPositions(
|
||||||
|
$board_phid,
|
||||||
|
$column_phid);
|
||||||
|
|
||||||
|
$column_phids = $layout_engine->getColumnObjectPHIDs(
|
||||||
|
$board_phid,
|
||||||
|
$column_phid);
|
||||||
|
|
||||||
|
$column_tasks = array_select_keys($objects, $column_phids);
|
||||||
|
|
||||||
if ($order == PhabricatorProjectColumn::ORDER_NATURAL) {
|
if ($order == PhabricatorProjectColumn::ORDER_NATURAL) {
|
||||||
// TODO: This is a little bit awkward, because PHP and JS use
|
// TODO: This is a little bit awkward, because PHP and JS use
|
||||||
|
|
|
@ -86,9 +86,14 @@ final class PhabricatorBoardLayoutEngine extends Phobject {
|
||||||
return array_select_keys($this->columnMap, array_keys($columns));
|
return array_select_keys($this->columnMap, array_keys($columns));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getColumnObjectPHIDs($board_phid, $column_phid) {
|
public function getColumnObjectPositions($board_phid, $column_phid) {
|
||||||
$columns = idx($this->boardLayout, $board_phid, array());
|
$columns = idx($this->boardLayout, $board_phid, array());
|
||||||
$positions = idx($columns, $column_phid, array());
|
return idx($columns, $column_phid, array());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getColumnObjectPHIDs($board_phid, $column_phid) {
|
||||||
|
$positions = $this->getColumnObjectPositions($board_phid, $column_phid);
|
||||||
return mpull($positions, 'getObjectPHID');
|
return mpull($positions, 'getObjectPHID');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,10 @@ final class PhabricatorProjectProfilePanelEngine
|
||||||
->setBuiltinKey(PhabricatorProject::PANEL_PROFILE)
|
->setBuiltinKey(PhabricatorProject::PANEL_PROFILE)
|
||||||
->setPanelKey(PhabricatorProjectDetailsProfilePanel::PANELKEY);
|
->setPanelKey(PhabricatorProjectDetailsProfilePanel::PANELKEY);
|
||||||
|
|
||||||
|
$panels[] = $this->newPanel()
|
||||||
|
->setBuiltinKey(PhabricatorProject::PANEL_POINTS)
|
||||||
|
->setPanelKey(PhabricatorProjectPointsProfilePanel::PANELKEY);
|
||||||
|
|
||||||
$panels[] = $this->newPanel()
|
$panels[] = $this->newPanel()
|
||||||
->setBuiltinKey(PhabricatorProject::PANEL_WORKBOARD)
|
->setBuiltinKey(PhabricatorProject::PANEL_WORKBOARD)
|
||||||
->setPanelKey(PhabricatorProjectWorkboardProfilePanel::PANELKEY);
|
->setPanelKey(PhabricatorProjectWorkboardProfilePanel::PANELKEY);
|
||||||
|
|
|
@ -0,0 +1,192 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorProjectPointsProfilePanel
|
||||||
|
extends PhabricatorProfilePanel {
|
||||||
|
|
||||||
|
const PANELKEY = 'project.points';
|
||||||
|
|
||||||
|
public function getPanelTypeName() {
|
||||||
|
return pht('Project Points');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getDefaultName() {
|
||||||
|
return pht('Points Bar');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function shouldEnableForObject($object) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
// Only render this element for milestones.
|
||||||
|
if (!$object->isMilestone()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't show if points aren't configured.
|
||||||
|
if (!ManiphestTaskPoints::getIsEnabled()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Points are only available if Maniphest is installed.
|
||||||
|
$class = 'PhabricatorManiphestApplication';
|
||||||
|
if (!PhabricatorApplication::isClassInstalledForViewer($class, $viewer)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDisplayName(
|
||||||
|
PhabricatorProfilePanelConfiguration $config) {
|
||||||
|
return $this->getDefaultName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildEditEngineFields(
|
||||||
|
PhabricatorProfilePanelConfiguration $config) {
|
||||||
|
return array(
|
||||||
|
id(new PhabricatorInstructionsEditField())
|
||||||
|
->setValue(
|
||||||
|
pht(
|
||||||
|
'This is a progress bar which shows how many points of work '.
|
||||||
|
'are complete within the milestone. It has no configurable '.
|
||||||
|
'settings.')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function newNavigationMenuItems(
|
||||||
|
PhabricatorProfilePanelConfiguration $config) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
$project = $config->getProfileObject();
|
||||||
|
|
||||||
|
$limit = 250;
|
||||||
|
|
||||||
|
$tasks = id(new ManiphestTaskQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withEdgeLogicPHIDs(
|
||||||
|
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST,
|
||||||
|
PhabricatorQueryConstraint::OPERATOR_AND,
|
||||||
|
array($project->getPHID()))
|
||||||
|
->setLimit($limit + 1)
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
if (count($tasks) > $limit) {
|
||||||
|
return $this->renderError(
|
||||||
|
pht(
|
||||||
|
'Too many tasks to compute statistics for (more than %s).',
|
||||||
|
new PhutilNumber($limit)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$tasks) {
|
||||||
|
return $this->renderError(
|
||||||
|
pht(
|
||||||
|
'This milestone has no tasks yet.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$statuses = array();
|
||||||
|
$points_done = 0;
|
||||||
|
$points_total = 0;
|
||||||
|
$no_points = 0;
|
||||||
|
foreach ($tasks as $task) {
|
||||||
|
$points = $task->getPoints();
|
||||||
|
|
||||||
|
if ($points === null) {
|
||||||
|
$no_points++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$points) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$status = $task->getStatus();
|
||||||
|
if (empty($statuses[$status])) {
|
||||||
|
$statuses[$status] = 0;
|
||||||
|
}
|
||||||
|
$statuses[$status] += $points;
|
||||||
|
|
||||||
|
if (ManiphestTaskStatus::isClosedStatus($status)) {
|
||||||
|
$points_done += $points;
|
||||||
|
}
|
||||||
|
|
||||||
|
$points_total += $points;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($no_points == count($tasks)) {
|
||||||
|
return $this->renderError(
|
||||||
|
pht('No tasks have assigned point values.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!$points_total) {
|
||||||
|
return $this->renderError(
|
||||||
|
pht('All tasks with assigned point values are worth zero points.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$label = pht(
|
||||||
|
'%s of %s %s',
|
||||||
|
new PhutilNumber($points_done),
|
||||||
|
new PhutilNumber($points_total),
|
||||||
|
ManiphestTaskPoints::getPointsLabel());
|
||||||
|
|
||||||
|
$bar = id(new PHUISegmentBarView())
|
||||||
|
->setLabel($label);
|
||||||
|
|
||||||
|
$map = ManiphestTaskStatus::getTaskStatusMap();
|
||||||
|
$statuses = array_select_keys($statuses, array_keys($map));
|
||||||
|
|
||||||
|
foreach ($statuses as $status => $points) {
|
||||||
|
if (!$points) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ManiphestTaskStatus::isClosedStatus($status)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$color = ManiphestTaskStatus::getStatusColor($status);
|
||||||
|
if (!$color) {
|
||||||
|
$color = 'sky';
|
||||||
|
}
|
||||||
|
|
||||||
|
$tooltip = pht(
|
||||||
|
'%s %s',
|
||||||
|
new PhutilNumber($points),
|
||||||
|
ManiphestTaskStatus::getTaskStatusName($status));
|
||||||
|
|
||||||
|
$bar->newSegment()
|
||||||
|
->setWidth($points / $points_total)
|
||||||
|
->setColor($color)
|
||||||
|
->setTooltip($tooltip);
|
||||||
|
}
|
||||||
|
|
||||||
|
$bar = phutil_tag(
|
||||||
|
'div',
|
||||||
|
array(
|
||||||
|
'class' => 'phui-profile-segment-bar',
|
||||||
|
),
|
||||||
|
$bar);
|
||||||
|
|
||||||
|
$item = $this->newItem()
|
||||||
|
->appendChild($bar);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
$item,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function renderError($message) {
|
||||||
|
$message = phutil_tag(
|
||||||
|
'div',
|
||||||
|
array(
|
||||||
|
'class' => 'phui-profile-menu-error',
|
||||||
|
),
|
||||||
|
$message);
|
||||||
|
|
||||||
|
$item = $this->newItem()
|
||||||
|
->appendChild($message);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
$item,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -18,6 +18,18 @@ final class PhabricatorProjectWorkboardProfilePanel
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function shouldEnableForObject($object) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
// Workboards are only available if Maniphest is installed.
|
||||||
|
$class = 'PhabricatorManiphestApplication';
|
||||||
|
if (!PhabricatorApplication::isClassInstalledForViewer($class, $viewer)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public function getDisplayName(
|
public function getDisplayName(
|
||||||
PhabricatorProfilePanelConfiguration $config) {
|
PhabricatorProfilePanelConfiguration $config) {
|
||||||
$name = $config->getPanelProperty('name');
|
$name = $config->getPanelProperty('name');
|
||||||
|
@ -42,14 +54,6 @@ final class PhabricatorProjectWorkboardProfilePanel
|
||||||
|
|
||||||
protected function newNavigationMenuItems(
|
protected function newNavigationMenuItems(
|
||||||
PhabricatorProfilePanelConfiguration $config) {
|
PhabricatorProfilePanelConfiguration $config) {
|
||||||
$viewer = $this->getViewer();
|
|
||||||
|
|
||||||
// Workboards are only available if Maniphest is installed.
|
|
||||||
$class = 'PhabricatorManiphestApplication';
|
|
||||||
if (!PhabricatorApplication::isClassInstalledForViewer($class, $viewer)) {
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
|
|
||||||
$project = $config->getProfileObject();
|
$project = $config->getProfileObject();
|
||||||
|
|
||||||
$has_workboard = $project->getHasWorkboard();
|
$has_workboard = $project->getHasWorkboard();
|
||||||
|
|
|
@ -48,6 +48,7 @@ final class PhabricatorProject extends PhabricatorProjectDAO
|
||||||
const TABLE_DATASOURCE_TOKEN = 'project_datasourcetoken';
|
const TABLE_DATASOURCE_TOKEN = 'project_datasourcetoken';
|
||||||
|
|
||||||
const PANEL_PROFILE = 'project.profile';
|
const PANEL_PROFILE = 'project.profile';
|
||||||
|
const PANEL_POINTS = 'project.points';
|
||||||
const PANEL_WORKBOARD = 'project.workboard';
|
const PANEL_WORKBOARD = 'project.workboard';
|
||||||
const PANEL_MEMBERS = 'project.members';
|
const PANEL_MEMBERS = 'project.members';
|
||||||
const PANEL_MANAGE = 'project.manage';
|
const PANEL_MANAGE = 'project.manage';
|
||||||
|
|
|
@ -236,6 +236,11 @@ abstract class PhabricatorProfilePanelEngine extends Phobject {
|
||||||
->withProfilePHIDs(array($object->getPHID()))
|
->withProfilePHIDs(array($object->getPHID()))
|
||||||
->execute();
|
->execute();
|
||||||
|
|
||||||
|
foreach ($stored_panels as $stored_panel) {
|
||||||
|
$impl = $stored_panel->getPanel();
|
||||||
|
$impl->setViewer($viewer);
|
||||||
|
}
|
||||||
|
|
||||||
// Merge the stored panels into the builtin panels. If a builtin panel has
|
// Merge the stored panels into the builtin panels. If a builtin panel has
|
||||||
// a stored version, replace the defaults with the stored changes.
|
// a stored version, replace the defaults with the stored changes.
|
||||||
foreach ($stored_panels as $stored_panel) {
|
foreach ($stored_panels as $stored_panel) {
|
||||||
|
@ -259,12 +264,6 @@ abstract class PhabricatorProfilePanelEngine extends Phobject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($panels as $panel) {
|
|
||||||
$impl = $panel->getPanel();
|
|
||||||
|
|
||||||
$impl->setViewer($viewer);
|
|
||||||
}
|
|
||||||
|
|
||||||
$panels = msort($panels, 'getSortKey');
|
$panels = msort($panels, 'getSortKey');
|
||||||
|
|
||||||
// Normalize keys since callers shouldn't rely on this array being
|
// Normalize keys since callers shouldn't rely on this array being
|
||||||
|
@ -306,6 +305,7 @@ abstract class PhabricatorProfilePanelEngine extends Phobject {
|
||||||
$builtins = $this->getBuiltinProfilePanels($object);
|
$builtins = $this->getBuiltinProfilePanels($object);
|
||||||
|
|
||||||
$panels = PhabricatorProfilePanel::getAllPanels();
|
$panels = PhabricatorProfilePanel::getAllPanels();
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
$order = 1;
|
$order = 1;
|
||||||
$map = array();
|
$map = array();
|
||||||
|
@ -339,6 +339,9 @@ abstract class PhabricatorProfilePanelEngine extends Phobject {
|
||||||
$panel_key));
|
$panel_key));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$panel = clone $panel;
|
||||||
|
$panel->setViewer($viewer);
|
||||||
|
|
||||||
$builtin
|
$builtin
|
||||||
->setProfilePHID($object->getPHID())
|
->setProfilePHID($object->getPHID())
|
||||||
->attachPanel($panel)
|
->attachPanel($panel)
|
||||||
|
|
|
@ -5,6 +5,7 @@ final class PHUISegmentBarSegmentView extends AphrontTagView {
|
||||||
private $width;
|
private $width;
|
||||||
private $color;
|
private $color;
|
||||||
private $position;
|
private $position;
|
||||||
|
private $tooltip;
|
||||||
|
|
||||||
public function setWidth($width) {
|
public function setWidth($width) {
|
||||||
$this->width = $width;
|
$this->width = $width;
|
||||||
|
@ -25,6 +26,11 @@ final class PHUISegmentBarSegmentView extends AphrontTagView {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setTooltip($tooltip) {
|
||||||
|
$this->tooltip = $tooltip;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
protected function canAppendChild() {
|
protected function canAppendChild() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -48,9 +54,25 @@ final class PHUISegmentBarSegmentView extends AphrontTagView {
|
||||||
$left = floor(100 * $left) / 100;
|
$left = floor(100 * $left) / 100;
|
||||||
$left = sprintf('%.2f%%', $left);
|
$left = sprintf('%.2f%%', $left);
|
||||||
|
|
||||||
|
$tooltip = $this->tooltip;
|
||||||
|
if (strlen($tooltip)) {
|
||||||
|
Javelin::initBehavior('phabricator-tooltips');
|
||||||
|
|
||||||
|
$sigil = 'has-tooltip';
|
||||||
|
$meta = array(
|
||||||
|
'tip' => $tooltip,
|
||||||
|
'align' => 'E',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$sigil = null;
|
||||||
|
$meta = null;
|
||||||
|
}
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'class' => implode(' ', $classes),
|
'class' => implode(' ', $classes),
|
||||||
'style' => "left: {$left}; width: {$width};",
|
'style' => "left: {$left}; width: {$width};",
|
||||||
|
'sigil' => $sigil,
|
||||||
|
'meta' => $meta,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -149,6 +149,18 @@
|
||||||
color: {$menu.profile.text};
|
color: {$menu.profile.text};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.phui-profile-menu .phabricator-side-menu .phui-profile-menu-error {
|
||||||
|
color: {$greytext};
|
||||||
|
font-size: {$smallerfontsize};
|
||||||
|
padding: 18px 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phui-profile-menu .phabricator-side-menu .phui-profile-segment-bar {
|
||||||
|
color: {$menu.profile.text};
|
||||||
|
padding: 12px 15px 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.phui-profile-menu .phabricator-side-menu .phui-profile-menu-spacer {
|
.phui-profile-menu .phabricator-side-menu .phui-profile-menu-spacer {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
height: {$menu.profile.item.height};
|
height: {$menu.profile.item.height};
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
margin-left: -4px;
|
margin-left: -5px;
|
||||||
border-right: 5px solid;
|
border-right: 5px solid;
|
||||||
border-radius: 0 4px 4px 0;
|
border-radius: 0 4px 4px 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue