1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-18 12:52:42 +01:00

Introduce "Curtain" views, panels, and extensions

Summary:
This opens up the new action column to have specialized rendering and behavior. Briefly:

  - Converted applications (right now, only Paste) render a `CurtainView` to build the column content.
  - This view uses new extensions to build panels (projects, subscribers, tokens).
  - The panel extension code and rendering can be changed without breaking old stuff.

Minor changes:

  - Token awards now load their tokens, for consistency/simplicity.
  - Removed the rest of the "fork of" / "forked from" UI in Paste -- I essentially removed these features a while ago, and no one has complained.

Test Plan:
UI is a bit rough, but works, and it's going to get changed now anyway:

{F1160550}

{F1160551}

Reviewers: chad

Reviewed By: chad

Differential Revision: https://secure.phabricator.com/D15414
This commit is contained in:
epriestley 2016-03-05 14:45:56 -08:00
parent aaab1011e5
commit 61f82bb97b
14 changed files with 586 additions and 73 deletions

View file

@ -127,6 +127,7 @@ return array(
'rsrc/css/phui/phui-button.css' => 'a64a8de6',
'rsrc/css/phui/phui-chart.css' => '6bf6f78e',
'rsrc/css/phui/phui-crumbs-view.css' => '79d536e5',
'rsrc/css/phui/phui-curtain-view.css' => '8bb7ee8f',
'rsrc/css/phui/phui-document-pro.css' => '92d5b648',
'rsrc/css/phui/phui-document-summary.css' => '9ca48bdf',
'rsrc/css/phui/phui-document.css' => '9c71d2bf',
@ -811,6 +812,7 @@ return array(
'phui-calendar-month-css' => '476be7e0',
'phui-chart-css' => '6bf6f78e',
'phui-crumbs-view-css' => '79d536e5',
'phui-curtain-view-css' => '8bb7ee8f',
'phui-document-summary-view-css' => '9ca48bdf',
'phui-document-view-css' => '9c71d2bf',
'phui-document-view-pro-css' => '92d5b648',

View file

@ -1505,6 +1505,9 @@ phutil_register_library_map(array(
'PHUIColorPalletteExample' => 'applications/uiexample/examples/PHUIColorPalletteExample.php',
'PHUICrumbView' => 'view/phui/PHUICrumbView.php',
'PHUICrumbsView' => 'view/phui/PHUICrumbsView.php',
'PHUICurtainExtension' => 'view/extension/PHUICurtainExtension.php',
'PHUICurtainPanelView' => 'view/layout/PHUICurtainPanelView.php',
'PHUICurtainView' => 'view/layout/PHUICurtainView.php',
'PHUIDiffInlineCommentDetailView' => 'infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php',
'PHUIDiffInlineCommentEditView' => 'infrastructure/diff/view/PHUIDiffInlineCommentEditView.php',
'PHUIDiffInlineCommentRowScaffold' => 'infrastructure/diff/view/PHUIDiffInlineCommentRowScaffold.php',
@ -3011,6 +3014,7 @@ phutil_register_library_map(array(
'PhabricatorProjectWatcherListView' => 'applications/project/view/PhabricatorProjectWatcherListView.php',
'PhabricatorProjectWorkboardBackgroundColor' => 'applications/project/constants/PhabricatorProjectWorkboardBackgroundColor.php',
'PhabricatorProjectWorkboardProfilePanel' => 'applications/project/profilepanel/PhabricatorProjectWorkboardProfilePanel.php',
'PhabricatorProjectsCurtainExtension' => 'applications/project/engineextension/PhabricatorProjectsCurtainExtension.php',
'PhabricatorProjectsEditEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsEditEngineExtension.php',
'PhabricatorProjectsEditField' => 'applications/transactions/editfield/PhabricatorProjectsEditField.php',
'PhabricatorProjectsFulltextEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsFulltextEngineExtension.php',
@ -3320,6 +3324,7 @@ phutil_register_library_map(array(
'PhabricatorSubscriptionsAddSelfHeraldAction' => 'applications/subscriptions/herald/PhabricatorSubscriptionsAddSelfHeraldAction.php',
'PhabricatorSubscriptionsAddSubscribersHeraldAction' => 'applications/subscriptions/herald/PhabricatorSubscriptionsAddSubscribersHeraldAction.php',
'PhabricatorSubscriptionsApplication' => 'applications/subscriptions/application/PhabricatorSubscriptionsApplication.php',
'PhabricatorSubscriptionsCurtainExtension' => 'applications/subscriptions/engineextension/PhabricatorSubscriptionsCurtainExtension.php',
'PhabricatorSubscriptionsEditController' => 'applications/subscriptions/controller/PhabricatorSubscriptionsEditController.php',
'PhabricatorSubscriptionsEditEngineExtension' => 'applications/subscriptions/engineextension/PhabricatorSubscriptionsEditEngineExtension.php',
'PhabricatorSubscriptionsEditor' => 'applications/subscriptions/editor/PhabricatorSubscriptionsEditor.php',
@ -3388,6 +3393,7 @@ phutil_register_library_map(array(
'PhabricatorTokenUIEventListener' => 'applications/tokens/event/PhabricatorTokenUIEventListener.php',
'PhabricatorTokenizerEditField' => 'applications/transactions/editfield/PhabricatorTokenizerEditField.php',
'PhabricatorTokensApplication' => 'applications/tokens/application/PhabricatorTokensApplication.php',
'PhabricatorTokensCurtainExtension' => 'applications/tokens/engineextension/PhabricatorTokensCurtainExtension.php',
'PhabricatorTokensSettingsPanel' => 'applications/settings/panel/PhabricatorTokensSettingsPanel.php',
'PhabricatorTooltipUIExample' => 'applications/uiexample/examples/PhabricatorTooltipUIExample.php',
'PhabricatorTransactions' => 'applications/transactions/constants/PhabricatorTransactions.php',
@ -5751,6 +5757,9 @@ phutil_register_library_map(array(
'PHUIColorPalletteExample' => 'PhabricatorUIExample',
'PHUICrumbView' => 'AphrontView',
'PHUICrumbsView' => 'AphrontView',
'PHUICurtainExtension' => 'Phobject',
'PHUICurtainPanelView' => 'AphrontTagView',
'PHUICurtainView' => 'AphrontTagView',
'PHUIDiffInlineCommentDetailView' => 'PHUIDiffInlineCommentView',
'PHUIDiffInlineCommentEditView' => 'PHUIDiffInlineCommentView',
'PHUIDiffInlineCommentRowScaffold' => 'AphrontView',
@ -7503,6 +7512,7 @@ phutil_register_library_map(array(
'PhabricatorProjectWatcherListView' => 'PhabricatorProjectUserListView',
'PhabricatorProjectWorkboardBackgroundColor' => 'Phobject',
'PhabricatorProjectWorkboardProfilePanel' => 'PhabricatorProfilePanel',
'PhabricatorProjectsCurtainExtension' => 'PHUICurtainExtension',
'PhabricatorProjectsEditEngineExtension' => 'PhabricatorEditEngineExtension',
'PhabricatorProjectsEditField' => 'PhabricatorTokenizerEditField',
'PhabricatorProjectsFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension',
@ -7872,6 +7882,7 @@ phutil_register_library_map(array(
'PhabricatorSubscriptionsAddSelfHeraldAction' => 'PhabricatorSubscriptionsHeraldAction',
'PhabricatorSubscriptionsAddSubscribersHeraldAction' => 'PhabricatorSubscriptionsHeraldAction',
'PhabricatorSubscriptionsApplication' => 'PhabricatorApplication',
'PhabricatorSubscriptionsCurtainExtension' => 'PHUICurtainExtension',
'PhabricatorSubscriptionsEditController' => 'PhabricatorController',
'PhabricatorSubscriptionsEditEngineExtension' => 'PhabricatorEditEngineExtension',
'PhabricatorSubscriptionsEditor' => 'PhabricatorEditor',
@ -7945,6 +7956,7 @@ phutil_register_library_map(array(
'PhabricatorTokenUIEventListener' => 'PhabricatorEventListener',
'PhabricatorTokenizerEditField' => 'PhabricatorPHIDListEditField',
'PhabricatorTokensApplication' => 'PhabricatorApplication',
'PhabricatorTokensCurtainExtension' => 'PHUICurtainExtension',
'PhabricatorTokensSettingsPanel' => 'PhabricatorSettingsPanel',
'PhabricatorTooltipUIExample' => 'PhabricatorUIExample',
'PhabricatorTransactions' => 'Phobject',

View file

@ -468,7 +468,26 @@ abstract class PhabricatorController extends AphrontController {
public function newApplicationMenu() {
return id(new PHUIApplicationMenuView())
->setViewer($this->getRequest()->getUser());
->setViewer($this->getViewer());
}
public function newCurtainView($object) {
$viewer = $this->getViewer();
$action_list = id(new PhabricatorActionListView())
->setViewer($viewer)
->setObject($object);
$curtain = id(new PHUICurtainView())
->setViewer($viewer)
->setActionList($action_list);
$panels = PHUICurtainExtension::buildExtensionPanels($viewer, $object);
foreach ($panels as $panel) {
$curtain->addPanel($panel);
}
return $curtain;
}
protected function buildTransactionTimeline(

View file

@ -40,15 +40,9 @@ final class PhabricatorPasteViewController extends PhabricatorPasteController {
return new Aphront404Response();
}
$forks = id(new PhabricatorPasteQuery())
->setViewer($viewer)
->withParentPHIDs(array($paste->getPHID()))
->execute();
$fork_phids = mpull($forks, 'getPHID');
$header = $this->buildHeaderView($paste);
$actions = $this->buildActionView($viewer, $paste);
$properties = $this->buildPropertyView($paste, $fork_phids);
$curtain = $this->buildCurtain($paste);
$subheader = $this->buildSubheaderView($paste);
$source_code = $this->buildSourceCodeView($paste, $this->highlightMap);
@ -78,8 +72,7 @@ final class PhabricatorPasteViewController extends PhabricatorPasteController {
$timeline,
$comment_view,
))
->setPropertyList($properties)
->setActionList($actions)
->setCurtain($curtain)
->addClass('ponder-question-view');
return $this->newPage()
@ -116,9 +109,9 @@ final class PhabricatorPasteViewController extends PhabricatorPasteController {
return $header;
}
private function buildActionView(
PhabricatorUser $viewer,
PhabricatorPaste $paste) {
private function buildCurtain(PhabricatorPaste $paste) {
$viewer = $this->getViewer();
$curtain = $this->newCurtainView($paste);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
@ -126,43 +119,42 @@ final class PhabricatorPasteViewController extends PhabricatorPasteController {
PhabricatorPolicyCapability::CAN_EDIT);
$id = $paste->getID();
$edit_uri = $this->getApplicationURI("edit/{$id}/");
$archive_uri = $this->getApplicationURI("archive/{$id}/");
$raw_uri = $this->getApplicationURI("raw/{$id}/");
$action_list = id(new PhabricatorActionListView())
->setUser($viewer)
->setObject($paste);
$action_list->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Paste'))
->setIcon('fa-pencil')
->setDisabled(!$can_edit)
->setHref($this->getApplicationURI("edit/{$id}/")));
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Paste'))
->setIcon('fa-pencil')
->setDisabled(!$can_edit)
->setHref($edit_uri));
if ($paste->isArchived()) {
$action_list->addAction(
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Activate Paste'))
->setIcon('fa-check')
->setDisabled(!$can_edit)
->setWorkflow($can_edit)
->setHref($this->getApplicationURI("archive/{$id}/")));
->setName(pht('Activate Paste'))
->setIcon('fa-check')
->setDisabled(!$can_edit)
->setWorkflow($can_edit)
->setHref($archive_uri));
} else {
$action_list->addAction(
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Archive Paste'))
->setIcon('fa-ban')
->setDisabled(!$can_edit)
->setWorkflow($can_edit)
->setHref($this->getApplicationURI("archive/{$id}/")));
->setName(pht('Archive Paste'))
->setIcon('fa-ban')
->setDisabled(!$can_edit)
->setWorkflow($can_edit)
->setHref($archive_uri));
}
$action_list->addAction(
id(new PhabricatorActionView())
->setName(pht('View Raw File'))
->setIcon('fa-file-text-o')
->setHref($this->getApplicationURI("raw/{$id}/")));
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('View Raw File'))
->setIcon('fa-file-text-o')
->setHref($raw_uri));
return $action_list;
return $curtain;
}
@ -191,32 +183,4 @@ final class PhabricatorPasteViewController extends PhabricatorPasteController {
->setContent($content);
}
private function buildPropertyView(
PhabricatorPaste $paste,
array $child_phids) {
$viewer = $this->getViewer();
$properties = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($paste);
if ($paste->getParentPHID()) {
$properties->addProperty(
pht('Forked From'),
$viewer->renderHandle($paste->getParentPHID()));
}
if ($child_phids) {
$properties->addProperty(
pht('Forks'),
$viewer->renderHandleList($child_phids));
}
$descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions(
$viewer,
$paste);
return $properties;
}
}

View file

@ -0,0 +1,91 @@
<?php
final class PhabricatorProjectsCurtainExtension
extends PHUICurtainExtension {
const EXTENSIONKEY = 'projects.projects';
public function shouldEnableForObject($object) {
return ($object instanceof PhabricatorProjectInterface);
}
public function getExtensionApplication() {
return new PhabricatorProjectApplication();
}
public function buildCurtainPanel($object) {
$viewer = $this->getViewer();
$project_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
$object->getPHID(),
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
$has_projects = (bool)$project_phids;
$project_phids = array_reverse($project_phids);
$handles = $viewer->loadHandles($project_phids);
// If this object can appear on boards, build the workboard annotations.
// Some day, this might be a generic interface. For now, only tasks can
// appear on boards.
$can_appear_on_boards = ($object instanceof ManiphestTask);
$annotations = array();
if ($has_projects && $can_appear_on_boards) {
$engine = id(new PhabricatorBoardLayoutEngine())
->setViewer($viewer)
->setBoardPHIDs($project_phids)
->setObjectPHIDs(array($object->getPHID()))
->executeLayout();
// TDOO: Generalize this UI and move it out of Maniphest.
require_celerity_resource('maniphest-task-summary-css');
foreach ($project_phids as $project_phid) {
$handle = $handles[$project_phid];
$columns = $engine->getObjectColumns(
$project_phid,
$object->getPHID());
$annotation = array();
foreach ($columns as $column) {
$project_id = $column->getProject()->getID();
$column_name = pht('(%s)', $column->getDisplayName());
$column_link = phutil_tag(
'a',
array(
'href' => "/project/board/{$project_id}/",
'class' => 'maniphest-board-link',
),
$column_name);
$annotation[] = $column_link;
}
if ($annotation) {
$annotations[$project_phid] = array(
' ',
phutil_implode_html(', ', $annotation),
);
}
}
}
if ($has_projects) {
$list = id(new PHUIHandleTagListView())
->setHandles($handles)
->setAnnotations($annotations)
->setShowHovercards(true);
} else {
$list = phutil_tag('em', array(), pht('None'));
}
return $this->newPanel()
->setHeaderText(pht('Projects'))
->setOrder(10000)
->appendChild($list);
}
}

View file

@ -0,0 +1,39 @@
<?php
final class PhabricatorSubscriptionsCurtainExtension
extends PHUICurtainExtension {
const EXTENSIONKEY = 'subscriptions.subscribers';
public function shouldEnableForObject($object) {
return ($object instanceof PhabricatorSubscribableInterface);
}
public function getExtensionApplication() {
return new PhabricatorSubscriptionsApplication();
}
public function buildCurtainPanel($object) {
$viewer = $this->getViewer();
$object_phid = $object->getPHID();
$subscriber_phids = PhabricatorSubscribersQuery::loadSubscribersForPHID(
$object_phid);
$handles = $viewer->loadHandles($subscriber_phids);
// TODO: This class can't accept a HandleList yet.
$handles = iterator_to_array($handles);
$susbscribers_view = id(new SubscriptionListStringBuilder())
->setObjectPHID($object_phid)
->setHandles($handles)
->buildPropertyString();
return $this->newPanel()
->setHeaderText(pht('Subscribers'))
->setOrder(20000)
->appendChild($susbscribers_view);
}
}

View file

@ -0,0 +1,67 @@
<?php
final class PhabricatorTokensCurtainExtension
extends PHUICurtainExtension {
const EXTENSIONKEY = 'tokens.tokens';
public function shouldEnableForObject($object) {
return ($object instanceof PhabricatorTokenReceiverInterface);
}
public function getExtensionApplication() {
return new PhabricatorTokensApplication();
}
public function buildCurtainPanel($object) {
$viewer = $this->getViewer();
$tokens_given = id(new PhabricatorTokenGivenQuery())
->setViewer($viewer)
->withObjectPHIDs(array($object->getPHID()))
->execute();
if (!$tokens_given) {
return null;
}
$author_phids = mpull($tokens_given, 'getAuthorPHID');
$handles = $viewer->loadHandles($author_phids);
Javelin::initBehavior('phabricator-tooltips');
$list = array();
foreach ($tokens_given as $token_given) {
$token = $token_given->getToken();
$aural = javelin_tag(
'span',
array(
'aural' => true,
),
pht(
'"%s" token, awarded by %s.',
$token->getName(),
$handles[$token_given->getAuthorPHID()]->getName()));
$list[] = javelin_tag(
'span',
array(
'sigil' => 'has-tooltip',
'class' => 'token-icon',
'meta' => array(
'tip' => $handles[$token_given->getAuthorPHID()]->getName(),
),
),
array(
$aural,
$token->renderIcon(),
));
}
return $this->newPanel()
->setHeaderText(pht('Tokens'))
->setOrder(30000)
->appendChild($list);
}
}

View file

@ -58,10 +58,12 @@ final class PhabricatorTokenGivenQuery
}
protected function willFilterPage(array $results) {
$viewer = $this->getViewer();
$object_phids = mpull($results, 'getObjectPHID');
$objects = id(new PhabricatorObjectQuery())
->setViewer($this->getViewer())
->setViewer($viewer)
->withPHIDs($object_phids)
->execute();
$objects = mpull($objects, null, 'getPHID');
@ -80,6 +82,31 @@ final class PhabricatorTokenGivenQuery
unset($results[$key]);
}
if (!$results) {
return $results;
}
$token_phids = mpull($results, 'getTokenPHID');
$tokens = id(new PhabricatorTokenQuery())
->setViewer($viewer)
->withPHIDs($token_phids)
->execute();
$tokens = mpull($tokens, null, 'getPHID');
foreach ($results as $key => $result) {
$token_phid = $result->getTokenPHID();
$token = idx($tokens, $token_phid);
if (!$token) {
$this->didRejectResult($result);
unset($results[$key]);
continue;
}
$result->attachToken($token);
}
return $results;
}

View file

@ -8,6 +8,7 @@ final class PhabricatorTokenGiven extends PhabricatorTokenDAO
protected $tokenPHID;
private $object = self::ATTACHABLE;
private $token = self::ATTACHABLE;
protected function getConfiguration() {
return array(
@ -35,6 +36,15 @@ final class PhabricatorTokenGiven extends PhabricatorTokenDAO
return $this->assertAttached($this->object);
}
public function attachToken(PhabricatorToken $token) {
$this->token = $token;
return $this;
}
public function getToken() {
return $this->assertAttached($this->token);
}
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,

View file

@ -0,0 +1,124 @@
<?php
abstract class PHUICurtainExtension extends Phobject {
private $viewer;
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
}
public function getViewer() {
return $this->viewer;
}
abstract public function shouldEnableForObject($object);
abstract public function getExtensionApplication();
public function buildCurtainPanels($object) {
$panel = $this->buildCurtainPanel($object);
if ($panel !== null) {
return array($panel);
}
return array();
}
public function buildCurtainPanel($object) {
throw new PhutilMethodNotImplementedException();
}
final public function getExtensionKey() {
return $this->getPhobjectClassConstant('EXTENSIONKEY');
}
final public static function getAllExtensions() {
return id(new PhutilClassMapQuery())
->setAncestorClass(__CLASS__)
->setUniqueMethod('getExtensionKey')
->execute();
}
protected function newPanel() {
return new PHUICurtainPanelView();
}
final public static function buildExtensionPanels(
PhabricatorUser $viewer,
$object) {
$extensions = self::getAllExtensions();
foreach ($extensions as $extension) {
$extension->setViewer($viewer);
}
foreach ($extensions as $key => $extension) {
$application = $extension->getExtensionApplication();
if (!($application instanceof PhabricatorApplication)) {
throw new Exception(
pht(
'Curtain extension ("%s", of class "%s") did not return an '.
'application from method "%s". This method must return an '.
'object of class "%s".',
$key,
get_class($extension),
'getExtensionApplication()',
'PhabricatorApplication'));
}
$has_application = PhabricatorApplication::isClassInstalledForViewer(
get_class($application),
$viewer);
if (!$has_application) {
unset($extensions[$key]);
}
}
foreach ($extensions as $key => $extension) {
if (!$extension->shouldEnableForObject($object)) {
unset($extensions[$key]);
}
}
$result = array();
foreach ($extensions as $key => $extension) {
$panels = $extension->buildCurtainPanels($object);
if (!is_array($panels)) {
throw new Exception(
pht(
'Curtain extension ("%s", of class "%s") did not return a list of '.
'curtain panels from method "%s". This method must return an '.
'array, and each value in the array must be a "%s" object.',
$key,
get_class($extension),
'buildCurtainPanels()',
'PHUICurtainPanelView'));
}
foreach ($panels as $panel_key => $panel) {
if (!($panel instanceof PHUICurtainPanelView)) {
throw new Exception(
pht(
'Curtain extension ("%s", of class "%s") returned a list of '.
'curtain panels from "%s" that contains an invalid value: '.
'a value (with key "%s") is not an object of class "%s". '.
'Each item in the returned array must be a panel.',
$key,
get_class($extension),
'buildCurtainPanels()',
$panel_key,
'PHUICurtainPanelView'));
}
$result[] = $panel;
}
}
return $result;
}
}

View file

@ -0,0 +1,63 @@
<?php
final class PHUICurtainPanelView extends AphrontTagView {
private $order = 0;
private $headerText;
public function setHeaderText($header_text) {
$this->headerText = $header_text;
return $this;
}
public function getHeaderText() {
return $this->headerText;
}
public function setOrder($order) {
$this->order = $order;
return $this;
}
public function getOrder() {
return $this->order;
}
public function getOrderVector() {
return id(new PhutilSortVector())
->addInt($this->getOrder());
}
protected function getTagAttributes() {
return array(
'class' => 'phui-curtain-panel',
);
}
protected function getTagContent() {
$header = null;
$header_text = $this->getHeaderText();
if (strlen($header_text)) {
$header = phutil_tag(
'div',
array(
'class' => 'phui-curtain-panel-header',
),
$header_text);
}
$body = phutil_tag(
'div',
array(
'class' => 'phui-curtain-panel-body',
),
$this->renderChildren());
return array(
$header,
$body,
);
}
}

View file

@ -0,0 +1,52 @@
<?php
final class PHUICurtainView extends AphrontTagView {
private $actionList;
private $panels = array();
public function addAction(PhabricatorActionView $action) {
$this->getActionList()->addAction($action);
return $this;
}
public function addPanel(PHUICurtainPanelView $curtain_panel) {
$this->panels[] = $curtain_panel;
return $this;
}
public function setActionList(PhabricatorActionListView $action_list) {
$this->actionList = $action_list;
return $this;
}
public function getActionList() {
return $this->actionList;
}
protected function canAppendChild() {
return false;
}
protected function getTagContent() {
$action_list = $this->actionList;
require_celerity_resource('phui-curtain-view-css');
$panels = $this->renderPanels();
return id(new PHUIObjectBoxView())
->appendChild($action_list)
->appendChild($panels)
->addClass('phui-two-column-properties');
}
private function renderPanels() {
$panels = $this->panels;
$panels = msortv($panels, 'getOrderVector');
return $panels;
}
}

View file

@ -11,6 +11,7 @@ final class PHUITwoColumnView extends AphrontTagView {
private $propertySection = array();
private $actionList;
private $propertyList;
private $curtain;
const DISPLAY_LEFT = 'phui-side-column-left';
const DISPLAY_RIGHT = 'phui-side-column-right';
@ -50,6 +51,15 @@ final class PHUITwoColumnView extends AphrontTagView {
return $this;
}
public function setCurtain(PHUICurtainView $curtain) {
$this->curtain = $curtain;
return $this;
}
public function getCurtain() {
return $this->curtain;
}
public function setFluid($fluid) {
$this->fluid = $fluid;
return $this;
@ -98,9 +108,17 @@ final class PHUITwoColumnView extends AphrontTagView {
$header = null;
if ($this->header) {
if ($this->actionList) {
$this->header->setActionList($this->actionList);
$curtain = $this->getCurtain();
if ($curtain) {
$action_list = $curtain->getActionList();
} else {
$action_list = $this->actionList;
}
if ($action_list) {
$this->header->setActionList($action_list);
}
$header = phutil_tag_div(
'phui-two-column-header', $this->header);
}
@ -166,6 +184,8 @@ final class PHUITwoColumnView extends AphrontTagView {
->addClass('phui-two-column-properties');
}
$curtain = $this->getCurtain();
return phutil_tag(
'div',
array(
@ -173,6 +193,7 @@ final class PHUITwoColumnView extends AphrontTagView {
),
array(
$properties,
$curtain,
$this->sideColumn,
));
}

View file

@ -0,0 +1,22 @@
/**
* @provides phui-curtain-view-css
*/
.phui-curtain-panel {
margin: 4px;
padding: 4px 0;
}
.device-desktop .phui-curtain-panel {
border-top: 1px solid {$lightblueborder};
}
.phui-curtain-panel-header {
padding: 4px 0 0;
color: {$bluetext};
font-weight: bold;
}
.phui-curtain-panel-body {
padding: 4px 0 0;
}