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:
parent
aaab1011e5
commit
61f82bb97b
14 changed files with 586 additions and 73 deletions
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
124
src/view/extension/PHUICurtainExtension.php
Normal file
124
src/view/extension/PHUICurtainExtension.php
Normal 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;
|
||||
}
|
||||
|
||||
}
|
63
src/view/layout/PHUICurtainPanelView.php
Normal file
63
src/view/layout/PHUICurtainPanelView.php
Normal 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,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
52
src/view/layout/PHUICurtainView.php
Normal file
52
src/view/layout/PHUICurtainView.php
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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,
|
||||
));
|
||||
}
|
||||
|
|
22
webroot/rsrc/css/phui/phui-curtain-view.css
Normal file
22
webroot/rsrc/css/phui/phui-curtain-view.css
Normal 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;
|
||||
}
|
Loading…
Reference in a new issue