@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Begin modularizing profile panel/link construction

Summary: Ref T10054. This has no product impact, but prepares us for customizable side nav on "profiles" (today, projects; probably users some day; and maybe other stuff down the road).

Test Plan: Clicked all links on a profile, everything was exactly the same as before.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T10054

Differential Revision: https://secure.phabricator.com/D15007

+664 -63
+26
src/__phutil_library_map__.php
··· 2419 2419 'PhabricatorLegalpadDocumentPHIDType' => 'applications/legalpad/phid/PhabricatorLegalpadDocumentPHIDType.php', 2420 2420 'PhabricatorLegalpadSignaturePolicyRule' => 'applications/legalpad/policyrule/PhabricatorLegalpadSignaturePolicyRule.php', 2421 2421 'PhabricatorLibraryTestCase' => '__tests__/PhabricatorLibraryTestCase.php', 2422 + 'PhabricatorLinkProfilePanel' => 'applications/search/profilepanel/PhabricatorLinkProfilePanel.php', 2422 2423 'PhabricatorLipsumArtist' => 'applications/lipsum/image/PhabricatorLipsumArtist.php', 2423 2424 'PhabricatorLipsumGenerateWorkflow' => 'applications/lipsum/management/PhabricatorLipsumGenerateWorkflow.php', 2424 2425 'PhabricatorLipsumManagementWorkflow' => 'applications/lipsum/management/PhabricatorLipsumManagementWorkflow.php', ··· 2826 2827 'PhabricatorPolicyTestObject' => 'applications/policy/__tests__/PhabricatorPolicyTestObject.php', 2827 2828 'PhabricatorPolicyType' => 'applications/policy/constants/PhabricatorPolicyType.php', 2828 2829 'PhabricatorPonderApplication' => 'applications/ponder/application/PhabricatorPonderApplication.php', 2830 + 'PhabricatorProfilePanel' => 'applications/search/profilepanel/PhabricatorProfilePanel.php', 2831 + 'PhabricatorProfilePanelConfiguration' => 'applications/search/storage/PhabricatorProfilePanelConfiguration.php', 2832 + 'PhabricatorProfilePanelConfigurationQuery' => 'applications/search/query/PhabricatorProfilePanelConfigurationQuery.php', 2833 + 'PhabricatorProfilePanelConfigurationTransaction' => 'applications/search/storage/PhabricatorProfilePanelConfigurationTransaction.php', 2834 + 'PhabricatorProfilePanelEngine' => 'applications/search/engine/PhabricatorProfilePanelEngine.php', 2835 + 'PhabricatorProfilePanelInterface' => 'applications/search/interface/PhabricatorProfilePanelInterface.php', 2836 + 'PhabricatorProfilePanelPHIDType' => 'applications/search/phidtype/PhabricatorProfilePanelPHIDType.php', 2829 2837 'PhabricatorProject' => 'applications/project/storage/PhabricatorProject.php', 2830 2838 'PhabricatorProjectAddHeraldAction' => 'applications/project/herald/PhabricatorProjectAddHeraldAction.php', 2831 2839 'PhabricatorProjectApplication' => 'applications/project/application/PhabricatorProjectApplication.php', ··· 2856 2864 'PhabricatorProjectDAO' => 'applications/project/storage/PhabricatorProjectDAO.php', 2857 2865 'PhabricatorProjectDatasource' => 'applications/project/typeahead/PhabricatorProjectDatasource.php', 2858 2866 'PhabricatorProjectDescriptionField' => 'applications/project/customfield/PhabricatorProjectDescriptionField.php', 2867 + 'PhabricatorProjectDetailsProfilePanel' => 'applications/project/profilepanel/PhabricatorProjectDetailsProfilePanel.php', 2859 2868 'PhabricatorProjectEditController' => 'applications/project/controller/PhabricatorProjectEditController.php', 2860 2869 'PhabricatorProjectEditEngine' => 'applications/project/engine/PhabricatorProjectEditEngine.php', 2861 2870 'PhabricatorProjectEditPictureController' => 'applications/project/controller/PhabricatorProjectEditPictureController.php', ··· 2877 2886 'PhabricatorProjectMembersDatasource' => 'applications/project/typeahead/PhabricatorProjectMembersDatasource.php', 2878 2887 'PhabricatorProjectMembersEditController' => 'applications/project/controller/PhabricatorProjectMembersEditController.php', 2879 2888 'PhabricatorProjectMembersPolicyRule' => 'applications/project/policyrule/PhabricatorProjectMembersPolicyRule.php', 2889 + 'PhabricatorProjectMembersProfilePanel' => 'applications/project/profilepanel/PhabricatorProjectMembersProfilePanel.php', 2880 2890 'PhabricatorProjectMembersRemoveController' => 'applications/project/controller/PhabricatorProjectMembersRemoveController.php', 2881 2891 'PhabricatorProjectMilestonesController' => 'applications/project/controller/PhabricatorProjectMilestonesController.php', 2882 2892 'PhabricatorProjectMoveController' => 'applications/project/controller/PhabricatorProjectMoveController.php', ··· 2909 2919 'PhabricatorProjectUserFunctionDatasource' => 'applications/project/typeahead/PhabricatorProjectUserFunctionDatasource.php', 2910 2920 'PhabricatorProjectViewController' => 'applications/project/controller/PhabricatorProjectViewController.php', 2911 2921 'PhabricatorProjectWatchController' => 'applications/project/controller/PhabricatorProjectWatchController.php', 2922 + 'PhabricatorProjectWorkboardProfilePanel' => 'applications/project/profilepanel/PhabricatorProjectWorkboardProfilePanel.php', 2912 2923 'PhabricatorProjectsEditEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsEditEngineExtension.php', 2913 2924 'PhabricatorProjectsEditField' => 'applications/transactions/editfield/PhabricatorProjectsEditField.php', 2914 2925 'PhabricatorProjectsFulltextEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsFulltextEngineExtension.php', ··· 6700 6711 'PhabricatorLegalpadDocumentPHIDType' => 'PhabricatorPHIDType', 6701 6712 'PhabricatorLegalpadSignaturePolicyRule' => 'PhabricatorPolicyRule', 6702 6713 'PhabricatorLibraryTestCase' => 'PhutilLibraryTestCase', 6714 + 'PhabricatorLinkProfilePanel' => 'PhabricatorProfilePanel', 6703 6715 'PhabricatorLipsumArtist' => 'Phobject', 6704 6716 'PhabricatorLipsumGenerateWorkflow' => 'PhabricatorLipsumManagementWorkflow', 6705 6717 'PhabricatorLipsumManagementWorkflow' => 'PhabricatorManagementWorkflow', ··· 7171 7183 ), 7172 7184 'PhabricatorPolicyType' => 'PhabricatorPolicyConstants', 7173 7185 'PhabricatorPonderApplication' => 'PhabricatorApplication', 7186 + 'PhabricatorProfilePanel' => 'Phobject', 7187 + 'PhabricatorProfilePanelConfiguration' => array( 7188 + 'PhabricatorSearchDAO', 7189 + 'PhabricatorPolicyInterface', 7190 + 'PhabricatorExtendedPolicyInterface', 7191 + ), 7192 + 'PhabricatorProfilePanelConfigurationQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 7193 + 'PhabricatorProfilePanelConfigurationTransaction' => 'PhabricatorApplicationTransaction', 7194 + 'PhabricatorProfilePanelEngine' => 'Phobject', 7195 + 'PhabricatorProfilePanelPHIDType' => 'PhabricatorPHIDType', 7174 7196 'PhabricatorProject' => array( 7175 7197 'PhabricatorProjectDAO', 7176 7198 'PhabricatorApplicationTransactionInterface', ··· 7182 7204 'PhabricatorDestructibleInterface', 7183 7205 'PhabricatorFulltextInterface', 7184 7206 'PhabricatorConduitResultInterface', 7207 + 'PhabricatorProfilePanelInterface', 7185 7208 ), 7186 7209 'PhabricatorProjectAddHeraldAction' => 'PhabricatorProjectHeraldAction', 7187 7210 'PhabricatorProjectApplication' => 'PhabricatorApplication', ··· 7223 7246 'PhabricatorProjectDAO' => 'PhabricatorLiskDAO', 7224 7247 'PhabricatorProjectDatasource' => 'PhabricatorTypeaheadDatasource', 7225 7248 'PhabricatorProjectDescriptionField' => 'PhabricatorProjectStandardCustomField', 7249 + 'PhabricatorProjectDetailsProfilePanel' => 'PhabricatorProfilePanel', 7226 7250 'PhabricatorProjectEditController' => 'PhabricatorProjectController', 7227 7251 'PhabricatorProjectEditEngine' => 'PhabricatorEditEngine', 7228 7252 'PhabricatorProjectEditPictureController' => 'PhabricatorProjectController', ··· 7243 7267 'PhabricatorProjectMembersDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 7244 7268 'PhabricatorProjectMembersEditController' => 'PhabricatorProjectController', 7245 7269 'PhabricatorProjectMembersPolicyRule' => 'PhabricatorPolicyRule', 7270 + 'PhabricatorProjectMembersProfilePanel' => 'PhabricatorProfilePanel', 7246 7271 'PhabricatorProjectMembersRemoveController' => 'PhabricatorProjectController', 7247 7272 'PhabricatorProjectMilestonesController' => 'PhabricatorProjectController', 7248 7273 'PhabricatorProjectMoveController' => 'PhabricatorProjectController', ··· 7278 7303 'PhabricatorProjectUserFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 7279 7304 'PhabricatorProjectViewController' => 'PhabricatorProjectController', 7280 7305 'PhabricatorProjectWatchController' => 'PhabricatorProjectController', 7306 + 'PhabricatorProjectWorkboardProfilePanel' => 'PhabricatorProfilePanel', 7281 7307 'PhabricatorProjectsEditEngineExtension' => 'PhabricatorEditEngineExtension', 7282 7308 'PhabricatorProjectsEditField' => 'PhabricatorTokenizerEditField', 7283 7309 'PhabricatorProjectsFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension',
+1 -1
src/applications/project/controller/PhabricatorProjectBoardController.php
··· 6 6 public function buildIconNavView(PhabricatorProject $project) { 7 7 $id = $project->getID(); 8 8 $nav = parent::buildIconNavView($project); 9 - $nav->selectFilter("board/{$id}/"); 9 + $nav->selectFilter(PhabricatorProject::PANEL_WORKBOARD); 10 10 $nav->addClass('project-board-nav'); 11 11 return $nav; 12 12 }
+5 -55
src/applications/project/controller/PhabricatorProjectController.php
··· 86 86 public function buildSideNavView($for_app = false) { 87 87 $project = $this->getProject(); 88 88 89 + 90 + 89 91 $nav = new AphrontSideNavFilterView(); 90 92 $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); 91 93 ··· 115 117 } 116 118 117 119 public function buildIconNavView(PhabricatorProject $project) { 118 - $this->setProject($project); 119 120 $viewer = $this->getViewer(); 120 - $id = $project->getID(); 121 - $picture = $project->getProfileImageURI(); 122 - $name = $project->getName(); 123 121 124 - $columns = id(new PhabricatorProjectColumnQuery()) 122 + $engine = id(new PhabricatorProfilePanelEngine()) 125 123 ->setViewer($viewer) 126 - ->withProjectPHIDs(array($project->getPHID())) 127 - ->execute(); 128 - if ($columns) { 129 - $board_icon = 'fa-columns'; 130 - } else { 131 - $board_icon = 'fa-columns grey'; 132 - } 133 - 134 - $nav = new AphrontSideNavFilterView(); 135 - $nav->setIconNav(true); 136 - $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); 137 - $nav->addIcon("profile/{$id}/", $name, null, $picture); 138 - 139 - $class = 'PhabricatorManiphestApplication'; 140 - if (PhabricatorApplication::isClassInstalledForViewer($class, $viewer)) { 141 - $phid = $project->getPHID(); 142 - $nav->addIcon("board/{$id}/", pht('Workboard'), $board_icon); 143 - $query_uri = urisprintf( 144 - '/maniphest/?statuses=open()&projects=%s#R', 145 - $phid); 146 - $nav->addIcon(null, pht('Open Tasks'), 'fa-anchor', null, $query_uri); 147 - } 148 - 149 - $nav->addIcon("feed/{$id}/", pht('Feed'), 'fa-newspaper-o'); 150 - $nav->addIcon("members/{$id}/", pht('Members'), 'fa-group'); 151 - 152 - if (false && PhabricatorEnv::getEnvConfig('phabricator.show-prototypes')) { 153 - if ($project->supportsSubprojects()) { 154 - $subprojects_icon = 'fa-sitemap'; 155 - } else { 156 - $subprojects_icon = 'fa-sitemap grey'; 157 - } 158 - 159 - $key = PhabricatorProjectIconSet::getMilestoneIconKey(); 160 - $milestones_icon = PhabricatorProjectIconSet::getIconIcon($key); 161 - if (!$project->supportsMilestones()) { 162 - $milestones_icon = "{$milestones_icon} grey"; 163 - } 164 - 165 - $nav->addIcon( 166 - "subprojects/{$id}/", 167 - pht('Subprojects'), 168 - $subprojects_icon); 124 + ->setProfileObject($project); 169 125 170 - $nav->addIcon( 171 - "milestones/{$id}/", 172 - pht('Milestones'), 173 - $milestones_icon); 174 - } 175 - 176 - return $nav; 126 + return $engine->buildNavigation(); 177 127 } 178 128 179 129 protected function buildApplicationCrumbs() {
+1 -1
src/applications/project/controller/PhabricatorProjectEditPictureController.php
··· 281 281 ->setForm($upload_form); 282 282 283 283 $nav = $this->buildIconNavView($project); 284 - $nav->selectFilter("edit/{$id}/"); 284 + $nav->selectFilter(PhabricatorProject::PANEL_PROFILE); 285 285 $nav->appendChild($form_box); 286 286 $nav->appendChild($upload_box); 287 287
+1 -1
src/applications/project/controller/PhabricatorProjectFeedController.php
··· 34 34 ->appendChild($feed); 35 35 36 36 $nav = $this->buildIconNavView($project); 37 - $nav->selectFilter("feed/{$id}/"); 37 + $nav->selectFilter('feed'); 38 38 39 39 $crumbs = $this->buildApplicationCrumbs(); 40 40 $crumbs->addTextCrumb(pht('Feed'));
+1 -1
src/applications/project/controller/PhabricatorProjectMembersEditController.php
··· 96 96 $member_list = $this->renderMemberList($project, $handles); 97 97 98 98 $nav = $this->buildIconNavView($project); 99 - $nav->selectFilter("members/{$id}/"); 99 + $nav->selectFilter(PhabricatorProject::PANEL_MEMBERS); 100 100 101 101 $crumbs = $this->buildApplicationCrumbs(); 102 102 $crumbs->addTextCrumb(pht('Members'));
+1 -1
src/applications/project/controller/PhabricatorProjectMilestonesController.php
··· 77 77 ->renderList()); 78 78 79 79 $nav = $this->buildIconNavView($project); 80 - $nav->selectFilter("milestones/{$id}/"); 80 + $nav->selectFilter(PhabricatorProject::PANEL_MILESTONES); 81 81 82 82 $crumbs = $this->buildApplicationCrumbs(); 83 83 $crumbs->addTextCrumb(pht('Milestones'));
+1 -1
src/applications/project/controller/PhabricatorProjectProfileController.php
··· 44 44 $timeline->setShouldTerminate(true); 45 45 46 46 $nav = $this->buildIconNavView($project); 47 - $nav->selectFilter("profile/{$id}/"); 47 + $nav->selectFilter(PhabricatorProject::PANEL_PROFILE); 48 48 49 49 $crumbs = $this->buildApplicationCrumbs(); 50 50
+1 -1
src/applications/project/controller/PhabricatorProjectSubprojectsController.php
··· 76 76 ->renderList()); 77 77 78 78 $nav = $this->buildIconNavView($project); 79 - $nav->selectFilter("subprojects/{$id}/"); 79 + $nav->selectFilter(PhabricatorProject::PANEL_SUBPROJECTS); 80 80 81 81 $crumbs = $this->buildApplicationCrumbs(); 82 82 $crumbs->addTextCrumb(pht('Subprojects'));
+31
src/applications/project/profilepanel/PhabricatorProjectDetailsProfilePanel.php
··· 1 + <?php 2 + 3 + final class PhabricatorProjectDetailsProfilePanel 4 + extends PhabricatorProfilePanel { 5 + 6 + const PANELKEY = 'project.details'; 7 + 8 + protected function newNavigationMenuItems( 9 + PhabricatorProfilePanelConfiguration $config) { 10 + 11 + $project = $config->getProfileObject(); 12 + 13 + $id = $project->getID(); 14 + $picture = $project->getProfileImageURI(); 15 + $name = $project->getName(); 16 + 17 + $href = "/project/profile/{$id}/"; 18 + 19 + $item = id(new PHUIListItemView()) 20 + ->setRenderNameAsTooltip(true) 21 + ->setType(PHUIListItemView::TYPE_ICON_NAV) 22 + ->setHref($href) 23 + ->setName($name) 24 + ->setProfileImage($picture); 25 + 26 + return array( 27 + $item, 28 + ); 29 + } 30 + 31 + }
+31
src/applications/project/profilepanel/PhabricatorProjectMembersProfilePanel.php
··· 1 + <?php 2 + 3 + final class PhabricatorProjectMembersProfilePanel 4 + extends PhabricatorProfilePanel { 5 + 6 + const PANELKEY = 'project.members'; 7 + 8 + protected function newNavigationMenuItems( 9 + PhabricatorProfilePanelConfiguration $config) { 10 + 11 + $project = $config->getProfileObject(); 12 + 13 + $id = $project->getID(); 14 + 15 + $name = pht('Members'); 16 + $icon = 'fa-group'; 17 + $href = "/project/members/{$id}/"; 18 + 19 + $item = id(new PHUIListItemView()) 20 + ->setRenderNameAsTooltip(true) 21 + ->setType(PHUIListItemView::TYPE_ICON_NAV) 22 + ->setHref($href) 23 + ->setName($name) 24 + ->setIcon($icon); 25 + 26 + return array( 27 + $item, 28 + ); 29 + } 30 + 31 + }
+46
src/applications/project/profilepanel/PhabricatorProjectWorkboardProfilePanel.php
··· 1 + <?php 2 + 3 + final class PhabricatorProjectWorkboardProfilePanel 4 + extends PhabricatorProfilePanel { 5 + 6 + const PANELKEY = 'project.workboard'; 7 + 8 + protected function newNavigationMenuItems( 9 + PhabricatorProfilePanelConfiguration $config) { 10 + $viewer = $this->getViewer(); 11 + 12 + // Workboards are only available if Maniphest is installed. 13 + $class = 'PhabricatorManiphestApplication'; 14 + if (!PhabricatorApplication::isClassInstalledForViewer($class, $viewer)) { 15 + return array(); 16 + } 17 + 18 + $project = $config->getProfileObject(); 19 + 20 + $columns = id(new PhabricatorProjectColumnQuery()) 21 + ->setViewer($viewer) 22 + ->withProjectPHIDs(array($project->getPHID())) 23 + ->execute(); 24 + if ($columns) { 25 + $icon = 'fa-columns'; 26 + } else { 27 + $icon = 'fa-columns grey'; 28 + } 29 + 30 + $id = $project->getID(); 31 + $href = "/project/board/{$id}/"; 32 + $name = pht('Workboard'); 33 + 34 + $item = id(new PHUIListItemView()) 35 + ->setRenderNameAsTooltip(true) 36 + ->setType(PHUIListItemView::TYPE_ICON_NAV) 37 + ->setHref($href) 38 + ->setName($name) 39 + ->setIcon($icon); 40 + 41 + return array( 42 + $item, 43 + ); 44 + } 45 + 46 + }
+51 -1
src/applications/project/storage/PhabricatorProject.php
··· 10 10 PhabricatorCustomFieldInterface, 11 11 PhabricatorDestructibleInterface, 12 12 PhabricatorFulltextInterface, 13 - PhabricatorConduitResultInterface { 13 + PhabricatorConduitResultInterface, 14 + PhabricatorProfilePanelInterface { 14 15 15 16 protected $name; 16 17 protected $status = PhabricatorProjectStatus::STATUS_ACTIVE; ··· 48 49 const DEFAULT_COLOR = 'blue'; 49 50 50 51 const TABLE_DATASOURCE_TOKEN = 'project_datasourcetoken'; 52 + 53 + const PANEL_PROFILE = 'project.profile'; 54 + const PANEL_WORKBOARD = 'project.workboard'; 55 + const PANEL_MEMBERS = 'project.members'; 56 + const PANEL_MILESTONES = 'project.milestones'; 57 + const PANEL_SUBPROJECTS = 'project.subprojects'; 51 58 52 59 public static function initializeNewProject(PhabricatorUser $actor) { 53 60 $app = id(new PhabricatorApplicationQuery()) ··· 642 649 643 650 public function getConduitSearchAttachments() { 644 651 return array(); 652 + } 653 + 654 + 655 + /* -( PhabricatorProfilePanelInterface )----------------------------------- */ 656 + 657 + 658 + public function getBuiltinProfilePanels() { 659 + $panels = array(); 660 + 661 + $panels[] = id(new PhabricatorProfilePanelConfiguration()) 662 + ->setBuiltinKey(self::PANEL_PROFILE) 663 + ->setPanelKey(PhabricatorProjectDetailsProfilePanel::PANELKEY); 664 + 665 + $panels[] = id(new PhabricatorProfilePanelConfiguration()) 666 + ->setBuiltinKey(self::PANEL_WORKBOARD) 667 + ->setPanelKey(PhabricatorProjectWorkboardProfilePanel::PANELKEY); 668 + 669 + // TODO: This is temporary. 670 + $href = urisprintf( 671 + '/maniphest/?statuses=open()&projects=%s#R', 672 + $this->getPHID()); 673 + 674 + $panels[] = id(new PhabricatorProfilePanelConfiguration()) 675 + ->setBuiltinKey('tasks') 676 + ->setPanelKey(PhabricatorLinkProfilePanel::PANELKEY) 677 + ->setPanelProperty('icon', 'fa-anchor') 678 + ->setPanelProperty('name', pht('Open Tasks')) 679 + ->setPanelProperty('href', $href); 680 + 681 + // TODO: This is temporary. 682 + $id = $this->getID(); 683 + $panels[] = id(new PhabricatorProfilePanelConfiguration()) 684 + ->setBuiltinKey('feed') 685 + ->setPanelKey(PhabricatorLinkProfilePanel::PANELKEY) 686 + ->setPanelProperty('icon', 'fa-newspaper-o') 687 + ->setPanelProperty('name', pht('Feed')) 688 + ->setPanelProperty('href', "/project/feed/{$id}/"); 689 + 690 + $panels[] = id(new PhabricatorProfilePanelConfiguration()) 691 + ->setBuiltinKey(self::PANEL_MEMBERS) 692 + ->setPanelKey(PhabricatorProjectMembersProfilePanel::PANELKEY); 693 + 694 + return $panels; 645 695 } 646 696 647 697 }
+152
src/applications/search/engine/PhabricatorProfilePanelEngine.php
··· 1 + <?php 2 + 3 + final class PhabricatorProfilePanelEngine extends Phobject { 4 + 5 + private $viewer; 6 + private $profileObject; 7 + private $panels; 8 + 9 + public function setViewer(PhabricatorUser $viewer) { 10 + $this->viewer = $viewer; 11 + return $this; 12 + } 13 + 14 + public function getViewer() { 15 + return $this->viewer; 16 + } 17 + 18 + public function setProfileObject( 19 + PhabricatorProfilePanelInterface $profile_object) { 20 + $this->profileObject = $profile_object; 21 + return $this; 22 + } 23 + 24 + public function getProfileObject() { 25 + return $this->profileObject; 26 + } 27 + 28 + public function buildNavigation() { 29 + $nav = id(new AphrontSideNavFilterView()) 30 + ->setIconNav(true) 31 + ->setBaseURI(new PhutilURI('/project/')); 32 + 33 + $panels = $this->getPanels(); 34 + 35 + foreach ($panels as $panel) { 36 + $items = $panel->buildNavigationMenuItems(); 37 + foreach ($items as $item) { 38 + $this->validateNavigationMenuItem($item); 39 + } 40 + 41 + // If the panel produced only a single item which does not otherwise 42 + // have a key, try to automatically assign it a reasonable key. This 43 + // makes selecting the correct item simpler. 44 + 45 + if (count($items) == 1) { 46 + $item = head($items); 47 + if ($item->getKey() === null) { 48 + $builtin_key = $panel->getBuiltinKey(); 49 + $panel_phid = $panel->getPHID(); 50 + if ($builtin_key !== null) { 51 + $item->setKey($builtin_key); 52 + } else if ($panel_phid !== null) { 53 + $item->setKey($panel_phid); 54 + } 55 + } 56 + } 57 + 58 + foreach ($items as $item) { 59 + $nav->addMenuItem($item); 60 + } 61 + } 62 + 63 + $nav->selectFilter(null); 64 + 65 + return $nav; 66 + } 67 + 68 + private function getPanels() { 69 + if ($this->panels === null) { 70 + $this->panels = $this->loadPanels(); 71 + } 72 + 73 + return $this->panels; 74 + } 75 + 76 + private function loadPanels() { 77 + $viewer = $this->getViewer(); 78 + 79 + $panels = $this->loadBuiltinProfilePanels(); 80 + 81 + // TODO: Load persisted panels. 82 + 83 + foreach ($panels as $panel) { 84 + $impl = $panel->getPanel(); 85 + 86 + $impl->setViewer($viewer); 87 + } 88 + 89 + return $panels; 90 + } 91 + 92 + private function loadBuiltinProfilePanels() { 93 + $object = $this->getProfileObject(); 94 + $builtins = $object->getBuiltinProfilePanels(); 95 + 96 + $panels = PhabricatorProfilePanel::getAllPanels(); 97 + 98 + $order = 1; 99 + $map = array(); 100 + foreach ($builtins as $builtin) { 101 + $builtin_key = $builtin->getBuiltinKey(); 102 + 103 + if (!$builtin_key) { 104 + throw new Exception( 105 + pht( 106 + 'Object produced a builtin panel with no builtin panel key! '. 107 + 'Builtin panels must have a unique key.')); 108 + } 109 + 110 + if (isset($map[$builtin_key])) { 111 + throw new Exception( 112 + pht( 113 + 'Object produced two panels with the same builtin key ("%s"). '. 114 + 'Each panel must have a unique builtin key.', 115 + $builtin_key)); 116 + } 117 + 118 + $panel_key = $builtin->getPanelKey(); 119 + 120 + $panel = idx($panels, $panel_key); 121 + if (!$panel) { 122 + throw new Exception( 123 + pht( 124 + 'Builtin panel ("%s") specifies a bad panel key ("%s"); there '. 125 + 'is no corresponding panel implementation available.', 126 + $builtin_key, 127 + $panel_key)); 128 + } 129 + 130 + $builtin 131 + ->attachPanel($panel) 132 + ->attachProfileObject($object) 133 + ->setPanelOrder($order); 134 + 135 + $map[$builtin_key] = $builtin; 136 + 137 + $order++; 138 + } 139 + 140 + return $map; 141 + } 142 + 143 + private function validateNavigationMenuItem($item) { 144 + if (!($item instanceof PHUIListItemView)) { 145 + throw new Exception( 146 + pht( 147 + 'Expected buildNavigationMenuItems() to return a list of '. 148 + 'PHUIListItemView objects, but got a surprise.')); 149 + } 150 + } 151 + 152 + }
+7
src/applications/search/interface/PhabricatorProfilePanelInterface.php
··· 1 + <?php 2 + 3 + interface PhabricatorProfilePanelInterface { 4 + 5 + public function getBuiltinProfilePanels(); 6 + 7 + }
+40
src/applications/search/phidtype/PhabricatorProfilePanelPHIDType.php
··· 1 + <?php 2 + 3 + final class PhabricatorProfilePanelPHIDType 4 + extends PhabricatorPHIDType { 5 + 6 + const TYPECONST = 'PANL'; 7 + 8 + public function getTypeName() { 9 + return pht('Profile Panel'); 10 + } 11 + 12 + public function newObject() { 13 + return new PhabricatorProfilePanelConfiguration(); 14 + } 15 + 16 + public function getPHIDTypeApplicationClass() { 17 + return 'PhabricatorSearchApplication'; 18 + } 19 + 20 + protected function buildQueryForObjects( 21 + PhabricatorObjectQuery $object_query, 22 + array $phids) { 23 + return id(new PhabricatorProfilePanelConfigurationQuery()) 24 + ->withPHIDs($phids); 25 + } 26 + 27 + public function loadHandles( 28 + PhabricatorHandleQuery $query, 29 + array $handles, 30 + array $objects) { 31 + 32 + foreach ($handles as $phid => $handle) { 33 + $config = $objects[$phid]; 34 + 35 + $handle->setName(pht('Profile Panel')); 36 + $handle->setURI($config->getURI()); 37 + } 38 + } 39 + 40 + }
+27
src/applications/search/profilepanel/PhabricatorLinkProfilePanel.php
··· 1 + <?php 2 + 3 + final class PhabricatorLinkProfilePanel 4 + extends PhabricatorProfilePanel { 5 + 6 + const PANELKEY = 'link'; 7 + 8 + protected function newNavigationMenuItems( 9 + PhabricatorProfilePanelConfiguration $config) { 10 + 11 + $icon = $config->getPanelProperty('icon'); 12 + $name = $config->getPanelProperty('name'); 13 + $href = $config->getPanelProperty('href'); 14 + 15 + $item = id(new PHUIListItemView()) 16 + ->setRenderNameAsTooltip(true) 17 + ->setType(PHUIListItemView::TYPE_ICON_NAV) 18 + ->setHref($href) 19 + ->setName($name) 20 + ->setIcon($icon); 21 + 22 + return array( 23 + $item, 24 + ); 25 + } 26 + 27 + }
+35
src/applications/search/profilepanel/PhabricatorProfilePanel.php
··· 1 + <?php 2 + 3 + abstract class PhabricatorProfilePanel extends Phobject { 4 + 5 + private $viewer; 6 + 7 + final public function buildNavigationMenuItems( 8 + PhabricatorProfilePanelConfiguration $config) { 9 + return $this->newNavigationMenuItems($config); 10 + } 11 + 12 + abstract protected function newNavigationMenuItems( 13 + PhabricatorProfilePanelConfiguration $config); 14 + 15 + public function setViewer(PhabricatorUser $viewer) { 16 + $this->viewer = $viewer; 17 + return $this; 18 + } 19 + 20 + public function getViewer() { 21 + return $this->viewer; 22 + } 23 + 24 + final public function getPanelKey() { 25 + return $this->getPhobjectClassConstant('PANELKEY'); 26 + } 27 + 28 + final public static function getAllPanels() { 29 + return id(new PhutilClassMapQuery()) 30 + ->setAncestorClass(__CLASS__) 31 + ->setUniqueMethod('getPanelKey') 32 + ->execute(); 33 + } 34 + 35 + }
+64
src/applications/search/query/PhabricatorProfilePanelConfigurationQuery.php
··· 1 + <?php 2 + 3 + final class PhabricatorProfilePanelConfigurationQuery 4 + extends PhabricatorCursorPagedPolicyAwareQuery { 5 + 6 + private $ids; 7 + private $phids; 8 + private $profileObjectPHIDs; 9 + 10 + public function withIDs(array $ids) { 11 + $this->ids = $ids; 12 + return $this; 13 + } 14 + 15 + public function withPHIDs(array $phids) { 16 + $this->phids = $phids; 17 + return $this; 18 + } 19 + 20 + public function withProfileObjectPHIDs(array $phids) { 21 + $this->profileObjectPHIDs = $phids; 22 + return $this; 23 + } 24 + 25 + public function newResultObject() { 26 + return new PhabricatorProfilePanelConfiguration(); 27 + } 28 + 29 + protected function loadPage() { 30 + return $this->loadStandardPage($this->newResultObject()); 31 + } 32 + 33 + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { 34 + $where = parent::buildWhereClauseParts($conn); 35 + 36 + if ($this->ids !== null) { 37 + $where[] = qsprintf( 38 + $conn, 39 + 'id IN (%Ld)', 40 + $this->ids); 41 + } 42 + 43 + if ($this->phids !== null) { 44 + $where[] = qsprintf( 45 + $conn, 46 + 'phid IN (%Ls)', 47 + $this->phids); 48 + } 49 + 50 + if ($this->profileObjectPHIDs !== null) { 51 + $where[] = qsprintf( 52 + $conn, 53 + 'profileObjectPHID IN (%Ls)', 54 + $this->profileObjectPHIDs); 55 + } 56 + 57 + return $where; 58 + } 59 + 60 + public function getQueryApplicationClass() { 61 + return 'PhabricatorSearchApplication'; 62 + } 63 + 64 + }
+124
src/applications/search/storage/PhabricatorProfilePanelConfiguration.php
··· 1 + <?php 2 + 3 + final class PhabricatorProfilePanelConfiguration 4 + extends PhabricatorSearchDAO 5 + implements 6 + PhabricatorPolicyInterface, 7 + PhabricatorExtendedPolicyInterface { 8 + 9 + protected $profilePHID; 10 + protected $panelKey; 11 + protected $builtinKey; 12 + protected $panelOrder; 13 + protected $isDisabled; 14 + protected $panelProperties = array(); 15 + 16 + private $profileObject = self::ATTACHABLE; 17 + private $panel = self::ATTACHABLE; 18 + 19 + public static function initializeNewPanelConfiguration( 20 + PhabricatorProfilePanelInterface $profile_object, 21 + PhabricatorProfilePanel $panel) { 22 + 23 + return id(new self()) 24 + ->setProfilePHID($profile_object->getPHID()) 25 + ->setPanelKey($panel->getPanelKey()) 26 + ->setIsDisabled(0) 27 + ->attachPanel($panel) 28 + ->attachProfileObject($profile_object); 29 + } 30 + 31 + protected function getConfiguration() { 32 + return array( 33 + self::CONFIG_AUX_PHID => true, 34 + self::CONFIG_SERIALIZATION => array( 35 + 'panelProperties' => self::SERIALIZATION_JSON, 36 + ), 37 + self::CONFIG_COLUMN_SCHEMA => array( 38 + 'panelKey' => 'text64', 39 + 'builtinKey' => 'text64', 40 + 'panelOrder' => 'uint32', 41 + 'isDisabled' => 'bool', 42 + ), 43 + self::CONFIG_KEY_SCHEMA => array( 44 + 'key_profile' => array( 45 + 'columns' => array('profilePHID', 'panelOrder'), 46 + ), 47 + ), 48 + ) + parent::getConfiguration(); 49 + } 50 + 51 + public function attachPanel(PhabricatorProfilePanel $panel) { 52 + $this->panel = $panel; 53 + return $this; 54 + } 55 + 56 + public function getPanel() { 57 + return $this->assertAttached($this->panel); 58 + } 59 + 60 + public function attachProfileObject( 61 + PhabricatorProfilePanelInterface $profile_object) { 62 + $this->profileObject = $profile_object; 63 + return $this; 64 + } 65 + 66 + public function getProfileObject() { 67 + return $this->assertAttached($this->profileObject); 68 + } 69 + 70 + public function buildNavigationMenuItems() { 71 + return $this->getPanel()->buildNavigationMenuItems($this); 72 + } 73 + 74 + public function setPanelProperty($key, $value) { 75 + $this->panelProperties[$key] = $value; 76 + return $this; 77 + } 78 + 79 + public function getPanelProperty($key, $default = null) { 80 + return idx($this->panelProperties, $key, $default); 81 + } 82 + 83 + 84 + /* -( PhabricatorPolicyInterface )----------------------------------------- */ 85 + 86 + 87 + public function getCapabilities() { 88 + return array( 89 + PhabricatorPolicyCapability::CAN_VIEW, 90 + PhabricatorPolicyCapability::CAN_EDIT, 91 + ); 92 + } 93 + 94 + 95 + public function getPolicy($capability) { 96 + return PhabricatorPolicies::getMostOpenPolicy(); 97 + } 98 + 99 + 100 + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { 101 + return $this->getProfileObject()->hasAutomaticCapability( 102 + $capability, 103 + $viewer); 104 + } 105 + 106 + 107 + public function describeAutomaticCapability($capability) { 108 + return null; 109 + } 110 + 111 + 112 + /* -( PhabricatorExtendedPolicyInterface )--------------------------------- */ 113 + 114 + 115 + public function getExtendedPolicy($capability, PhabricatorUser $viewer) { 116 + return array( 117 + array( 118 + $this->getProfileObject(), 119 + $capability, 120 + ), 121 + ); 122 + } 123 + 124 + }
+18
src/applications/search/storage/PhabricatorProfilePanelConfigurationTransaction.php
··· 1 + <?php 2 + 3 + final class PhabricatorProfilePanelConfigurationTransaction 4 + extends PhabricatorApplicationTransaction { 5 + 6 + public function getApplicationName() { 7 + return 'search'; 8 + } 9 + 10 + public function getApplicationTransactionType() { 11 + return PhabricatorProfilePanelPHIDType::TYPECONST; 12 + } 13 + 14 + public function getApplicationTransactionCommentObject() { 15 + return null; 16 + } 17 + 18 + }