Page MenuHomePhabricator

D15414.diff
No OneTemporary

D15414.diff

diff --git a/resources/celerity/map.php b/resources/celerity/map.php
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -127,6 +127,7 @@
'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 @@
'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',
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1505,6 +1505,9 @@
'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 @@
'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 @@
'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 @@
'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 @@
'PHUIColorPalletteExample' => 'PhabricatorUIExample',
'PHUICrumbView' => 'AphrontView',
'PHUICrumbsView' => 'AphrontView',
+ 'PHUICurtainExtension' => 'Phobject',
+ 'PHUICurtainPanelView' => 'AphrontTagView',
+ 'PHUICurtainView' => 'AphrontTagView',
'PHUIDiffInlineCommentDetailView' => 'PHUIDiffInlineCommentView',
'PHUIDiffInlineCommentEditView' => 'PHUIDiffInlineCommentView',
'PHUIDiffInlineCommentRowScaffold' => 'AphrontView',
@@ -7503,6 +7512,7 @@
'PhabricatorProjectWatcherListView' => 'PhabricatorProjectUserListView',
'PhabricatorProjectWorkboardBackgroundColor' => 'Phobject',
'PhabricatorProjectWorkboardProfilePanel' => 'PhabricatorProfilePanel',
+ 'PhabricatorProjectsCurtainExtension' => 'PHUICurtainExtension',
'PhabricatorProjectsEditEngineExtension' => 'PhabricatorEditEngineExtension',
'PhabricatorProjectsEditField' => 'PhabricatorTokenizerEditField',
'PhabricatorProjectsFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension',
@@ -7872,6 +7882,7 @@
'PhabricatorSubscriptionsAddSelfHeraldAction' => 'PhabricatorSubscriptionsHeraldAction',
'PhabricatorSubscriptionsAddSubscribersHeraldAction' => 'PhabricatorSubscriptionsHeraldAction',
'PhabricatorSubscriptionsApplication' => 'PhabricatorApplication',
+ 'PhabricatorSubscriptionsCurtainExtension' => 'PHUICurtainExtension',
'PhabricatorSubscriptionsEditController' => 'PhabricatorController',
'PhabricatorSubscriptionsEditEngineExtension' => 'PhabricatorEditEngineExtension',
'PhabricatorSubscriptionsEditor' => 'PhabricatorEditor',
@@ -7945,6 +7956,7 @@
'PhabricatorTokenUIEventListener' => 'PhabricatorEventListener',
'PhabricatorTokenizerEditField' => 'PhabricatorPHIDListEditField',
'PhabricatorTokensApplication' => 'PhabricatorApplication',
+ 'PhabricatorTokensCurtainExtension' => 'PHUICurtainExtension',
'PhabricatorTokensSettingsPanel' => 'PhabricatorSettingsPanel',
'PhabricatorTooltipUIExample' => 'PhabricatorUIExample',
'PhabricatorTransactions' => 'Phobject',
diff --git a/src/applications/base/controller/PhabricatorController.php b/src/applications/base/controller/PhabricatorController.php
--- a/src/applications/base/controller/PhabricatorController.php
+++ b/src/applications/base/controller/PhabricatorController.php
@@ -468,7 +468,26 @@
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(
diff --git a/src/applications/paste/controller/PhabricatorPasteViewController.php b/src/applications/paste/controller/PhabricatorPasteViewController.php
--- a/src/applications/paste/controller/PhabricatorPasteViewController.php
+++ b/src/applications/paste/controller/PhabricatorPasteViewController.php
@@ -40,15 +40,9 @@
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 @@
$timeline,
$comment_view,
))
- ->setPropertyList($properties)
- ->setActionList($actions)
+ ->setCurtain($curtain)
->addClass('ponder-question-view');
return $this->newPage()
@@ -116,9 +109,9 @@
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 @@
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 @@
->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;
- }
-
}
diff --git a/src/applications/project/engineextension/PhabricatorProjectsCurtainExtension.php b/src/applications/project/engineextension/PhabricatorProjectsCurtainExtension.php
new file mode 100644
--- /dev/null
+++ b/src/applications/project/engineextension/PhabricatorProjectsCurtainExtension.php
@@ -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);
+ }
+
+}
diff --git a/src/applications/subscriptions/engineextension/PhabricatorSubscriptionsCurtainExtension.php b/src/applications/subscriptions/engineextension/PhabricatorSubscriptionsCurtainExtension.php
new file mode 100644
--- /dev/null
+++ b/src/applications/subscriptions/engineextension/PhabricatorSubscriptionsCurtainExtension.php
@@ -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);
+ }
+
+}
diff --git a/src/applications/tokens/engineextension/PhabricatorTokensCurtainExtension.php b/src/applications/tokens/engineextension/PhabricatorTokensCurtainExtension.php
new file mode 100644
--- /dev/null
+++ b/src/applications/tokens/engineextension/PhabricatorTokensCurtainExtension.php
@@ -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);
+ }
+
+}
diff --git a/src/applications/tokens/query/PhabricatorTokenGivenQuery.php b/src/applications/tokens/query/PhabricatorTokenGivenQuery.php
--- a/src/applications/tokens/query/PhabricatorTokenGivenQuery.php
+++ b/src/applications/tokens/query/PhabricatorTokenGivenQuery.php
@@ -58,10 +58,12 @@
}
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 @@
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;
}
diff --git a/src/applications/tokens/storage/PhabricatorTokenGiven.php b/src/applications/tokens/storage/PhabricatorTokenGiven.php
--- a/src/applications/tokens/storage/PhabricatorTokenGiven.php
+++ b/src/applications/tokens/storage/PhabricatorTokenGiven.php
@@ -8,6 +8,7 @@
protected $tokenPHID;
private $object = self::ATTACHABLE;
+ private $token = self::ATTACHABLE;
protected function getConfiguration() {
return array(
@@ -35,6 +36,15 @@
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,
diff --git a/src/view/extension/PHUICurtainExtension.php b/src/view/extension/PHUICurtainExtension.php
new file mode 100644
--- /dev/null
+++ b/src/view/extension/PHUICurtainExtension.php
@@ -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;
+ }
+
+}
diff --git a/src/view/layout/PHUICurtainPanelView.php b/src/view/layout/PHUICurtainPanelView.php
new file mode 100644
--- /dev/null
+++ b/src/view/layout/PHUICurtainPanelView.php
@@ -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,
+ );
+ }
+
+}
diff --git a/src/view/layout/PHUICurtainView.php b/src/view/layout/PHUICurtainView.php
new file mode 100644
--- /dev/null
+++ b/src/view/layout/PHUICurtainView.php
@@ -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;
+ }
+
+
+}
diff --git a/src/view/phui/PHUITwoColumnView.php b/src/view/phui/PHUITwoColumnView.php
--- a/src/view/phui/PHUITwoColumnView.php
+++ b/src/view/phui/PHUITwoColumnView.php
@@ -11,6 +11,7 @@
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 @@
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 @@
$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 @@
->addClass('phui-two-column-properties');
}
+ $curtain = $this->getCurtain();
+
return phutil_tag(
'div',
array(
@@ -173,6 +193,7 @@
),
array(
$properties,
+ $curtain,
$this->sideColumn,
));
}
diff --git a/webroot/rsrc/css/phui/phui-curtain-view.css b/webroot/rsrc/css/phui/phui-curtain-view.css
new file mode 100644
--- /dev/null
+++ b/webroot/rsrc/css/phui/phui-curtain-view.css
@@ -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;
+}

File Metadata

Mime Type
text/plain
Expires
Wed, Mar 19, 5:54 PM (3 d, 19 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7390652
Default Alt Text
D15414.diff (28 KB)

Event Timeline