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 @@ -2419,6 +2419,7 @@ 'PhabricatorLegalpadDocumentPHIDType' => 'applications/legalpad/phid/PhabricatorLegalpadDocumentPHIDType.php', 'PhabricatorLegalpadSignaturePolicyRule' => 'applications/legalpad/policyrule/PhabricatorLegalpadSignaturePolicyRule.php', 'PhabricatorLibraryTestCase' => '__tests__/PhabricatorLibraryTestCase.php', + 'PhabricatorLinkProfilePanel' => 'applications/search/profilepanel/PhabricatorLinkProfilePanel.php', 'PhabricatorLipsumArtist' => 'applications/lipsum/image/PhabricatorLipsumArtist.php', 'PhabricatorLipsumGenerateWorkflow' => 'applications/lipsum/management/PhabricatorLipsumGenerateWorkflow.php', 'PhabricatorLipsumManagementWorkflow' => 'applications/lipsum/management/PhabricatorLipsumManagementWorkflow.php', @@ -2826,6 +2827,13 @@ 'PhabricatorPolicyTestObject' => 'applications/policy/__tests__/PhabricatorPolicyTestObject.php', 'PhabricatorPolicyType' => 'applications/policy/constants/PhabricatorPolicyType.php', 'PhabricatorPonderApplication' => 'applications/ponder/application/PhabricatorPonderApplication.php', + 'PhabricatorProfilePanel' => 'applications/search/profilepanel/PhabricatorProfilePanel.php', + 'PhabricatorProfilePanelConfiguration' => 'applications/search/storage/PhabricatorProfilePanelConfiguration.php', + 'PhabricatorProfilePanelConfigurationQuery' => 'applications/search/query/PhabricatorProfilePanelConfigurationQuery.php', + 'PhabricatorProfilePanelConfigurationTransaction' => 'applications/search/storage/PhabricatorProfilePanelConfigurationTransaction.php', + 'PhabricatorProfilePanelEngine' => 'applications/search/engine/PhabricatorProfilePanelEngine.php', + 'PhabricatorProfilePanelInterface' => 'applications/search/interface/PhabricatorProfilePanelInterface.php', + 'PhabricatorProfilePanelPHIDType' => 'applications/search/phidtype/PhabricatorProfilePanelPHIDType.php', 'PhabricatorProject' => 'applications/project/storage/PhabricatorProject.php', 'PhabricatorProjectAddHeraldAction' => 'applications/project/herald/PhabricatorProjectAddHeraldAction.php', 'PhabricatorProjectApplication' => 'applications/project/application/PhabricatorProjectApplication.php', @@ -2856,6 +2864,7 @@ 'PhabricatorProjectDAO' => 'applications/project/storage/PhabricatorProjectDAO.php', 'PhabricatorProjectDatasource' => 'applications/project/typeahead/PhabricatorProjectDatasource.php', 'PhabricatorProjectDescriptionField' => 'applications/project/customfield/PhabricatorProjectDescriptionField.php', + 'PhabricatorProjectDetailsProfilePanel' => 'applications/project/profilepanel/PhabricatorProjectDetailsProfilePanel.php', 'PhabricatorProjectEditController' => 'applications/project/controller/PhabricatorProjectEditController.php', 'PhabricatorProjectEditEngine' => 'applications/project/engine/PhabricatorProjectEditEngine.php', 'PhabricatorProjectEditPictureController' => 'applications/project/controller/PhabricatorProjectEditPictureController.php', @@ -2877,6 +2886,7 @@ 'PhabricatorProjectMembersDatasource' => 'applications/project/typeahead/PhabricatorProjectMembersDatasource.php', 'PhabricatorProjectMembersEditController' => 'applications/project/controller/PhabricatorProjectMembersEditController.php', 'PhabricatorProjectMembersPolicyRule' => 'applications/project/policyrule/PhabricatorProjectMembersPolicyRule.php', + 'PhabricatorProjectMembersProfilePanel' => 'applications/project/profilepanel/PhabricatorProjectMembersProfilePanel.php', 'PhabricatorProjectMembersRemoveController' => 'applications/project/controller/PhabricatorProjectMembersRemoveController.php', 'PhabricatorProjectMilestonesController' => 'applications/project/controller/PhabricatorProjectMilestonesController.php', 'PhabricatorProjectMoveController' => 'applications/project/controller/PhabricatorProjectMoveController.php', @@ -2909,6 +2919,7 @@ 'PhabricatorProjectUserFunctionDatasource' => 'applications/project/typeahead/PhabricatorProjectUserFunctionDatasource.php', 'PhabricatorProjectViewController' => 'applications/project/controller/PhabricatorProjectViewController.php', 'PhabricatorProjectWatchController' => 'applications/project/controller/PhabricatorProjectWatchController.php', + 'PhabricatorProjectWorkboardProfilePanel' => 'applications/project/profilepanel/PhabricatorProjectWorkboardProfilePanel.php', 'PhabricatorProjectsEditEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsEditEngineExtension.php', 'PhabricatorProjectsEditField' => 'applications/transactions/editfield/PhabricatorProjectsEditField.php', 'PhabricatorProjectsFulltextEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsFulltextEngineExtension.php', @@ -6700,6 +6711,7 @@ 'PhabricatorLegalpadDocumentPHIDType' => 'PhabricatorPHIDType', 'PhabricatorLegalpadSignaturePolicyRule' => 'PhabricatorPolicyRule', 'PhabricatorLibraryTestCase' => 'PhutilLibraryTestCase', + 'PhabricatorLinkProfilePanel' => 'PhabricatorProfilePanel', 'PhabricatorLipsumArtist' => 'Phobject', 'PhabricatorLipsumGenerateWorkflow' => 'PhabricatorLipsumManagementWorkflow', 'PhabricatorLipsumManagementWorkflow' => 'PhabricatorManagementWorkflow', @@ -7171,6 +7183,16 @@ ), 'PhabricatorPolicyType' => 'PhabricatorPolicyConstants', 'PhabricatorPonderApplication' => 'PhabricatorApplication', + 'PhabricatorProfilePanel' => 'Phobject', + 'PhabricatorProfilePanelConfiguration' => array( + 'PhabricatorSearchDAO', + 'PhabricatorPolicyInterface', + 'PhabricatorExtendedPolicyInterface', + ), + 'PhabricatorProfilePanelConfigurationQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', + 'PhabricatorProfilePanelConfigurationTransaction' => 'PhabricatorApplicationTransaction', + 'PhabricatorProfilePanelEngine' => 'Phobject', + 'PhabricatorProfilePanelPHIDType' => 'PhabricatorPHIDType', 'PhabricatorProject' => array( 'PhabricatorProjectDAO', 'PhabricatorApplicationTransactionInterface', @@ -7182,6 +7204,7 @@ 'PhabricatorDestructibleInterface', 'PhabricatorFulltextInterface', 'PhabricatorConduitResultInterface', + 'PhabricatorProfilePanelInterface', ), 'PhabricatorProjectAddHeraldAction' => 'PhabricatorProjectHeraldAction', 'PhabricatorProjectApplication' => 'PhabricatorApplication', @@ -7223,6 +7246,7 @@ 'PhabricatorProjectDAO' => 'PhabricatorLiskDAO', 'PhabricatorProjectDatasource' => 'PhabricatorTypeaheadDatasource', 'PhabricatorProjectDescriptionField' => 'PhabricatorProjectStandardCustomField', + 'PhabricatorProjectDetailsProfilePanel' => 'PhabricatorProfilePanel', 'PhabricatorProjectEditController' => 'PhabricatorProjectController', 'PhabricatorProjectEditEngine' => 'PhabricatorEditEngine', 'PhabricatorProjectEditPictureController' => 'PhabricatorProjectController', @@ -7243,6 +7267,7 @@ 'PhabricatorProjectMembersDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 'PhabricatorProjectMembersEditController' => 'PhabricatorProjectController', 'PhabricatorProjectMembersPolicyRule' => 'PhabricatorPolicyRule', + 'PhabricatorProjectMembersProfilePanel' => 'PhabricatorProfilePanel', 'PhabricatorProjectMembersRemoveController' => 'PhabricatorProjectController', 'PhabricatorProjectMilestonesController' => 'PhabricatorProjectController', 'PhabricatorProjectMoveController' => 'PhabricatorProjectController', @@ -7278,6 +7303,7 @@ 'PhabricatorProjectUserFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 'PhabricatorProjectViewController' => 'PhabricatorProjectController', 'PhabricatorProjectWatchController' => 'PhabricatorProjectController', + 'PhabricatorProjectWorkboardProfilePanel' => 'PhabricatorProfilePanel', 'PhabricatorProjectsEditEngineExtension' => 'PhabricatorEditEngineExtension', 'PhabricatorProjectsEditField' => 'PhabricatorTokenizerEditField', 'PhabricatorProjectsFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension', diff --git a/src/applications/project/controller/PhabricatorProjectBoardController.php b/src/applications/project/controller/PhabricatorProjectBoardController.php --- a/src/applications/project/controller/PhabricatorProjectBoardController.php +++ b/src/applications/project/controller/PhabricatorProjectBoardController.php @@ -6,7 +6,7 @@ public function buildIconNavView(PhabricatorProject $project) { $id = $project->getID(); $nav = parent::buildIconNavView($project); - $nav->selectFilter("board/{$id}/"); + $nav->selectFilter(PhabricatorProject::PANEL_WORKBOARD); $nav->addClass('project-board-nav'); return $nav; } diff --git a/src/applications/project/controller/PhabricatorProjectController.php b/src/applications/project/controller/PhabricatorProjectController.php --- a/src/applications/project/controller/PhabricatorProjectController.php +++ b/src/applications/project/controller/PhabricatorProjectController.php @@ -86,6 +86,8 @@ public function buildSideNavView($for_app = false) { $project = $this->getProject(); + + $nav = new AphrontSideNavFilterView(); $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); @@ -115,65 +117,13 @@ } public function buildIconNavView(PhabricatorProject $project) { - $this->setProject($project); $viewer = $this->getViewer(); - $id = $project->getID(); - $picture = $project->getProfileImageURI(); - $name = $project->getName(); - $columns = id(new PhabricatorProjectColumnQuery()) + $engine = id(new PhabricatorProfilePanelEngine()) ->setViewer($viewer) - ->withProjectPHIDs(array($project->getPHID())) - ->execute(); - if ($columns) { - $board_icon = 'fa-columns'; - } else { - $board_icon = 'fa-columns grey'; - } - - $nav = new AphrontSideNavFilterView(); - $nav->setIconNav(true); - $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); - $nav->addIcon("profile/{$id}/", $name, null, $picture); - - $class = 'PhabricatorManiphestApplication'; - if (PhabricatorApplication::isClassInstalledForViewer($class, $viewer)) { - $phid = $project->getPHID(); - $nav->addIcon("board/{$id}/", pht('Workboard'), $board_icon); - $query_uri = urisprintf( - '/maniphest/?statuses=open()&projects=%s#R', - $phid); - $nav->addIcon(null, pht('Open Tasks'), 'fa-anchor', null, $query_uri); - } - - $nav->addIcon("feed/{$id}/", pht('Feed'), 'fa-newspaper-o'); - $nav->addIcon("members/{$id}/", pht('Members'), 'fa-group'); + ->setProfileObject($project); - if (false && PhabricatorEnv::getEnvConfig('phabricator.show-prototypes')) { - if ($project->supportsSubprojects()) { - $subprojects_icon = 'fa-sitemap'; - } else { - $subprojects_icon = 'fa-sitemap grey'; - } - - $key = PhabricatorProjectIconSet::getMilestoneIconKey(); - $milestones_icon = PhabricatorProjectIconSet::getIconIcon($key); - if (!$project->supportsMilestones()) { - $milestones_icon = "{$milestones_icon} grey"; - } - - $nav->addIcon( - "subprojects/{$id}/", - pht('Subprojects'), - $subprojects_icon); - - $nav->addIcon( - "milestones/{$id}/", - pht('Milestones'), - $milestones_icon); - } - - return $nav; + return $engine->buildNavigation(); } protected function buildApplicationCrumbs() { diff --git a/src/applications/project/controller/PhabricatorProjectEditPictureController.php b/src/applications/project/controller/PhabricatorProjectEditPictureController.php --- a/src/applications/project/controller/PhabricatorProjectEditPictureController.php +++ b/src/applications/project/controller/PhabricatorProjectEditPictureController.php @@ -281,7 +281,7 @@ ->setForm($upload_form); $nav = $this->buildIconNavView($project); - $nav->selectFilter("edit/{$id}/"); + $nav->selectFilter(PhabricatorProject::PANEL_PROFILE); $nav->appendChild($form_box); $nav->appendChild($upload_box); diff --git a/src/applications/project/controller/PhabricatorProjectFeedController.php b/src/applications/project/controller/PhabricatorProjectFeedController.php --- a/src/applications/project/controller/PhabricatorProjectFeedController.php +++ b/src/applications/project/controller/PhabricatorProjectFeedController.php @@ -34,7 +34,7 @@ ->appendChild($feed); $nav = $this->buildIconNavView($project); - $nav->selectFilter("feed/{$id}/"); + $nav->selectFilter('feed'); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Feed')); diff --git a/src/applications/project/controller/PhabricatorProjectMembersEditController.php b/src/applications/project/controller/PhabricatorProjectMembersEditController.php --- a/src/applications/project/controller/PhabricatorProjectMembersEditController.php +++ b/src/applications/project/controller/PhabricatorProjectMembersEditController.php @@ -96,7 +96,7 @@ $member_list = $this->renderMemberList($project, $handles); $nav = $this->buildIconNavView($project); - $nav->selectFilter("members/{$id}/"); + $nav->selectFilter(PhabricatorProject::PANEL_MEMBERS); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Members')); diff --git a/src/applications/project/controller/PhabricatorProjectMilestonesController.php b/src/applications/project/controller/PhabricatorProjectMilestonesController.php --- a/src/applications/project/controller/PhabricatorProjectMilestonesController.php +++ b/src/applications/project/controller/PhabricatorProjectMilestonesController.php @@ -77,7 +77,7 @@ ->renderList()); $nav = $this->buildIconNavView($project); - $nav->selectFilter("milestones/{$id}/"); + $nav->selectFilter(PhabricatorProject::PANEL_MILESTONES); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Milestones')); diff --git a/src/applications/project/controller/PhabricatorProjectProfileController.php b/src/applications/project/controller/PhabricatorProjectProfileController.php --- a/src/applications/project/controller/PhabricatorProjectProfileController.php +++ b/src/applications/project/controller/PhabricatorProjectProfileController.php @@ -44,7 +44,7 @@ $timeline->setShouldTerminate(true); $nav = $this->buildIconNavView($project); - $nav->selectFilter("profile/{$id}/"); + $nav->selectFilter(PhabricatorProject::PANEL_PROFILE); $crumbs = $this->buildApplicationCrumbs(); diff --git a/src/applications/project/controller/PhabricatorProjectSubprojectsController.php b/src/applications/project/controller/PhabricatorProjectSubprojectsController.php --- a/src/applications/project/controller/PhabricatorProjectSubprojectsController.php +++ b/src/applications/project/controller/PhabricatorProjectSubprojectsController.php @@ -76,7 +76,7 @@ ->renderList()); $nav = $this->buildIconNavView($project); - $nav->selectFilter("subprojects/{$id}/"); + $nav->selectFilter(PhabricatorProject::PANEL_SUBPROJECTS); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Subprojects')); diff --git a/src/applications/project/profilepanel/PhabricatorProjectDetailsProfilePanel.php b/src/applications/project/profilepanel/PhabricatorProjectDetailsProfilePanel.php new file mode 100644 --- /dev/null +++ b/src/applications/project/profilepanel/PhabricatorProjectDetailsProfilePanel.php @@ -0,0 +1,31 @@ +getProfileObject(); + + $id = $project->getID(); + $picture = $project->getProfileImageURI(); + $name = $project->getName(); + + $href = "/project/profile/{$id}/"; + + $item = id(new PHUIListItemView()) + ->setRenderNameAsTooltip(true) + ->setType(PHUIListItemView::TYPE_ICON_NAV) + ->setHref($href) + ->setName($name) + ->setProfileImage($picture); + + return array( + $item, + ); + } + +} diff --git a/src/applications/project/profilepanel/PhabricatorProjectMembersProfilePanel.php b/src/applications/project/profilepanel/PhabricatorProjectMembersProfilePanel.php new file mode 100644 --- /dev/null +++ b/src/applications/project/profilepanel/PhabricatorProjectMembersProfilePanel.php @@ -0,0 +1,31 @@ +getProfileObject(); + + $id = $project->getID(); + + $name = pht('Members'); + $icon = 'fa-group'; + $href = "/project/members/{$id}/"; + + $item = id(new PHUIListItemView()) + ->setRenderNameAsTooltip(true) + ->setType(PHUIListItemView::TYPE_ICON_NAV) + ->setHref($href) + ->setName($name) + ->setIcon($icon); + + return array( + $item, + ); + } + +} diff --git a/src/applications/project/profilepanel/PhabricatorProjectWorkboardProfilePanel.php b/src/applications/project/profilepanel/PhabricatorProjectWorkboardProfilePanel.php new file mode 100644 --- /dev/null +++ b/src/applications/project/profilepanel/PhabricatorProjectWorkboardProfilePanel.php @@ -0,0 +1,46 @@ +getViewer(); + + // Workboards are only available if Maniphest is installed. + $class = 'PhabricatorManiphestApplication'; + if (!PhabricatorApplication::isClassInstalledForViewer($class, $viewer)) { + return array(); + } + + $project = $config->getProfileObject(); + + $columns = id(new PhabricatorProjectColumnQuery()) + ->setViewer($viewer) + ->withProjectPHIDs(array($project->getPHID())) + ->execute(); + if ($columns) { + $icon = 'fa-columns'; + } else { + $icon = 'fa-columns grey'; + } + + $id = $project->getID(); + $href = "/project/board/{$id}/"; + $name = pht('Workboard'); + + $item = id(new PHUIListItemView()) + ->setRenderNameAsTooltip(true) + ->setType(PHUIListItemView::TYPE_ICON_NAV) + ->setHref($href) + ->setName($name) + ->setIcon($icon); + + return array( + $item, + ); + } + +} diff --git a/src/applications/project/storage/PhabricatorProject.php b/src/applications/project/storage/PhabricatorProject.php --- a/src/applications/project/storage/PhabricatorProject.php +++ b/src/applications/project/storage/PhabricatorProject.php @@ -10,7 +10,8 @@ PhabricatorCustomFieldInterface, PhabricatorDestructibleInterface, PhabricatorFulltextInterface, - PhabricatorConduitResultInterface { + PhabricatorConduitResultInterface, + PhabricatorProfilePanelInterface { protected $name; protected $status = PhabricatorProjectStatus::STATUS_ACTIVE; @@ -49,6 +50,12 @@ const TABLE_DATASOURCE_TOKEN = 'project_datasourcetoken'; + const PANEL_PROFILE = 'project.profile'; + const PANEL_WORKBOARD = 'project.workboard'; + const PANEL_MEMBERS = 'project.members'; + const PANEL_MILESTONES = 'project.milestones'; + const PANEL_SUBPROJECTS = 'project.subprojects'; + public static function initializeNewProject(PhabricatorUser $actor) { $app = id(new PhabricatorApplicationQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) @@ -644,4 +651,47 @@ return array(); } + +/* -( PhabricatorProfilePanelInterface )----------------------------------- */ + + + public function getBuiltinProfilePanels() { + $panels = array(); + + $panels[] = id(new PhabricatorProfilePanelConfiguration()) + ->setBuiltinKey(self::PANEL_PROFILE) + ->setPanelKey(PhabricatorProjectDetailsProfilePanel::PANELKEY); + + $panels[] = id(new PhabricatorProfilePanelConfiguration()) + ->setBuiltinKey(self::PANEL_WORKBOARD) + ->setPanelKey(PhabricatorProjectWorkboardProfilePanel::PANELKEY); + + // TODO: This is temporary. + $href = urisprintf( + '/maniphest/?statuses=open()&projects=%s#R', + $this->getPHID()); + + $panels[] = id(new PhabricatorProfilePanelConfiguration()) + ->setBuiltinKey('tasks') + ->setPanelKey(PhabricatorLinkProfilePanel::PANELKEY) + ->setPanelProperty('icon', 'fa-anchor') + ->setPanelProperty('name', pht('Open Tasks')) + ->setPanelProperty('href', $href); + + // TODO: This is temporary. + $id = $this->getID(); + $panels[] = id(new PhabricatorProfilePanelConfiguration()) + ->setBuiltinKey('feed') + ->setPanelKey(PhabricatorLinkProfilePanel::PANELKEY) + ->setPanelProperty('icon', 'fa-newspaper-o') + ->setPanelProperty('name', pht('Feed')) + ->setPanelProperty('href', "/project/feed/{$id}/"); + + $panels[] = id(new PhabricatorProfilePanelConfiguration()) + ->setBuiltinKey(self::PANEL_MEMBERS) + ->setPanelKey(PhabricatorProjectMembersProfilePanel::PANELKEY); + + return $panels; + } + } diff --git a/src/applications/search/engine/PhabricatorProfilePanelEngine.php b/src/applications/search/engine/PhabricatorProfilePanelEngine.php new file mode 100644 --- /dev/null +++ b/src/applications/search/engine/PhabricatorProfilePanelEngine.php @@ -0,0 +1,152 @@ +viewer = $viewer; + return $this; + } + + public function getViewer() { + return $this->viewer; + } + + public function setProfileObject( + PhabricatorProfilePanelInterface $profile_object) { + $this->profileObject = $profile_object; + return $this; + } + + public function getProfileObject() { + return $this->profileObject; + } + + public function buildNavigation() { + $nav = id(new AphrontSideNavFilterView()) + ->setIconNav(true) + ->setBaseURI(new PhutilURI('/project/')); + + $panels = $this->getPanels(); + + foreach ($panels as $panel) { + $items = $panel->buildNavigationMenuItems(); + foreach ($items as $item) { + $this->validateNavigationMenuItem($item); + } + + // If the panel produced only a single item which does not otherwise + // have a key, try to automatically assign it a reasonable key. This + // makes selecting the correct item simpler. + + if (count($items) == 1) { + $item = head($items); + if ($item->getKey() === null) { + $builtin_key = $panel->getBuiltinKey(); + $panel_phid = $panel->getPHID(); + if ($builtin_key !== null) { + $item->setKey($builtin_key); + } else if ($panel_phid !== null) { + $item->setKey($panel_phid); + } + } + } + + foreach ($items as $item) { + $nav->addMenuItem($item); + } + } + + $nav->selectFilter(null); + + return $nav; + } + + private function getPanels() { + if ($this->panels === null) { + $this->panels = $this->loadPanels(); + } + + return $this->panels; + } + + private function loadPanels() { + $viewer = $this->getViewer(); + + $panels = $this->loadBuiltinProfilePanels(); + + // TODO: Load persisted panels. + + foreach ($panels as $panel) { + $impl = $panel->getPanel(); + + $impl->setViewer($viewer); + } + + return $panels; + } + + private function loadBuiltinProfilePanels() { + $object = $this->getProfileObject(); + $builtins = $object->getBuiltinProfilePanels(); + + $panels = PhabricatorProfilePanel::getAllPanels(); + + $order = 1; + $map = array(); + foreach ($builtins as $builtin) { + $builtin_key = $builtin->getBuiltinKey(); + + if (!$builtin_key) { + throw new Exception( + pht( + 'Object produced a builtin panel with no builtin panel key! '. + 'Builtin panels must have a unique key.')); + } + + if (isset($map[$builtin_key])) { + throw new Exception( + pht( + 'Object produced two panels with the same builtin key ("%s"). '. + 'Each panel must have a unique builtin key.', + $builtin_key)); + } + + $panel_key = $builtin->getPanelKey(); + + $panel = idx($panels, $panel_key); + if (!$panel) { + throw new Exception( + pht( + 'Builtin panel ("%s") specifies a bad panel key ("%s"); there '. + 'is no corresponding panel implementation available.', + $builtin_key, + $panel_key)); + } + + $builtin + ->attachPanel($panel) + ->attachProfileObject($object) + ->setPanelOrder($order); + + $map[$builtin_key] = $builtin; + + $order++; + } + + return $map; + } + + private function validateNavigationMenuItem($item) { + if (!($item instanceof PHUIListItemView)) { + throw new Exception( + pht( + 'Expected buildNavigationMenuItems() to return a list of '. + 'PHUIListItemView objects, but got a surprise.')); + } + } + +} diff --git a/src/applications/search/interface/PhabricatorProfilePanelInterface.php b/src/applications/search/interface/PhabricatorProfilePanelInterface.php new file mode 100644 --- /dev/null +++ b/src/applications/search/interface/PhabricatorProfilePanelInterface.php @@ -0,0 +1,7 @@ +withPHIDs($phids); + } + + public function loadHandles( + PhabricatorHandleQuery $query, + array $handles, + array $objects) { + + foreach ($handles as $phid => $handle) { + $config = $objects[$phid]; + + $handle->setName(pht('Profile Panel')); + $handle->setURI($config->getURI()); + } + } + +} diff --git a/src/applications/search/profilepanel/PhabricatorLinkProfilePanel.php b/src/applications/search/profilepanel/PhabricatorLinkProfilePanel.php new file mode 100644 --- /dev/null +++ b/src/applications/search/profilepanel/PhabricatorLinkProfilePanel.php @@ -0,0 +1,27 @@ +getPanelProperty('icon'); + $name = $config->getPanelProperty('name'); + $href = $config->getPanelProperty('href'); + + $item = id(new PHUIListItemView()) + ->setRenderNameAsTooltip(true) + ->setType(PHUIListItemView::TYPE_ICON_NAV) + ->setHref($href) + ->setName($name) + ->setIcon($icon); + + return array( + $item, + ); + } + +} diff --git a/src/applications/search/profilepanel/PhabricatorProfilePanel.php b/src/applications/search/profilepanel/PhabricatorProfilePanel.php new file mode 100644 --- /dev/null +++ b/src/applications/search/profilepanel/PhabricatorProfilePanel.php @@ -0,0 +1,35 @@ +newNavigationMenuItems($config); + } + + abstract protected function newNavigationMenuItems( + PhabricatorProfilePanelConfiguration $config); + + public function setViewer(PhabricatorUser $viewer) { + $this->viewer = $viewer; + return $this; + } + + public function getViewer() { + return $this->viewer; + } + + final public function getPanelKey() { + return $this->getPhobjectClassConstant('PANELKEY'); + } + + final public static function getAllPanels() { + return id(new PhutilClassMapQuery()) + ->setAncestorClass(__CLASS__) + ->setUniqueMethod('getPanelKey') + ->execute(); + } + +} diff --git a/src/applications/search/query/PhabricatorProfilePanelConfigurationQuery.php b/src/applications/search/query/PhabricatorProfilePanelConfigurationQuery.php new file mode 100644 --- /dev/null +++ b/src/applications/search/query/PhabricatorProfilePanelConfigurationQuery.php @@ -0,0 +1,64 @@ +ids = $ids; + return $this; + } + + public function withPHIDs(array $phids) { + $this->phids = $phids; + return $this; + } + + public function withProfileObjectPHIDs(array $phids) { + $this->profileObjectPHIDs = $phids; + return $this; + } + + public function newResultObject() { + return new PhabricatorProfilePanelConfiguration(); + } + + protected function loadPage() { + return $this->loadStandardPage($this->newResultObject()); + } + + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { + $where = parent::buildWhereClauseParts($conn); + + if ($this->ids !== null) { + $where[] = qsprintf( + $conn, + 'id IN (%Ld)', + $this->ids); + } + + if ($this->phids !== null) { + $where[] = qsprintf( + $conn, + 'phid IN (%Ls)', + $this->phids); + } + + if ($this->profileObjectPHIDs !== null) { + $where[] = qsprintf( + $conn, + 'profileObjectPHID IN (%Ls)', + $this->profileObjectPHIDs); + } + + return $where; + } + + public function getQueryApplicationClass() { + return 'PhabricatorSearchApplication'; + } + +} diff --git a/src/applications/search/storage/PhabricatorProfilePanelConfiguration.php b/src/applications/search/storage/PhabricatorProfilePanelConfiguration.php new file mode 100644 --- /dev/null +++ b/src/applications/search/storage/PhabricatorProfilePanelConfiguration.php @@ -0,0 +1,124 @@ +setProfilePHID($profile_object->getPHID()) + ->setPanelKey($panel->getPanelKey()) + ->setIsDisabled(0) + ->attachPanel($panel) + ->attachProfileObject($profile_object); + } + + protected function getConfiguration() { + return array( + self::CONFIG_AUX_PHID => true, + self::CONFIG_SERIALIZATION => array( + 'panelProperties' => self::SERIALIZATION_JSON, + ), + self::CONFIG_COLUMN_SCHEMA => array( + 'panelKey' => 'text64', + 'builtinKey' => 'text64', + 'panelOrder' => 'uint32', + 'isDisabled' => 'bool', + ), + self::CONFIG_KEY_SCHEMA => array( + 'key_profile' => array( + 'columns' => array('profilePHID', 'panelOrder'), + ), + ), + ) + parent::getConfiguration(); + } + + public function attachPanel(PhabricatorProfilePanel $panel) { + $this->panel = $panel; + return $this; + } + + public function getPanel() { + return $this->assertAttached($this->panel); + } + + public function attachProfileObject( + PhabricatorProfilePanelInterface $profile_object) { + $this->profileObject = $profile_object; + return $this; + } + + public function getProfileObject() { + return $this->assertAttached($this->profileObject); + } + + public function buildNavigationMenuItems() { + return $this->getPanel()->buildNavigationMenuItems($this); + } + + public function setPanelProperty($key, $value) { + $this->panelProperties[$key] = $value; + return $this; + } + + public function getPanelProperty($key, $default = null) { + return idx($this->panelProperties, $key, $default); + } + + +/* -( PhabricatorPolicyInterface )----------------------------------------- */ + + + public function getCapabilities() { + return array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + ); + } + + + public function getPolicy($capability) { + return PhabricatorPolicies::getMostOpenPolicy(); + } + + + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { + return $this->getProfileObject()->hasAutomaticCapability( + $capability, + $viewer); + } + + + public function describeAutomaticCapability($capability) { + return null; + } + + +/* -( PhabricatorExtendedPolicyInterface )--------------------------------- */ + + + public function getExtendedPolicy($capability, PhabricatorUser $viewer) { + return array( + array( + $this->getProfileObject(), + $capability, + ), + ); + } + +} diff --git a/src/applications/search/storage/PhabricatorProfilePanelConfigurationTransaction.php b/src/applications/search/storage/PhabricatorProfilePanelConfigurationTransaction.php new file mode 100644 --- /dev/null +++ b/src/applications/search/storage/PhabricatorProfilePanelConfigurationTransaction.php @@ -0,0 +1,18 @@ +