@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.

Modularize application configuration panels

Summary:
Ref T7149. This is a few steps away, but:

- Generally, I'd like to reduce the amount of "Config" configuration we have.
- One good way to do this is to move it into UIs in Application configuration. We did this with email recently.
- I think this was a great change and I'd like to keep moving in this direction.
- T7149 touches configuration related to file storage engines. Although I'm not planning to fully move configuration into applications yet, it would be easier to debug and test if I could drop a read-only panel there to show engines.
- So, modularize the config stuff so I can add a new panel without hard-coding it.

Test Plan:
- Added, edited, and deleted application emails.
- Viewed non-email application detail pages.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T7149

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

+305 -118
+48 -52
resources/celerity/map.php
··· 7 7 */ 8 8 return array( 9 9 'names' => array( 10 - 'core.pkg.css' => 'efdeeb14', 10 + 'core.pkg.css' => '70320e8a', 11 11 'core.pkg.js' => '5f50c01b', 12 12 'darkconsole.pkg.js' => '8ab24e01', 13 13 'differential.pkg.css' => '1940be3f', ··· 34 34 'rsrc/css/aphront/typeahead.css' => '0e403212', 35 35 'rsrc/css/application/almanac/almanac.css' => 'dbb9b3af', 36 36 'rsrc/css/application/auth/auth.css' => '1e655982', 37 - 'rsrc/css/application/base/main-menu-view.css' => '58db7ad2', 37 + 'rsrc/css/application/base/main-menu-view.css' => 'f9f5cd1b', 38 38 'rsrc/css/application/base/notification-menu.css' => '6aa0a74b', 39 39 'rsrc/css/application/base/phabricator-application-launch-view.css' => '16ca323f', 40 40 'rsrc/css/application/base/standard-page-view.css' => 'df338a4b', ··· 44 44 'rsrc/css/application/config/config-welcome.css' => '6abd79be', 45 45 'rsrc/css/application/config/setup-issue.css' => '22270af2', 46 46 'rsrc/css/application/config/unhandled-exception.css' => '37d4f9a2', 47 - 'rsrc/css/application/conpherence/durable-column.css' => 'acefcb30', 47 + 'rsrc/css/application/conpherence/durable-column.css' => '7abcc3f2', 48 48 'rsrc/css/application/conpherence/menu.css' => 'c6ac5299', 49 49 'rsrc/css/application/conpherence/message-pane.css' => '5930260a', 50 50 'rsrc/css/application/conpherence/notification.css' => '04a6e10a', ··· 105 105 'rsrc/css/application/tokens/tokens.css' => '3d0f239e', 106 106 'rsrc/css/application/uiexample/example.css' => '528b19de', 107 107 'rsrc/css/core/core.css' => '86bfbe8c', 108 - 'rsrc/css/core/remarkup.css' => 'bc65f3cc', 108 + 'rsrc/css/core/remarkup.css' => '2dbff225', 109 109 'rsrc/css/core/syntax.css' => '56c1ba38', 110 110 'rsrc/css/core/z-index.css' => '2db67397', 111 111 'rsrc/css/diviner/diviner-shared.css' => '38813222', 112 112 'rsrc/css/font/font-awesome.css' => 'ae9a7b4d', 113 - 'rsrc/css/font/font-source-sans-pro.css' => '4a2430d7', 113 + 'rsrc/css/font/font-source-sans-pro.css' => '0d859f60', 114 114 'rsrc/css/font/phui-font-icon-base.css' => '3dad2ae3', 115 115 'rsrc/css/layout/phabricator-filetree-view.css' => 'fccf9f82', 116 116 'rsrc/css/layout/phabricator-hovercard-view.css' => '893f4783', ··· 128 128 'rsrc/css/phui/phui-crumbs-view.css' => '594d719e', 129 129 'rsrc/css/phui/phui-document.css' => '0f83a7df', 130 130 'rsrc/css/phui/phui-feed-story.css' => 'c9f3a0b5', 131 - 'rsrc/css/phui/phui-fontkit.css' => '1fa79503', 131 + 'rsrc/css/phui/phui-fontkit.css' => 'd30f4fa3', 132 132 'rsrc/css/phui/phui-form-view.css' => '78d729fe', 133 133 'rsrc/css/phui/phui-form.css' => 'f535f938', 134 134 'rsrc/css/phui/phui-header-view.css' => '083669db', ··· 352 352 'rsrc/js/application/aphlict/behavior-aphlict-status.js' => 'ea681761', 353 353 'rsrc/js/application/auth/behavior-persona-login.js' => '9414ff18', 354 354 'rsrc/js/application/config/behavior-reorder-fields.js' => '14a827de', 355 - 'rsrc/js/application/conpherence/ConpherenceThreadManager.js' => '0324970d', 356 - 'rsrc/js/application/conpherence/behavior-durable-column.js' => '9142e483', 357 - 'rsrc/js/application/conpherence/behavior-menu.js' => 'c4151295', 355 + 'rsrc/js/application/conpherence/ConpherenceThreadManager.js' => 'efef202b', 356 + 'rsrc/js/application/conpherence/behavior-durable-column.js' => 'aa3b6c22', 357 + 'rsrc/js/application/conpherence/behavior-menu.js' => 'e476c952', 358 358 'rsrc/js/application/conpherence/behavior-pontificate.js' => '21ba5861', 359 359 'rsrc/js/application/conpherence/behavior-quicksand-blacklist.js' => '7927a7d3', 360 360 'rsrc/js/application/conpherence/behavior-widget-pane.js' => '2c1cd7f5', ··· 514 514 'changeset-view-manager' => '88be0133', 515 515 'config-options-css' => '7fedf08b', 516 516 'config-welcome-css' => '6abd79be', 517 - 'conpherence-durable-column-view' => 'acefcb30', 517 + 'conpherence-durable-column-view' => '7abcc3f2', 518 518 'conpherence-menu-css' => 'c6ac5299', 519 519 'conpherence-message-pane-css' => '5930260a', 520 520 'conpherence-notification-css' => '04a6e10a', 521 - 'conpherence-thread-manager' => '0324970d', 521 + 'conpherence-thread-manager' => 'efef202b', 522 522 'conpherence-update-css' => '1099a660', 523 523 'conpherence-widget-pane-css' => '3d575438', 524 524 'differential-changeset-view-css' => '6a8b172a', ··· 534 534 'diffusion-source-css' => '66fdf661', 535 535 'diviner-shared-css' => '38813222', 536 536 'font-fontawesome' => 'ae9a7b4d', 537 - 'font-source-sans-pro' => '4a2430d7', 537 + 'font-source-sans-pro' => '0d859f60', 538 538 'global-drag-and-drop-css' => '697324ad', 539 539 'harbormaster-css' => '49d64eb4', 540 540 'herald-css' => '826075fa', ··· 558 558 'javelin-behavior-boards-dropdown' => '0ec56e1d', 559 559 'javelin-behavior-choose-control' => '6153c708', 560 560 'javelin-behavior-config-reorder-fields' => '14a827de', 561 - 'javelin-behavior-conpherence-menu' => 'c4151295', 561 + 'javelin-behavior-conpherence-menu' => 'e476c952', 562 562 'javelin-behavior-conpherence-pontificate' => '21ba5861', 563 563 'javelin-behavior-conpherence-widget-pane' => '2c1cd7f5', 564 564 'javelin-behavior-countdown-timer' => 'e4cc26b3', ··· 585 585 'javelin-behavior-diffusion-locate-file' => '6d3e1947', 586 586 'javelin-behavior-diffusion-pull-lastmodified' => '2b228192', 587 587 'javelin-behavior-doorkeeper-tag' => 'e5822781', 588 - 'javelin-behavior-durable-column' => '9142e483', 588 + 'javelin-behavior-durable-column' => 'aa3b6c22', 589 589 'javelin-behavior-error-log' => '6882e80a', 590 590 'javelin-behavior-fancy-datepicker' => 'c51ae228', 591 591 'javelin-behavior-global-drag-and-drop' => '07f199d8', ··· 730 730 'phabricator-hovercard-view-css' => '893f4783', 731 731 'phabricator-keyboard-shortcut' => '1ae869f2', 732 732 'phabricator-keyboard-shortcut-manager' => 'c1700f6f', 733 - 'phabricator-main-menu-view' => '58db7ad2', 733 + 'phabricator-main-menu-view' => 'f9f5cd1b', 734 734 'phabricator-nav-view-css' => '7aeaf435', 735 735 'phabricator-notification' => '0c6946e7', 736 736 'phabricator-notification-css' => '9c279160', ··· 739 739 'phabricator-phtize' => 'd254d646', 740 740 'phabricator-prefab' => '72da38cc', 741 741 'phabricator-profile-css' => '1a20dcbf', 742 - 'phabricator-remarkup-css' => 'bc65f3cc', 742 + 'phabricator-remarkup-css' => '2dbff225', 743 743 'phabricator-search-results-css' => '559cc554', 744 744 'phabricator-shaped-request' => '7cbe244b', 745 745 'phabricator-side-menu-view-css' => '7e8c6341', ··· 783 783 'phui-document-view-css' => '0f83a7df', 784 784 'phui-feed-story-css' => 'c9f3a0b5', 785 785 'phui-font-icon-base-css' => '3dad2ae3', 786 - 'phui-fontkit-css' => '1fa79503', 786 + 'phui-fontkit-css' => 'd30f4fa3', 787 787 'phui-form-css' => 'f535f938', 788 788 'phui-form-view-css' => '78d729fe', 789 789 'phui-header-view-css' => '083669db', ··· 845 845 '029a133d' => array( 846 846 'aphront-dialog-view-css', 847 847 ), 848 - '0324970d' => array( 849 - 'javelin-dom', 850 - 'javelin-util', 851 - 'javelin-stratcom', 852 - 'javelin-install', 853 - 'javelin-workflow', 854 - 'javelin-router', 855 - 'javelin-behavior-device', 856 - 'javelin-vector', 857 - ), 858 848 '03d6ed07' => array( 859 849 'javelin-behavior', 860 850 'javelin-stratcom', ··· 1150 1140 'javelin-dom', 1151 1141 'javelin-request', 1152 1142 'javelin-util', 1153 - ), 1154 - '4a2430d7' => array( 1155 - 'phui-fontkit-css', 1156 1143 ), 1157 1144 '4d94d9c3' => array( 1158 1145 'javelin-behavior', ··· 1546 1533 'javelin-uri', 1547 1534 'phabricator-notification', 1548 1535 ), 1549 - '9142e483' => array( 1550 - 'javelin-behavior', 1551 - 'javelin-dom', 1552 - 'javelin-stratcom', 1553 - 'javelin-behavior-device', 1554 - 'javelin-scrollbar', 1555 - 'javelin-quicksand', 1556 - 'phabricator-keyboard-shortcut', 1557 - 'conpherence-thread-manager', 1558 - ), 1559 1536 '92eb531d' => array( 1560 1537 'javelin-behavior', 1561 1538 'javelin-dom', ··· 1674 1651 'javelin-util', 1675 1652 'phabricator-prefab', 1676 1653 ), 1654 + 'aa3b6c22' => array( 1655 + 'javelin-behavior', 1656 + 'javelin-dom', 1657 + 'javelin-stratcom', 1658 + 'javelin-scrollbar', 1659 + 'javelin-quicksand', 1660 + 'phabricator-keyboard-shortcut', 1661 + 'conpherence-thread-manager', 1662 + ), 1677 1663 'b1f0ccee' => array( 1678 1664 'javelin-install', 1679 1665 'javelin-dom', ··· 1758 1744 'javelin-dom', 1759 1745 'javelin-vector', 1760 1746 ), 1761 - 'c4151295' => array( 1762 - 'javelin-behavior', 1763 - 'javelin-dom', 1764 - 'javelin-util', 1765 - 'javelin-stratcom', 1766 - 'javelin-workflow', 1767 - 'javelin-behavior-device', 1768 - 'javelin-history', 1769 - 'javelin-vector', 1770 - 'phabricator-shaped-request', 1771 - 'conpherence-thread-manager', 1772 - ), 1773 1747 'c51ae228' => array( 1774 1748 'javelin-behavior', 1775 1749 'javelin-util', ··· 1873 1847 'javelin-dom', 1874 1848 'javelin-uri', 1875 1849 ), 1850 + 'e476c952' => array( 1851 + 'javelin-behavior', 1852 + 'javelin-dom', 1853 + 'javelin-util', 1854 + 'javelin-stratcom', 1855 + 'javelin-workflow', 1856 + 'javelin-behavior-device', 1857 + 'javelin-history', 1858 + 'javelin-vector', 1859 + 'phabricator-shaped-request', 1860 + 'conpherence-thread-manager', 1861 + ), 1876 1862 'e4cc26b3' => array( 1877 1863 'javelin-behavior', 1878 1864 'javelin-dom', ··· 1919 1905 'efe49472' => array( 1920 1906 'javelin-install', 1921 1907 'javelin-util', 1908 + ), 1909 + 'efef202b' => array( 1910 + 'javelin-dom', 1911 + 'javelin-util', 1912 + 'javelin-stratcom', 1913 + 'javelin-install', 1914 + 'javelin-workflow', 1915 + 'javelin-router', 1916 + 'javelin-behavior-device', 1917 + 'javelin-vector', 1922 1918 ), 1923 1919 'f24f3253' => array( 1924 1920 'javelin-behavior',
+6 -2
src/__phutil_library_map__.php
··· 1269 1269 'PhabricatorApplication' => 'applications/base/PhabricatorApplication.php', 1270 1270 'PhabricatorApplicationApplicationPHIDType' => 'applications/meta/phid/PhabricatorApplicationApplicationPHIDType.php', 1271 1271 'PhabricatorApplicationConfigOptions' => 'applications/config/option/PhabricatorApplicationConfigOptions.php', 1272 + 'PhabricatorApplicationConfigurationPanel' => 'applications/meta/panel/PhabricatorApplicationConfigurationPanel.php', 1272 1273 'PhabricatorApplicationDatasource' => 'applications/meta/typeahead/PhabricatorApplicationDatasource.php', 1273 1274 'PhabricatorApplicationDetailViewController' => 'applications/meta/controller/PhabricatorApplicationDetailViewController.php', 1274 1275 'PhabricatorApplicationEditController' => 'applications/meta/controller/PhabricatorApplicationEditController.php', 1275 - 'PhabricatorApplicationEditEmailController' => 'applications/meta/controller/PhabricatorApplicationEditEmailController.php', 1276 1276 'PhabricatorApplicationLaunchView' => 'applications/meta/view/PhabricatorApplicationLaunchView.php', 1277 + 'PhabricatorApplicationPanelController' => 'applications/meta/controller/PhabricatorApplicationPanelController.php', 1277 1278 'PhabricatorApplicationQuery' => 'applications/meta/query/PhabricatorApplicationQuery.php', 1278 1279 'PhabricatorApplicationSearchController' => 'applications/search/controller/PhabricatorApplicationSearchController.php', 1279 1280 'PhabricatorApplicationSearchEngine' => 'applications/search/engine/PhabricatorApplicationSearchEngine.php', ··· 1988 1989 'PhabricatorMetaMTAApplicationEmail' => 'applications/metamta/storage/PhabricatorMetaMTAApplicationEmail.php', 1989 1990 'PhabricatorMetaMTAApplicationEmailDatasource' => 'applications/metamta/typeahead/PhabricatorMetaMTAApplicationEmailDatasource.php', 1990 1991 'PhabricatorMetaMTAApplicationEmailPHIDType' => 'applications/phid/PhabricatorMetaMTAApplicationEmailPHIDType.php', 1992 + 'PhabricatorMetaMTAApplicationEmailPanel' => 'applications/metamta/applicationpanel/PhabricatorMetaMTAApplicationEmailPanel.php', 1991 1993 'PhabricatorMetaMTAApplicationEmailQuery' => 'applications/metamta/query/PhabricatorMetaMTAApplicationEmailQuery.php', 1992 1994 'PhabricatorMetaMTAAttachment' => 'applications/metamta/storage/PhabricatorMetaMTAAttachment.php', 1993 1995 'PhabricatorMetaMTAConfigOptions' => 'applications/config/option/PhabricatorMetaMTAConfigOptions.php', ··· 4510 4512 'PhabricatorApplication' => 'PhabricatorPolicyInterface', 4511 4513 'PhabricatorApplicationApplicationPHIDType' => 'PhabricatorPHIDType', 4512 4514 'PhabricatorApplicationConfigOptions' => 'Phobject', 4515 + 'PhabricatorApplicationConfigurationPanel' => 'Phobject', 4513 4516 'PhabricatorApplicationDatasource' => 'PhabricatorTypeaheadDatasource', 4514 4517 'PhabricatorApplicationDetailViewController' => 'PhabricatorApplicationsController', 4515 4518 'PhabricatorApplicationEditController' => 'PhabricatorApplicationsController', 4516 - 'PhabricatorApplicationEditEmailController' => 'PhabricatorApplicationsController', 4517 4519 'PhabricatorApplicationLaunchView' => 'AphrontTagView', 4520 + 'PhabricatorApplicationPanelController' => 'PhabricatorApplicationsController', 4518 4521 'PhabricatorApplicationQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 4519 4522 'PhabricatorApplicationSearchController' => 'PhabricatorSearchBaseController', 4520 4523 'PhabricatorApplicationStatusView' => 'AphrontView', ··· 5272 5275 ), 5273 5276 'PhabricatorMetaMTAApplicationEmailDatasource' => 'PhabricatorTypeaheadDatasource', 5274 5277 'PhabricatorMetaMTAApplicationEmailPHIDType' => 'PhabricatorPHIDType', 5278 + 'PhabricatorMetaMTAApplicationEmailPanel' => 'PhabricatorApplicationConfigurationPanel', 5275 5279 'PhabricatorMetaMTAApplicationEmailQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 5276 5280 'PhabricatorMetaMTAConfigOptions' => 'PhabricatorApplicationConfigOptions', 5277 5281 'PhabricatorMetaMTAController' => 'PhabricatorController',
+2 -2
src/applications/meta/application/PhabricatorApplicationsApplication.php
··· 41 41 => 'PhabricatorApplicationDetailViewController', 42 42 'edit/(?P<application>\w+)/' 43 43 => 'PhabricatorApplicationEditController', 44 - 'editemail/(?P<application>\w+)/' 45 - => 'PhabricatorApplicationEditEmailController', 46 44 '(?P<application>\w+)/(?P<action>install|uninstall)/' 47 45 => 'PhabricatorApplicationUninstallController', 46 + 'panel/(?P<application>\w+)/(?P<panel>\w+)/(?P<path>.*)' 47 + => 'PhabricatorApplicationPanelController', 48 48 ), 49 49 ); 50 50 }
+17 -36
src/applications/meta/controller/PhabricatorApplicationDetailViewController.php
··· 9 9 } 10 10 11 11 public function handleRequest(AphrontRequest $request) { 12 - $user = $request->getUser(); 12 + $viewer = $this->getViewer(); 13 13 $application = $request->getURIData('application'); 14 14 15 15 $selected = id(new PhabricatorApplicationQuery()) 16 - ->setViewer($user) 16 + ->setViewer($viewer) 17 17 ->withClasses(array($application)) 18 18 ->executeOne(); 19 19 if (!$selected) { ··· 27 27 28 28 $header = id(new PHUIHeaderView()) 29 29 ->setHeader($title) 30 - ->setUser($user) 30 + ->setUser($viewer) 31 31 ->setPolicyObject($selected); 32 32 33 33 if ($selected->isInstalled()) { ··· 36 36 $header->setStatus('fa-ban', 'dark', pht('Uninstalled')); 37 37 } 38 38 39 - $actions = $this->buildActionView($user, $selected); 39 + $actions = $this->buildActionView($viewer, $selected); 40 40 $properties = $this->buildPropertyView($selected, $actions); 41 41 42 42 $object_box = id(new PHUIObjectBoxView()) 43 43 ->setHeader($header) 44 44 ->addPropertyList($properties); 45 45 46 + $configs = 47 + PhabricatorApplicationConfigurationPanel::loadAllPanelsForApplication( 48 + $selected); 49 + 50 + $panels = array(); 51 + foreach ($configs as $config) { 52 + $config->setViewer($viewer); 53 + $config->setApplication($selected); 54 + 55 + $panels[] = $config->buildConfigurationPagePanel(); 56 + } 57 + 46 58 return $this->buildApplicationPage( 47 59 array( 48 60 $crumbs, 49 61 $object_box, 62 + $panels, 50 63 ), 51 64 array( 52 65 'title' => $title, ··· 114 127 idx($descriptions, $capability)); 115 128 } 116 129 117 - if ($application->supportsEmailIntegration()) { 118 - $properties->addSectionHeader(pht('Application Emails')); 119 - $properties->addTextContent($application->getAppEmailBlurb()); 120 - $email_addresses = id(new PhabricatorMetaMTAApplicationEmailQuery()) 121 - ->setViewer($viewer) 122 - ->withApplicationPHIDs(array($application->getPHID())) 123 - ->execute(); 124 - if (empty($email_addresses)) { 125 - $properties->addProperty( 126 - null, 127 - pht('No email addresses configured.')); 128 - } else { 129 - foreach ($email_addresses as $email_address) { 130 - $properties->addProperty( 131 - null, 132 - $email_address->getAddress()); 133 - } 134 - } 135 - } 136 - 137 130 return $properties; 138 131 } 139 132 ··· 167 160 ->setDisabled(!$can_edit) 168 161 ->setWorkflow(!$can_edit) 169 162 ->setHref($edit_uri)); 170 - 171 - if ($selected->supportsEmailIntegration()) { 172 - $edit_email_uri = $this->getApplicationURI( 173 - 'editemail/'.get_class($selected).'/'); 174 - $view->addAction( 175 - id(new PhabricatorActionView()) 176 - ->setName(pht('Edit Application Emails')) 177 - ->setIcon('fa-envelope') 178 - ->setDisabled(!$can_edit) 179 - ->setWorkflow(!$can_edit) 180 - ->setHref($edit_email_uri)); 181 - } 182 163 183 164 if ($selected->canUninstall()) { 184 165 if ($selected->isInstalled()) {
+77 -26
src/applications/meta/controller/PhabricatorApplicationEditEmailController.php src/applications/metamta/applicationpanel/PhabricatorMetaMTAApplicationEmailPanel.php
··· 1 1 <?php 2 2 3 - final class PhabricatorApplicationEditEmailController 4 - extends PhabricatorApplicationsController { 3 + final class PhabricatorMetaMTAApplicationEmailPanel 4 + extends PhabricatorApplicationConfigurationPanel { 5 5 6 - public function handleRequest(AphrontRequest $request) { 7 - $viewer = $request->getUser(); 8 - $application = $request->getURIData('application'); 6 + public function getPanelKey() { 7 + return 'email'; 8 + } 9 9 10 - $application = id(new PhabricatorApplicationQuery()) 10 + public function shouldShowForApplication( 11 + PhabricatorApplication $application) { 12 + return $application->supportsEmailIntegration(); 13 + } 14 + 15 + public function buildConfigurationPagePanel() { 16 + $viewer = $this->getViewer(); 17 + $application = $this->getApplication(); 18 + 19 + $addresses = id(new PhabricatorMetaMTAApplicationEmailQuery()) 11 20 ->setViewer($viewer) 12 - ->withClasses(array($application)) 13 - ->requireCapabilities( 21 + ->withApplicationPHIDs(array($application->getPHID())) 22 + ->execute(); 23 + 24 + $rows = array(); 25 + foreach ($addresses as $address) { 26 + $rows[] = array( 27 + $address->getAddress(), 28 + ); 29 + } 30 + 31 + $table = id(new AphrontTableView($rows)) 32 + ->setNoDataString(pht('No email addresses configured.')) 33 + ->setHeaders( 14 34 array( 15 - PhabricatorPolicyCapability::CAN_VIEW, 16 - PhabricatorPolicyCapability::CAN_EDIT, 17 - )) 18 - ->executeOne(); 19 - if (!$application) { 35 + pht('Address'), 36 + )); 37 + 38 + $can_edit = PhabricatorPolicyFilter::hasCapability( 39 + $viewer, 40 + $application, 41 + PhabricatorPolicyCapability::CAN_EDIT); 42 + 43 + $header = id(new PHUIHeaderView()) 44 + ->setHeader(pht('Application Emails')) 45 + ->addActionLink( 46 + id(new PHUIButtonView()) 47 + ->setTag('a') 48 + ->setText(pht('Edit Application Emails')) 49 + ->setIcon( 50 + id(new PHUIIconView()) 51 + ->setIconFont('fa-pencil')) 52 + ->setHref($this->getPanelURI()) 53 + ->setDisabled(!$can_edit) 54 + ->setWorkflow(!$can_edit)); 55 + 56 + 57 + $box = id(new PHUIObjectBoxView()) 58 + ->setHeader($header) 59 + ->appendChild($table); 60 + 61 + return $box; 62 + } 63 + 64 + public function handlePanelRequest( 65 + AphrontRequest $request, 66 + PhabricatorController $controller) { 67 + $viewer = $request->getViewer(); 68 + $application = $this->getApplication(); 69 + 70 + $path = $request->getURIData('path'); 71 + if (strlen($path)) { 20 72 return new Aphront404Response(); 21 73 } 22 74 23 - $title = $application->getName(); 24 - 25 75 $uri = $request->getRequestURI(); 26 76 $uri->setQueryParams(array()); 27 77 ··· 106 156 $form = id(new AphrontFormView()) 107 157 ->setUser($viewer); 108 158 109 - $view_uri = $this->getApplicationURI('view/'.get_class($application).'/'); 110 - $crumbs = $this->buildApplicationCrumbs(); 111 - $crumbs->addTextCrumb($application->getName(), $view_uri); 159 + $crumbs = $controller->buildPanelCrumbs($this); 112 160 $crumbs->addTextCrumb(pht('Edit Application Emails')); 113 161 114 162 $header = id(new PHUIHeaderView()) 115 - ->setHeader(pht('Edit Application Emails: %s', $application->getName())); 163 + ->setHeader(pht('Edit Application Emails: %s', $application->getName())) 164 + ->setSubheader($application->getAppEmailBlurb()); 116 165 117 166 $icon = id(new PHUIIconView()) 118 167 ->setIconFont('fa-plus'); ··· 126 175 127 176 $object_box = id(new PHUIObjectBoxView()) 128 177 ->setHeader($header) 129 - ->appendChild($table) 130 - ->appendChild( 131 - id(new PHUIBoxView()) 132 - ->appendChild($application->getAppEmailBlurb()) 133 - ->addPadding(PHUI::PADDING_MEDIUM)); 178 + ->appendChild($table); 179 + 180 + $title = $application->getName(); 134 181 135 - return $this->buildApplicationPage( 182 + return $controller->buildPanelPage( 183 + $this, 136 184 array( 137 185 $crumbs, 138 186 $object_box, ··· 253 301 254 302 $default_user = $email_object->getConfigValue($default_user_key); 255 303 if ($default_user) { 256 - $default_user_handle = $this->loadViewerHandles(array($default_user)); 304 + $default_user_handle = id(new PhabricatorHandleQuery()) 305 + ->setViewer($viewer) 306 + ->withPHIDs(array($default_user)) 307 + ->execute(); 257 308 } else { 258 309 $default_user_handle = array(); 259 310 }
+67
src/applications/meta/controller/PhabricatorApplicationPanelController.php
··· 1 + <?php 2 + 3 + final class PhabricatorApplicationPanelController 4 + extends PhabricatorApplicationsController { 5 + 6 + private $application; 7 + 8 + public function handleRequest(AphrontRequest $request) { 9 + $viewer = $this->getViewer(); 10 + 11 + $application = $request->getURIData('application'); 12 + $panel_key = $request->getURIData('panel'); 13 + 14 + $selected = id(new PhabricatorApplicationQuery()) 15 + ->setViewer($viewer) 16 + ->withClasses(array($application)) 17 + ->requireCapabilities( 18 + array( 19 + PhabricatorPolicyCapability::CAN_VIEW, 20 + PhabricatorPolicyCapability::CAN_EDIT, 21 + )) 22 + ->executeOne(); 23 + if (!$selected) { 24 + return new Aphront404Response(); 25 + } 26 + 27 + $panels = 28 + PhabricatorApplicationConfigurationPanel::loadAllPanelsForApplication( 29 + $selected); 30 + if (empty($panels[$panel_key])) { 31 + return new Aphront404Response(); 32 + } 33 + 34 + $panel = $panels[$panel_key]; 35 + 36 + if (!$panel->shouldShowForApplication($selected)) { 37 + return new Aphront404Response(); 38 + } 39 + 40 + $panel->setViewer($viewer); 41 + $panel->setApplication($selected); 42 + 43 + $this->application = $selected; 44 + 45 + return $panel->handlePanelRequest($request, $this); 46 + } 47 + 48 + public function buildPanelCrumbs( 49 + PhabricatorApplicationConfigurationPanel $panel) { 50 + $application = $this->application; 51 + 52 + $crumbs = $this->buildApplicationCrumbs(); 53 + 54 + $view_uri = '/applications/view/'.get_class($application).'/'; 55 + $crumbs->addTextCrumb($application->getName(), $view_uri); 56 + 57 + return $crumbs; 58 + } 59 + 60 + public function buildPanelPage( 61 + PhabricatorApplicationConfigurationPanel $panel, 62 + $content, 63 + array $options) { 64 + return $this->buildApplicationPage($content, $options); 65 + } 66 + 67 + }
+88
src/applications/meta/panel/PhabricatorApplicationConfigurationPanel.php
··· 1 + <?php 2 + 3 + abstract class PhabricatorApplicationConfigurationPanel 4 + extends Phobject { 5 + 6 + private $viewer; 7 + private $application; 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 setApplication(PhabricatorApplication $application) { 19 + $this->application = $application; 20 + return $this; 21 + } 22 + 23 + public function getApplication() { 24 + return $this->application; 25 + } 26 + 27 + public function getPanelURI($path = null) { 28 + $app_key = get_class($this->getApplication()); 29 + $panel_key = $this->getPanelKey(); 30 + $base = "/applications/panel/{$app_key}/{$panel_key}/"; 31 + return $base.ltrim($path, '/'); 32 + } 33 + 34 + /** 35 + * Return a short, unique string key which identifies this panel. 36 + * 37 + * This key is used in URIs. Good values might be "email" or "files". 38 + */ 39 + abstract public function getPanelKey(); 40 + 41 + abstract public function shouldShowForApplication( 42 + PhabricatorApplication $application); 43 + 44 + abstract public function buildConfigurationPagePanel(); 45 + abstract public function handlePanelRequest( 46 + AphrontRequest $request, 47 + PhabricatorController $controller); 48 + 49 + public static function loadAllPanels() { 50 + $objects = id(new PhutilSymbolLoader()) 51 + ->setAncestorClass(__CLASS__) 52 + ->loadObjects(); 53 + 54 + $panels = array(); 55 + foreach ($objects as $object) { 56 + $key = $object->getPanelKey(); 57 + if (empty($panels[$key])) { 58 + $panels[$key] = $object; 59 + } else { 60 + throw new Exception( 61 + pht( 62 + 'Application configuration panels "%s" and "%s" have the same '. 63 + 'panel key, "%s". Each panel must have a unique key.', 64 + get_class($object), 65 + get_class($panels[$key]), 66 + $key)); 67 + } 68 + } 69 + 70 + return $panels; 71 + } 72 + 73 + public static function loadAllPanelsForApplication( 74 + PhabricatorApplication $application) { 75 + $panels = self::loadAllPanels(); 76 + 77 + $application_panels = array(); 78 + foreach ($panels as $key => $panel) { 79 + if (!$panel->shouldShowForApplication($application)) { 80 + continue; 81 + } 82 + $application_panels[$key] = $panel; 83 + } 84 + 85 + return $application_panels; 86 + } 87 + 88 + }