4 * Defines a settings panel. Settings panels appear in the Settings application,
5 * and behave like lightweight controllers -- generally, they render some sort
6 * of form with options in it, and then update preferences when the user
7 * submits the form. By extending this class, you can add new settings
10 * @task config Panel Configuration
11 * @task panel Panel Implementation
12 * @task internal Internals
14 abstract class PhabricatorSettingsPanel
extends Phobject
{
23 public function setUser(PhabricatorUser
$user) {
28 public function getUser() {
32 public function setViewer(PhabricatorUser
$viewer) {
33 $this->viewer
= $viewer;
37 public function getViewer() {
41 public function setOverrideURI($override_uri) {
42 $this->overrideURI
= $override_uri;
46 final public function setController(PhabricatorController
$controller) {
47 $this->controller
= $controller;
51 final public function getController() {
52 return $this->controller
;
55 final public function setNavigation(AphrontSideNavFilterView
$navigation) {
56 $this->navigation
= $navigation;
60 final public function getNavigation() {
61 return $this->navigation
;
64 public function setPreferences(PhabricatorUserPreferences
$preferences) {
65 $this->preferences
= $preferences;
69 public function getPreferences() {
70 return $this->preferences
;
73 final public static function getAllPanels() {
74 $panels = id(new PhutilClassMapQuery())
75 ->setAncestorClass(__CLASS__
)
76 ->setUniqueMethod('getPanelKey')
78 return msortv($panels, 'getPanelOrderVector');
81 final public static function getAllDisplayPanels() {
83 $groups = PhabricatorSettingsPanelGroup
::getAllPanelGroupsWithPanels();
84 foreach ($groups as $group) {
85 foreach ($group->getPanels() as $key => $panel) {
86 $panels[$key] = $panel;
93 final public function getPanelGroup() {
94 $group_key = $this->getPanelGroupKey();
96 $groups = PhabricatorSettingsPanelGroup
::getAllPanelGroupsWithPanels();
97 $group = idx($groups, $group_key);
101 'No settings panel group with key "%s" exists!',
109 /* -( Panel Configuration )------------------------------------------------ */
113 * Return a unique string used in the URI to identify this panel, like
116 * @return string Unique panel identifier (used in URIs).
119 public function getPanelKey() {
120 return $this->getPhobjectClassConstant('PANELKEY');
125 * Return a human-readable description of the panel's contents, like
126 * "Example Settings".
128 * @return string Human-readable panel name.
131 abstract public function getPanelName();
135 * Return an icon for the panel in the menu.
137 * @return string Icon identifier.
140 public function getPanelMenuIcon() {
145 * Return a panel group key constant for this panel.
147 * @return const Panel group key.
150 abstract public function getPanelGroupKey();
154 * Return false to prevent this panel from being displayed or used. You can
155 * do, e.g., configuration checks here, to determine if the feature your
156 * panel controls is unavailable in this install. By default, all panels are
159 * @return bool True if the panel should be shown.
162 public function isEnabled() {
168 * Return true if this panel is available to users while editing their own
171 * @return bool True to enable management on behalf of a user.
174 public function isUserPanel() {
180 * Return true if this panel is available to administrators while managing
181 * bot and mailing list accounts.
183 * @return bool True to enable management on behalf of accounts.
186 public function isManagementPanel() {
192 * Return true if this panel is available while editing settings templates.
194 * @return bool True to allow editing in templates.
197 public function isTemplatePanel() {
202 * Return true if this panel should be available when enrolling in MFA on
203 * a new account with MFA requiredd.
205 * @return bool True to allow configuration during MFA enrollment.
208 public function isMultiFactorEnrollmentPanel() {
213 /* -( Panel Implementation )----------------------------------------------- */
217 * Process a user request for this settings panel. Implement this method like
218 * a lightweight controller. If you return an @{class:AphrontResponse}, the
219 * response will be used in whole. If you return anything else, it will be
220 * treated as a view and composed into a normal settings page.
222 * Generally, render your settings panel by returning a form, then return
223 * a redirect when the user saves settings.
225 * @param AphrontRequest Incoming request.
226 * @return wild Response to request, either as an
227 * @{class:AphrontResponse} or something which can
228 * be composed into a @{class:AphrontView}.
231 abstract public function processRequest(AphrontRequest
$request);
235 * Get the URI for this panel.
237 * @param string? Optional path to append.
238 * @return string Relative URI for the panel.
241 final public function getPanelURI($path = '') {
242 $path = ltrim($path, '/');
244 if ($this->overrideURI
) {
245 return rtrim($this->overrideURI
, '/').'/'.$path;
248 $key = $this->getPanelKey();
249 $key = phutil_escape_uri($key);
251 $user = $this->getUser();
253 if ($user->isLoggedIn()) {
254 $username = $user->getUsername();
255 return "/settings/user/{$username}/page/{$key}/{$path}";
257 // For logged-out users, we can't put their username in the URI. This
258 // page will prompt them to login, then redirect them to the correct
260 return "/settings/panel/{$key}/";
263 $builtin = $this->getPreferences()->getBuiltinKey();
264 return "/settings/builtin/{$builtin}/page/{$key}/{$path}";
269 /* -( Internals )---------------------------------------------------------- */
273 * Generates a key to sort the list of panels.
275 * @return string Sortable key.
278 final public function getPanelOrderVector() {
279 return id(new PhutilSortVector())
280 ->addString($this->getPanelName());
283 protected function newDialog() {
284 return $this->getController()->newDialog();
287 protected function writeSetting(
288 PhabricatorUserPreferences
$preferences,
291 $viewer = $this->getViewer();
292 $request = $this->getController()->getRequest();
294 $editor = id(new PhabricatorUserPreferencesEditor())
296 ->setContentSourceFromRequest($request)
297 ->setContinueOnNoEffect(true)
298 ->setContinueOnMissingFields(true);
301 $xactions[] = $preferences->newTransaction($key, $value);
302 $editor->applyTransactions($preferences, $xactions);
306 public function newBox($title, $content, $actions = array()) {
307 $header = id(new PHUIHeaderView())
310 foreach ($actions as $action) {
311 $header->addActionLink($action);
314 $view = id(new PHUIObjectBoxView())
316 ->appendChild($content)
317 ->setBackground(PHUIObjectBoxView
::WHITE_CONFIG
);