Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/conduit/settings/PhabricatorConduitTokensSettingsPanel.php b/src/applications/conduit/settings/PhabricatorConduitTokensSettingsPanel.php
index 2075582386..cd97e2fd7f 100644
--- a/src/applications/conduit/settings/PhabricatorConduitTokensSettingsPanel.php
+++ b/src/applications/conduit/settings/PhabricatorConduitTokensSettingsPanel.php
@@ -1,118 +1,122 @@
<?php
final class PhabricatorConduitTokensSettingsPanel
extends PhabricatorSettingsPanel {
public function isManagementPanel() {
if ($this->getUser()->getIsMailingList()) {
return false;
}
return true;
}
public function getPanelKey() {
return 'apitokens';
}
public function getPanelName() {
return pht('Conduit API Tokens');
}
+ public function getPanelMenuIcon() {
+ return id(new PhabricatorConduitApplication())->getIcon();
+ }
+
public function getPanelGroupKey() {
return PhabricatorSettingsLogsPanelGroup::PANELGROUPKEY;
}
public function isEnabled() {
return true;
}
public function processRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$user = $this->getUser();
$tokens = id(new PhabricatorConduitTokenQuery())
->setViewer($viewer)
->withObjectPHIDs(array($user->getPHID()))
->withExpired(false)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->execute();
$rows = array();
foreach ($tokens as $token) {
$rows[] = array(
javelin_tag(
'a',
array(
'href' => '/conduit/token/edit/'.$token->getID().'/',
'sigil' => 'workflow',
),
$token->getPublicTokenName()),
PhabricatorConduitToken::getTokenTypeName($token->getTokenType()),
phabricator_datetime($token->getDateCreated(), $viewer),
($token->getExpires()
? phabricator_datetime($token->getExpires(), $viewer)
: pht('Never')),
javelin_tag(
'a',
array(
'class' => 'button small button-grey',
'href' => '/conduit/token/terminate/'.$token->getID().'/',
'sigil' => 'workflow',
),
pht('Terminate')),
);
}
$table = new AphrontTableView($rows);
$table->setNoDataString(pht("You don't have any active API tokens."));
$table->setHeaders(
array(
pht('Token'),
pht('Type'),
pht('Created'),
pht('Expires'),
null,
));
$table->setColumnClasses(
array(
'wide pri',
'',
'right',
'right',
'action',
));
$generate_button = id(new PHUIButtonView())
->setText(pht('Generate Token'))
->setHref('/conduit/token/edit/?objectPHID='.$user->getPHID())
->setTag('a')
->setWorkflow(true)
->setIcon('fa-plus');
$terminate_button = id(new PHUIButtonView())
->setText(pht('Terminate Tokens'))
->setHref('/conduit/token/terminate/?objectPHID='.$user->getPHID())
->setTag('a')
->setWorkflow(true)
->setIcon('fa-exclamation-triangle')
->setColor(PHUIButtonView::RED);
$header = id(new PHUIHeaderView())
->setHeader(pht('Active API Tokens'))
->addActionLink($generate_button)
->addActionLink($terminate_button);
$panel = id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::WHITE_CONFIG)
->appendChild($table);
return $panel;
}
}
diff --git a/src/applications/diffusion/panel/DiffusionSetPasswordSettingsPanel.php b/src/applications/diffusion/panel/DiffusionSetPasswordSettingsPanel.php
index 1899302223..789adfbf57 100644
--- a/src/applications/diffusion/panel/DiffusionSetPasswordSettingsPanel.php
+++ b/src/applications/diffusion/panel/DiffusionSetPasswordSettingsPanel.php
@@ -1,264 +1,268 @@
<?php
final class DiffusionSetPasswordSettingsPanel extends PhabricatorSettingsPanel {
public function isManagementPanel() {
if ($this->getUser()->getIsMailingList()) {
return false;
}
return true;
}
public function getPanelKey() {
return 'vcspassword';
}
public function getPanelName() {
return pht('VCS Password');
}
+ public function getPanelMenuIcon() {
+ return 'fa-code';
+ }
+
public function getPanelGroupKey() {
return PhabricatorSettingsAuthenticationPanelGroup::PANELGROUPKEY;
}
public function isEnabled() {
return PhabricatorEnv::getEnvConfig('diffusion.allow-http-auth');
}
public function processRequest(AphrontRequest $request) {
$viewer = $request->getUser();
$user = $this->getUser();
$token = id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession(
$viewer,
$request,
'/settings/');
$vcs_type = PhabricatorAuthPassword::PASSWORD_TYPE_VCS;
$vcspasswords = id(new PhabricatorAuthPasswordQuery())
->setViewer($viewer)
->withObjectPHIDs(array($user->getPHID()))
->withPasswordTypes(array($vcs_type))
->withIsRevoked(false)
->execute();
if ($vcspasswords) {
$vcspassword = head($vcspasswords);
} else {
$vcspassword = PhabricatorAuthPassword::initializeNewPassword(
$user,
$vcs_type);
}
$panel_uri = $this->getPanelURI('?saved=true');
$errors = array();
$e_password = true;
$e_confirm = true;
$content_source = PhabricatorContentSource::newFromRequest($request);
// NOTE: This test is against $viewer (not $user), so that the error
// message below makes sense in the case that the two are different,
// and because an admin reusing their own password is bad, while
// system agents generally do not have passwords anyway.
$engine = id(new PhabricatorAuthPasswordEngine())
->setViewer($viewer)
->setContentSource($content_source)
->setObject($viewer)
->setPasswordType($vcs_type);
if ($request->isFormPost()) {
if ($request->getBool('remove')) {
if ($vcspassword->getID()) {
$vcspassword->delete();
return id(new AphrontRedirectResponse())->setURI($panel_uri);
}
}
$new_password = $request->getStr('password');
$confirm = $request->getStr('confirm');
$envelope = new PhutilOpaqueEnvelope($new_password);
$confirm_envelope = new PhutilOpaqueEnvelope($confirm);
try {
$engine->checkNewPassword($envelope, $confirm_envelope);
$e_password = null;
$e_confirm = null;
} catch (PhabricatorAuthPasswordException $ex) {
$errors[] = $ex->getMessage();
$e_password = $ex->getPasswordError();
$e_confirm = $ex->getConfirmError();
}
if (!$errors) {
$vcspassword
->setPassword($envelope, $user)
->save();
return id(new AphrontRedirectResponse())->setURI($panel_uri);
}
}
$title = pht('Set VCS Password');
$form = id(new AphrontFormView())
->setUser($viewer)
->appendRemarkupInstructions(
pht(
'To access repositories hosted by Phabricator over HTTP, you must '.
'set a version control password. This password should be unique.'.
"\n\n".
"This password applies to all repositories available over ".
"HTTP."));
if ($vcspassword->getID()) {
$form
->appendChild(
id(new AphrontFormPasswordControl())
->setDisableAutocomplete(true)
->setLabel(pht('Current Password'))
->setDisabled(true)
->setValue('********************'));
} else {
$form
->appendChild(
id(new AphrontFormMarkupControl())
->setLabel(pht('Current Password'))
->setValue(phutil_tag('em', array(), pht('No Password Set'))));
}
$form
->appendChild(
id(new AphrontFormPasswordControl())
->setDisableAutocomplete(true)
->setName('password')
->setLabel(pht('New VCS Password'))
->setError($e_password))
->appendChild(
id(new AphrontFormPasswordControl())
->setDisableAutocomplete(true)
->setName('confirm')
->setLabel(pht('Confirm VCS Password'))
->setError($e_confirm))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Change Password')));
if (!$vcspassword->getID()) {
$is_serious = PhabricatorEnv::getEnvConfig(
'phabricator.serious-business');
$suggest = Filesystem::readRandomBytes(128);
$suggest = preg_replace('([^A-Za-z0-9/!().,;{}^&*%~])', '', $suggest);
$suggest = substr($suggest, 0, 20);
if ($is_serious) {
$form->appendRemarkupInstructions(
pht(
'Having trouble coming up with a good password? Try this randomly '.
'generated one, made by a computer:'.
"\n\n".
"`%s`",
$suggest));
} else {
$form->appendRemarkupInstructions(
pht(
'Having trouble coming up with a good password? Try this '.
'artisanal password, hand made in small batches by our expert '.
'craftspeople: '.
"\n\n".
"`%s`",
$suggest));
}
}
$hash_envelope = new PhutilOpaqueEnvelope($vcspassword->getPasswordHash());
$form->appendChild(
id(new AphrontFormStaticControl())
->setLabel(pht('Current Algorithm'))
->setValue(
PhabricatorPasswordHasher::getCurrentAlgorithmName($hash_envelope)));
$form->appendChild(
id(new AphrontFormStaticControl())
->setLabel(pht('Best Available Algorithm'))
->setValue(PhabricatorPasswordHasher::getBestAlgorithmName()));
if (strlen($hash_envelope->openEnvelope())) {
try {
$can_upgrade = PhabricatorPasswordHasher::canUpgradeHash(
$hash_envelope);
} catch (PhabricatorPasswordHasherUnavailableException $ex) {
$can_upgrade = false;
$errors[] = pht(
'Your VCS password is currently hashed using an algorithm which is '.
'no longer available on this install.');
$errors[] = pht(
'Because the algorithm implementation is missing, your password '.
'can not be used.');
$errors[] = pht(
'You can set a new password to replace the old password.');
}
if ($can_upgrade) {
$errors[] = pht(
'The strength of your stored VCS password hash can be upgraded. '.
'To upgrade, either: use the password to authenticate with a '.
'repository; or change your password.');
}
}
$object_box = id(new PHUIObjectBoxView())
->setHeaderText($title)
->setBackground(PHUIObjectBoxView::WHITE_CONFIG)
->setForm($form)
->setFormErrors($errors);
$remove_form = id(new AphrontFormView())
->setUser($viewer);
if ($vcspassword->getID()) {
$remove_form
->addHiddenInput('remove', true)
->appendRemarkupInstructions(
pht(
'You can remove your VCS password, which will prevent your '.
'account from accessing repositories.'))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Remove Password')));
} else {
$remove_form->appendRemarkupInstructions(
pht(
'You do not currently have a VCS password set. If you set one, you '.
'can remove it here later.'));
}
$remove_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Remove VCS Password'))
->setBackground(PHUIObjectBoxView::WHITE_CONFIG)
->setForm($remove_form);
$saved = null;
if ($request->getBool('saved')) {
$saved = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
->setTitle(pht('Password Updated'))
->appendChild(pht('Your VCS password has been updated.'));
}
return array(
$saved,
$object_box,
$remove_box,
);
}
}
diff --git a/src/applications/oauthserver/panel/PhabricatorOAuthServerAuthorizationsSettingsPanel.php b/src/applications/oauthserver/panel/PhabricatorOAuthServerAuthorizationsSettingsPanel.php
index 37e85ab53b..89a1cc0281 100644
--- a/src/applications/oauthserver/panel/PhabricatorOAuthServerAuthorizationsSettingsPanel.php
+++ b/src/applications/oauthserver/panel/PhabricatorOAuthServerAuthorizationsSettingsPanel.php
@@ -1,143 +1,147 @@
<?php
final class PhabricatorOAuthServerAuthorizationsSettingsPanel
extends PhabricatorSettingsPanel {
public function getPanelKey() {
return 'oauthorizations';
}
public function getPanelName() {
return pht('OAuth Authorizations');
}
+ public function getPanelMenuIcon() {
+ return 'fa-exchange';
+ }
+
public function getPanelGroupKey() {
return PhabricatorSettingsLogsPanelGroup::PANELGROUPKEY;
}
public function isEnabled() {
return PhabricatorApplication::isClassInstalled(
'PhabricatorOAuthServerApplication');
}
public function processRequest(AphrontRequest $request) {
$viewer = $request->getUser();
// TODO: It would be nice to simply disable this panel, but we can't do
// viewer-based checks for enabled panels right now.
$app_class = 'PhabricatorOAuthServerApplication';
$installed = PhabricatorApplication::isClassInstalledForViewer(
$app_class,
$viewer);
if (!$installed) {
$dialog = id(new AphrontDialogView())
->setUser($viewer)
->setTitle(pht('OAuth Not Available'))
->appendParagraph(
pht('You do not have access to OAuth authorizations.'))
->addCancelButton('/settings/');
return id(new AphrontDialogResponse())->setDialog($dialog);
}
$authorizations = id(new PhabricatorOAuthClientAuthorizationQuery())
->setViewer($viewer)
->withUserPHIDs(array($viewer->getPHID()))
->execute();
$authorizations = mpull($authorizations, null, 'getID');
$panel_uri = $this->getPanelURI();
$revoke = $request->getInt('revoke');
if ($revoke) {
if (empty($authorizations[$revoke])) {
return new Aphront404Response();
}
if ($request->isFormPost()) {
$authorizations[$revoke]->delete();
return id(new AphrontRedirectResponse())->setURI($panel_uri);
}
$dialog = id(new AphrontDialogView())
->setUser($viewer)
->setTitle(pht('Revoke Authorization?'))
->appendParagraph(
pht(
'This application will no longer be able to access Phabricator '.
'on your behalf.'))
->addSubmitButton(pht('Revoke Authorization'))
->addCancelButton($panel_uri);
return id(new AphrontDialogResponse())->setDialog($dialog);
}
$highlight = $request->getInt('id');
$rows = array();
$rowc = array();
foreach ($authorizations as $authorization) {
if ($highlight == $authorization->getID()) {
$rowc[] = 'highlighted';
} else {
$rowc[] = null;
}
$button = javelin_tag(
'a',
array(
'href' => $this->getPanelURI('?revoke='.$authorization->getID()),
'class' => 'small button button-grey',
'sigil' => 'workflow',
),
pht('Revoke'));
$rows[] = array(
phutil_tag(
'a',
array(
'href' => $authorization->getClient()->getViewURI(),
),
$authorization->getClient()->getName()),
$authorization->getScopeString(),
phabricator_datetime($authorization->getDateCreated(), $viewer),
phabricator_datetime($authorization->getDateModified(), $viewer),
$button,
);
}
$table = new AphrontTableView($rows);
$table->setNoDataString(
pht("You haven't authorized any OAuth applications."));
$table->setRowClasses($rowc);
$table->setHeaders(
array(
pht('Application'),
pht('Scope'),
pht('Created'),
pht('Updated'),
null,
));
$table->setColumnClasses(
array(
'pri',
'wide',
'right',
'right',
'action',
));
$header = id(new PHUIHeaderView())
->setHeader(pht('OAuth Application Authorizations'));
$panel = id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::WHITE_CONFIG)
->appendChild($table);
return $panel;
}
}
diff --git a/src/applications/settings/controller/PhabricatorSettingsMainController.php b/src/applications/settings/controller/PhabricatorSettingsMainController.php
index 46246c3ce5..ded20a8e96 100644
--- a/src/applications/settings/controller/PhabricatorSettingsMainController.php
+++ b/src/applications/settings/controller/PhabricatorSettingsMainController.php
@@ -1,238 +1,242 @@
<?php
final class PhabricatorSettingsMainController
extends PhabricatorController {
private $user;
private $builtinKey;
private $preferences;
private function getUser() {
return $this->user;
}
private function isSelf() {
$user = $this->getUser();
if (!$user) {
return false;
}
$user_phid = $user->getPHID();
$viewer_phid = $this->getViewer()->getPHID();
return ($viewer_phid == $user_phid);
}
private function isTemplate() {
return ($this->builtinKey !== null);
}
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
// Redirect "/panel/XYZ/" to the viewer's personal settings panel. This
// was the primary URI before global settings were introduced and allows
// generation of viewer-agnostic URIs for email and logged-out users.
$panel = $request->getURIData('panel');
if ($panel) {
$panel = phutil_escape_uri($panel);
$username = $viewer->getUsername();
$panel_uri = "/user/{$username}/page/{$panel}/";
$panel_uri = $this->getApplicationURI($panel_uri);
return id(new AphrontRedirectResponse())->setURI($panel_uri);
}
$username = $request->getURIData('username');
$builtin = $request->getURIData('builtin');
$key = $request->getURIData('pageKey');
if ($builtin) {
$this->builtinKey = $builtin;
$preferences = id(new PhabricatorUserPreferencesQuery())
->setViewer($viewer)
->withBuiltinKeys(array($builtin))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$preferences) {
$preferences = id(new PhabricatorUserPreferences())
->attachUser(null)
->setBuiltinKey($builtin);
}
} else {
$user = id(new PhabricatorPeopleQuery())
->setViewer($viewer)
->withUsernames(array($username))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$user) {
return new Aphront404Response();
}
$preferences = PhabricatorUserPreferences::loadUserPreferences($user);
$this->user = $user;
}
if (!$preferences) {
return new Aphront404Response();
}
PhabricatorPolicyFilter::requireCapability(
$viewer,
$preferences,
PhabricatorPolicyCapability::CAN_EDIT);
$this->preferences = $preferences;
$panels = $this->buildPanels($preferences);
$nav = $this->renderSideNav($panels);
$key = $nav->selectFilter($key, head($panels)->getPanelKey());
$panel = $panels[$key]
->setController($this)
->setNavigation($nav);
$response = $panel->processRequest($request);
if (($response instanceof AphrontResponse) ||
($response instanceof AphrontResponseProducerInterface)) {
return $response;
}
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($panel->getPanelName());
$crumbs->setBorder(true);
if ($this->user) {
$header_text = pht('Edit Settings: %s', $user->getUserName());
} else {
$header_text = pht('Edit Global Settings');
}
$header = id(new PHUIHeaderView())
->setHeader($header_text);
$title = $panel->getPanelName();
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter($response);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($nav)
->appendChild($view);
}
private function buildPanels(PhabricatorUserPreferences $preferences) {
$viewer = $this->getViewer();
$panels = PhabricatorSettingsPanel::getAllDisplayPanels();
$result = array();
foreach ($panels as $key => $panel) {
$panel
->setPreferences($preferences)
->setViewer($viewer);
if ($this->user) {
$panel->setUser($this->user);
}
if (!$panel->isEnabled()) {
continue;
}
if ($this->isTemplate()) {
if (!$panel->isTemplatePanel()) {
continue;
}
} else {
if (!$this->isSelf() && !$panel->isManagementPanel()) {
continue;
}
if ($this->isSelf() && !$panel->isUserPanel()) {
continue;
}
}
if (!empty($result[$key])) {
throw new Exception(pht(
"Two settings panels share the same panel key ('%s'): %s, %s.",
$key,
get_class($panel),
get_class($result[$key])));
}
$result[$key] = $panel;
}
if (!$result) {
throw new Exception(pht('No settings panels are available.'));
}
return $result;
}
private function renderSideNav(array $panels) {
$nav = new AphrontSideNavFilterView();
if ($this->isTemplate()) {
$base_uri = 'builtin/'.$this->builtinKey.'/page/';
} else {
$user = $this->getUser();
$base_uri = 'user/'.$user->getUsername().'/page/';
}
$nav->setBaseURI(new PhutilURI($this->getApplicationURI($base_uri)));
$group_key = null;
foreach ($panels as $panel) {
if ($panel->getPanelGroupKey() != $group_key) {
$group_key = $panel->getPanelGroupKey();
$group = $panel->getPanelGroup();
$panel_name = $group->getPanelGroupName();
if ($panel_name) {
$nav->addLabel($panel_name);
}
}
- $nav->addFilter($panel->getPanelKey(), $panel->getPanelName());
+ $nav->addFilter(
+ $panel->getPanelKey(),
+ $panel->getPanelName(),
+ null,
+ $panel->getPanelMenuIcon());
}
return $nav;
}
public function buildApplicationMenu() {
if ($this->preferences) {
$panels = $this->buildPanels($this->preferences);
return $this->renderSideNav($panels)->getMenu();
}
return parent::buildApplicationMenu();
}
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
$user = $this->getUser();
if (!$this->isSelf() && $user) {
$username = $user->getUsername();
$crumbs->addTextCrumb($username, "/p/{$username}/");
}
return $crumbs;
}
}
diff --git a/src/applications/settings/panel/PhabricatorActivitySettingsPanel.php b/src/applications/settings/panel/PhabricatorActivitySettingsPanel.php
index 2759f3a26c..a3654a4388 100644
--- a/src/applications/settings/panel/PhabricatorActivitySettingsPanel.php
+++ b/src/applications/settings/panel/PhabricatorActivitySettingsPanel.php
@@ -1,46 +1,50 @@
<?php
final class PhabricatorActivitySettingsPanel extends PhabricatorSettingsPanel {
public function getPanelKey() {
return 'activity';
}
public function getPanelName() {
return pht('Activity Logs');
}
+ public function getPanelMenuIcon() {
+ return 'fa-list';
+ }
+
public function getPanelGroupKey() {
return PhabricatorSettingsLogsPanelGroup::PANELGROUPKEY;
}
public function processRequest(AphrontRequest $request) {
$viewer = $request->getUser();
$user = $this->getUser();
$pager = id(new AphrontCursorPagerView())
->readFromRequest($request);
$logs = id(new PhabricatorPeopleLogQuery())
->setViewer($viewer)
->withRelatedPHIDs(array($user->getPHID()))
->executeWithCursorPager($pager);
$table = id(new PhabricatorUserLogView())
->setUser($viewer)
->setLogs($logs);
$panel = $this->newBox(pht('Account Activity Logs'), $table);
$pager_box = id(new PHUIBoxView())
->addMargin(PHUI::MARGIN_LARGE)
->appendChild($pager);
return array($panel, $pager_box);
}
public function isManagementPanel() {
return true;
}
}
diff --git a/src/applications/settings/panel/PhabricatorConpherencePreferencesSettingsPanel.php b/src/applications/settings/panel/PhabricatorConpherencePreferencesSettingsPanel.php
index 6ed6325d67..3ce72af2f8 100644
--- a/src/applications/settings/panel/PhabricatorConpherencePreferencesSettingsPanel.php
+++ b/src/applications/settings/panel/PhabricatorConpherencePreferencesSettingsPanel.php
@@ -1,20 +1,24 @@
<?php
final class PhabricatorConpherencePreferencesSettingsPanel
extends PhabricatorEditEngineSettingsPanel {
const PANELKEY = 'conpherence';
public function getPanelName() {
return pht('Conpherence');
}
+ public function getPanelMenuIcon() {
+ return 'fa-comment-o';
+ }
+
public function getPanelGroupKey() {
return PhabricatorSettingsApplicationsPanelGroup::PANELGROUPKEY;
}
public function isTemplatePanel() {
return true;
}
}
diff --git a/src/applications/settings/panel/PhabricatorContactNumbersSettingsPanel.php b/src/applications/settings/panel/PhabricatorContactNumbersSettingsPanel.php
index 0bfe747ec3..7834bff593 100644
--- a/src/applications/settings/panel/PhabricatorContactNumbersSettingsPanel.php
+++ b/src/applications/settings/panel/PhabricatorContactNumbersSettingsPanel.php
@@ -1,69 +1,73 @@
<?php
final class PhabricatorContactNumbersSettingsPanel
extends PhabricatorSettingsPanel {
public function getPanelKey() {
return 'contact';
}
public function getPanelName() {
return pht('Contact Numbers');
}
+ public function getPanelMenuIcon() {
+ return 'fa-mobile';
+ }
+
public function getPanelGroupKey() {
return PhabricatorSettingsAuthenticationPanelGroup::PANELGROUPKEY;
}
public function processRequest(AphrontRequest $request) {
$user = $this->getUser();
$viewer = $request->getUser();
$numbers = id(new PhabricatorAuthContactNumberQuery())
->setViewer($viewer)
->withObjectPHIDs(array($user->getPHID()))
->execute();
$rows = array();
foreach ($numbers as $number) {
$rows[] = array(
$number->newIconView(),
phutil_tag(
'a',
array(
'href' => $number->getURI(),
),
$number->getDisplayName()),
phabricator_datetime($number->getDateCreated(), $viewer),
);
}
$table = id(new AphrontTableView($rows))
->setNoDataString(
pht("You haven't added any contact numbers to your account."))
->setHeaders(
array(
null,
pht('Number'),
pht('Created'),
))
->setColumnClasses(
array(
null,
'wide pri',
'right',
));
$buttons = array();
$buttons[] = id(new PHUIButtonView())
->setTag('a')
->setIcon('fa-plus')
->setText(pht('Add Contact Number'))
->setHref('/auth/contact/edit/')
->setColor(PHUIButtonView::GREY);
return $this->newBox(pht('Contact Numbers'), $table, $buttons);
}
}
diff --git a/src/applications/settings/panel/PhabricatorDateTimeSettingsPanel.php b/src/applications/settings/panel/PhabricatorDateTimeSettingsPanel.php
index e5ca46510e..285bc6989f 100644
--- a/src/applications/settings/panel/PhabricatorDateTimeSettingsPanel.php
+++ b/src/applications/settings/panel/PhabricatorDateTimeSettingsPanel.php
@@ -1,24 +1,28 @@
<?php
final class PhabricatorDateTimeSettingsPanel
extends PhabricatorEditEngineSettingsPanel {
const PANELKEY = 'datetime';
public function getPanelName() {
return pht('Date and Time');
}
+ public function getPanelMenuIcon() {
+ return 'fa-calendar';
+ }
+
public function getPanelGroupKey() {
return PhabricatorSettingsAccountPanelGroup::PANELGROUPKEY;
}
public function isManagementPanel() {
return true;
}
public function isTemplatePanel() {
return true;
}
}
diff --git a/src/applications/settings/panel/PhabricatorDeveloperPreferencesSettingsPanel.php b/src/applications/settings/panel/PhabricatorDeveloperPreferencesSettingsPanel.php
index 384f7e3be9..e6ed8e7564 100644
--- a/src/applications/settings/panel/PhabricatorDeveloperPreferencesSettingsPanel.php
+++ b/src/applications/settings/panel/PhabricatorDeveloperPreferencesSettingsPanel.php
@@ -1,20 +1,24 @@
<?php
final class PhabricatorDeveloperPreferencesSettingsPanel
extends PhabricatorEditEngineSettingsPanel {
const PANELKEY = 'developer';
public function getPanelName() {
return pht('Developer Settings');
}
+ public function getPanelMenuIcon() {
+ return 'fa-magic';
+ }
+
public function getPanelGroupKey() {
return PhabricatorSettingsDeveloperPanelGroup::PANELGROUPKEY;
}
public function isTemplatePanel() {
return true;
}
}
diff --git a/src/applications/settings/panel/PhabricatorDiffPreferencesSettingsPanel.php b/src/applications/settings/panel/PhabricatorDiffPreferencesSettingsPanel.php
index 2e055c3408..acb7f50541 100644
--- a/src/applications/settings/panel/PhabricatorDiffPreferencesSettingsPanel.php
+++ b/src/applications/settings/panel/PhabricatorDiffPreferencesSettingsPanel.php
@@ -1,20 +1,24 @@
<?php
final class PhabricatorDiffPreferencesSettingsPanel
extends PhabricatorEditEngineSettingsPanel {
const PANELKEY = 'diff';
public function getPanelName() {
return pht('Diff Preferences');
}
+ public function getPanelMenuIcon() {
+ return 'fa-cog';
+ }
+
public function getPanelGroupKey() {
return PhabricatorSettingsApplicationsPanelGroup::PANELGROUPKEY;
}
public function isTemplatePanel() {
return true;
}
}
diff --git a/src/applications/settings/panel/PhabricatorDisplayPreferencesSettingsPanel.php b/src/applications/settings/panel/PhabricatorDisplayPreferencesSettingsPanel.php
index 6033ef79e9..7c17a9fea5 100644
--- a/src/applications/settings/panel/PhabricatorDisplayPreferencesSettingsPanel.php
+++ b/src/applications/settings/panel/PhabricatorDisplayPreferencesSettingsPanel.php
@@ -1,20 +1,24 @@
<?php
final class PhabricatorDisplayPreferencesSettingsPanel
extends PhabricatorEditEngineSettingsPanel {
const PANELKEY = 'display';
public function getPanelName() {
return pht('Display Preferences');
}
+ public function getPanelMenuIcon() {
+ return 'fa-desktop';
+ }
+
public function getPanelGroupKey() {
return PhabricatorSettingsApplicationsPanelGroup::PANELGROUPKEY;
}
public function isTemplatePanel() {
return true;
}
}
diff --git a/src/applications/settings/panel/PhabricatorEmailAddressesSettingsPanel.php b/src/applications/settings/panel/PhabricatorEmailAddressesSettingsPanel.php
index cd1bfba540..1b69adcd62 100644
--- a/src/applications/settings/panel/PhabricatorEmailAddressesSettingsPanel.php
+++ b/src/applications/settings/panel/PhabricatorEmailAddressesSettingsPanel.php
@@ -1,411 +1,415 @@
<?php
final class PhabricatorEmailAddressesSettingsPanel
extends PhabricatorSettingsPanel {
public function getPanelKey() {
return 'email';
}
public function getPanelName() {
return pht('Email Addresses');
}
+ public function getPanelMenuIcon() {
+ return 'fa-at';
+ }
+
public function getPanelGroupKey() {
return PhabricatorSettingsEmailPanelGroup::PANELGROUPKEY;
}
public function isEditableByAdministrators() {
if ($this->getUser()->getIsMailingList()) {
return true;
}
return false;
}
public function processRequest(AphrontRequest $request) {
$user = $this->getUser();
$editable = PhabricatorEnv::getEnvConfig('account.editable');
$uri = $request->getRequestURI();
$uri->setQueryParams(array());
if ($editable) {
$new = $request->getStr('new');
if ($new) {
return $this->returnNewAddressResponse($request, $uri, $new);
}
$delete = $request->getInt('delete');
if ($delete) {
return $this->returnDeleteAddressResponse($request, $uri, $delete);
}
}
$verify = $request->getInt('verify');
if ($verify) {
return $this->returnVerifyAddressResponse($request, $uri, $verify);
}
$primary = $request->getInt('primary');
if ($primary) {
return $this->returnPrimaryAddressResponse($request, $uri, $primary);
}
$emails = id(new PhabricatorUserEmail())->loadAllWhere(
'userPHID = %s ORDER BY address',
$user->getPHID());
$rowc = array();
$rows = array();
foreach ($emails as $email) {
$button_verify = javelin_tag(
'a',
array(
'class' => 'button small button-grey',
'href' => $uri->alter('verify', $email->getID()),
'sigil' => 'workflow',
),
pht('Verify'));
$button_make_primary = javelin_tag(
'a',
array(
'class' => 'button small button-grey',
'href' => $uri->alter('primary', $email->getID()),
'sigil' => 'workflow',
),
pht('Make Primary'));
$button_remove = javelin_tag(
'a',
array(
'class' => 'button small button-grey',
'href' => $uri->alter('delete', $email->getID()),
'sigil' => 'workflow',
),
pht('Remove'));
$button_primary = phutil_tag(
'a',
array(
'class' => 'button small disabled',
),
pht('Primary'));
if (!$email->getIsVerified()) {
$action = $button_verify;
} else if ($email->getIsPrimary()) {
$action = $button_primary;
} else {
$action = $button_make_primary;
}
if ($email->getIsPrimary()) {
$remove = $button_primary;
$rowc[] = 'highlighted';
} else {
$remove = $button_remove;
$rowc[] = null;
}
$rows[] = array(
$email->getAddress(),
$action,
$remove,
);
}
$table = new AphrontTableView($rows);
$table->setHeaders(
array(
pht('Email'),
pht('Status'),
pht('Remove'),
));
$table->setColumnClasses(
array(
'wide',
'action',
'action',
));
$table->setRowClasses($rowc);
$table->setColumnVisibility(
array(
true,
true,
$editable,
));
$buttons = array();
if ($editable) {
$buttons[] = id(new PHUIButtonView())
->setTag('a')
->setIcon('fa-plus')
->setText(pht('Add New Address'))
->setHref($uri->alter('new', 'true'))
->addSigil('workflow')
->setColor(PHUIButtonView::GREY);
}
return $this->newBox(pht('Email Addresses'), $table, $buttons);
}
private function returnNewAddressResponse(
AphrontRequest $request,
PhutilURI $uri,
$new) {
$user = $this->getUser();
$viewer = $this->getViewer();
$token = id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession(
$viewer,
$request,
$this->getPanelURI());
$e_email = true;
$email = null;
$errors = array();
if ($request->isDialogFormPost()) {
$email = trim($request->getStr('email'));
if ($new == 'verify') {
// The user clicked "Done" from the "an email has been sent" dialog.
return id(new AphrontReloadResponse())->setURI($uri);
}
PhabricatorSystemActionEngine::willTakeAction(
array($viewer->getPHID()),
new PhabricatorSettingsAddEmailAction(),
1);
if (!strlen($email)) {
$e_email = pht('Required');
$errors[] = pht('Email is required.');
} else if (!PhabricatorUserEmail::isValidAddress($email)) {
$e_email = pht('Invalid');
$errors[] = PhabricatorUserEmail::describeValidAddresses();
} else if (!PhabricatorUserEmail::isAllowedAddress($email)) {
$e_email = pht('Disallowed');
$errors[] = PhabricatorUserEmail::describeAllowedAddresses();
}
if ($e_email === true) {
$application_email = id(new PhabricatorMetaMTAApplicationEmailQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withAddresses(array($email))
->executeOne();
if ($application_email) {
$e_email = pht('In Use');
$errors[] = $application_email->getInUseMessage();
}
}
if (!$errors) {
$object = id(new PhabricatorUserEmail())
->setAddress($email)
->setIsVerified(0);
// If an administrator is editing a mailing list, automatically verify
// the address.
if ($viewer->getPHID() != $user->getPHID()) {
if ($viewer->getIsAdmin()) {
$object->setIsVerified(1);
}
}
try {
id(new PhabricatorUserEditor())
->setActor($viewer)
->addEmail($user, $object);
if ($object->getIsVerified()) {
// If we autoverified the address, just reload the page.
return id(new AphrontReloadResponse())->setURI($uri);
}
$object->sendVerificationEmail($user);
$dialog = $this->newDialog()
->addHiddenInput('new', 'verify')
->setTitle(pht('Verification Email Sent'))
->appendChild(phutil_tag('p', array(), pht(
'A verification email has been sent. Click the link in the '.
'email to verify your address.')))
->setSubmitURI($uri)
->addSubmitButton(pht('Done'));
return id(new AphrontDialogResponse())->setDialog($dialog);
} catch (AphrontDuplicateKeyQueryException $ex) {
$e_email = pht('Duplicate');
$errors[] = pht('Another user already has this email.');
}
}
}
if ($errors) {
$errors = id(new PHUIInfoView())
->setErrors($errors);
}
$form = id(new PHUIFormLayoutView())
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Email'))
->setName('email')
->setValue($email)
->setCaption(PhabricatorUserEmail::describeAllowedAddresses())
->setError($e_email));
$dialog = $this->newDialog()
->addHiddenInput('new', 'true')
->setTitle(pht('New Address'))
->appendChild($errors)
->appendChild($form)
->addSubmitButton(pht('Save'))
->addCancelButton($uri);
return id(new AphrontDialogResponse())->setDialog($dialog);
}
private function returnDeleteAddressResponse(
AphrontRequest $request,
PhutilURI $uri,
$email_id) {
$user = $this->getUser();
$viewer = $this->getViewer();
$token = id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession(
$viewer,
$request,
$this->getPanelURI());
// NOTE: You can only delete your own email addresses, and you can not
// delete your primary address.
$email = id(new PhabricatorUserEmail())->loadOneWhere(
'id = %d AND userPHID = %s AND isPrimary = 0',
$email_id,
$user->getPHID());
if (!$email) {
return new Aphront404Response();
}
if ($request->isFormPost()) {
id(new PhabricatorUserEditor())
->setActor($viewer)
->removeEmail($user, $email);
return id(new AphrontRedirectResponse())->setURI($uri);
}
$address = $email->getAddress();
$dialog = id(new AphrontDialogView())
->setUser($viewer)
->addHiddenInput('delete', $email_id)
->setTitle(pht("Really delete address '%s'?", $address))
->appendParagraph(
pht(
'Are you sure you want to delete this address? You will no '.
'longer be able to use it to login.'))
->appendParagraph(
pht(
'Note: Removing an email address from your account will invalidate '.
'any outstanding password reset links.'))
->addSubmitButton(pht('Delete'))
->addCancelButton($uri);
return id(new AphrontDialogResponse())->setDialog($dialog);
}
private function returnVerifyAddressResponse(
AphrontRequest $request,
PhutilURI $uri,
$email_id) {
$user = $this->getUser();
$viewer = $this->getViewer();
// NOTE: You can only send more email for your unverified addresses.
$email = id(new PhabricatorUserEmail())->loadOneWhere(
'id = %d AND userPHID = %s AND isVerified = 0',
$email_id,
$user->getPHID());
if (!$email) {
return new Aphront404Response();
}
if ($request->isFormPost()) {
$email->sendVerificationEmail($user);
return id(new AphrontRedirectResponse())->setURI($uri);
}
$address = $email->getAddress();
$dialog = id(new AphrontDialogView())
->setUser($viewer)
->addHiddenInput('verify', $email_id)
->setTitle(pht('Send Another Verification Email?'))
->appendChild(phutil_tag('p', array(), pht(
'Send another copy of the verification email to %s?',
$address)))
->addSubmitButton(pht('Send Email'))
->addCancelButton($uri);
return id(new AphrontDialogResponse())->setDialog($dialog);
}
private function returnPrimaryAddressResponse(
AphrontRequest $request,
PhutilURI $uri,
$email_id) {
$user = $this->getUser();
$viewer = $this->getViewer();
$token = id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession(
$viewer,
$request,
$this->getPanelURI());
// NOTE: You can only make your own verified addresses primary.
$email = id(new PhabricatorUserEmail())->loadOneWhere(
'id = %d AND userPHID = %s AND isVerified = 1 AND isPrimary = 0',
$email_id,
$user->getPHID());
if (!$email) {
return new Aphront404Response();
}
if ($request->isFormPost()) {
id(new PhabricatorUserEditor())
->setActor($viewer)
->changePrimaryEmail($user, $email);
return id(new AphrontRedirectResponse())->setURI($uri);
}
$address = $email->getAddress();
$dialog = id(new AphrontDialogView())
->setUser($viewer)
->addHiddenInput('primary', $email_id)
->setTitle(pht('Change primary email address?'))
->appendParagraph(
pht(
'If you change your primary address, Phabricator will send all '.
'email to %s.',
$address))
->appendParagraph(
pht(
'Note: Changing your primary email address will invalidate any '.
'outstanding password reset links.'))
->addSubmitButton(pht('Change Primary Address'))
->addCancelButton($uri);
return id(new AphrontDialogResponse())->setDialog($dialog);
}
}
diff --git a/src/applications/settings/panel/PhabricatorEmailDeliverySettingsPanel.php b/src/applications/settings/panel/PhabricatorEmailDeliverySettingsPanel.php
index 86260c1b5a..55932aa49b 100644
--- a/src/applications/settings/panel/PhabricatorEmailDeliverySettingsPanel.php
+++ b/src/applications/settings/panel/PhabricatorEmailDeliverySettingsPanel.php
@@ -1,28 +1,32 @@
<?php
final class PhabricatorEmailDeliverySettingsPanel
extends PhabricatorEditEngineSettingsPanel {
const PANELKEY = 'emaildelivery';
public function getPanelName() {
return pht('Email Delivery');
}
+ public function getPanelMenuIcon() {
+ return 'fa-envelope-o';
+ }
+
public function getPanelGroupKey() {
return PhabricatorSettingsEmailPanelGroup::PANELGROUPKEY;
}
public function isManagementPanel() {
if ($this->getUser()->getIsMailingList()) {
return true;
}
return false;
}
public function isTemplatePanel() {
return true;
}
}
diff --git a/src/applications/settings/panel/PhabricatorEmailFormatSettingsPanel.php b/src/applications/settings/panel/PhabricatorEmailFormatSettingsPanel.php
index 51ff40ed9d..5a4a707a05 100644
--- a/src/applications/settings/panel/PhabricatorEmailFormatSettingsPanel.php
+++ b/src/applications/settings/panel/PhabricatorEmailFormatSettingsPanel.php
@@ -1,39 +1,32 @@
<?php
final class PhabricatorEmailFormatSettingsPanel
extends PhabricatorEditEngineSettingsPanel {
const PANELKEY = 'emailformat';
public function getPanelName() {
return pht('Email Format');
}
+ public function getPanelMenuIcon() {
+ return 'fa-font';
+ }
+
public function getPanelGroupKey() {
return PhabricatorSettingsEmailPanelGroup::PANELGROUPKEY;
}
public function isUserPanel() {
return PhabricatorMetaMTAMail::shouldMailEachRecipient();
}
public function isManagementPanel() {
return false;
-/*
- if (!$this->isUserPanel()) {
- return false;
- }
-
- if ($this->getUser()->getIsMailingList()) {
- return true;
- }
-
- return false;
-*/
}
public function isTemplatePanel() {
return true;
}
}
diff --git a/src/applications/settings/panel/PhabricatorEmailPreferencesSettingsPanel.php b/src/applications/settings/panel/PhabricatorEmailPreferencesSettingsPanel.php
index faa79889ed..defee73393 100644
--- a/src/applications/settings/panel/PhabricatorEmailPreferencesSettingsPanel.php
+++ b/src/applications/settings/panel/PhabricatorEmailPreferencesSettingsPanel.php
@@ -1,213 +1,217 @@
<?php
final class PhabricatorEmailPreferencesSettingsPanel
extends PhabricatorSettingsPanel {
public function getPanelKey() {
return 'emailpreferences';
}
public function getPanelName() {
return pht('Email Preferences');
}
+ public function getPanelMenuIcon() {
+ return 'fa-envelope-open-o';
+ }
+
public function getPanelGroupKey() {
return PhabricatorSettingsEmailPanelGroup::PANELGROUPKEY;
}
public function isManagementPanel() {
if ($this->getUser()->getIsMailingList()) {
return true;
}
return false;
}
public function isTemplatePanel() {
return true;
}
public function processRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$user = $this->getUser();
$preferences = $this->getPreferences();
$value_email = PhabricatorEmailTagsSetting::VALUE_EMAIL;
$errors = array();
if ($request->isFormPost()) {
$new_tags = $request->getArr('mailtags');
$mailtags = $preferences->getPreference('mailtags', array());
$all_tags = $this->getAllTags($user);
foreach ($all_tags as $key => $label) {
$mailtags[$key] = (int)idx($new_tags, $key, $value_email);
}
$this->writeSetting(
$preferences,
PhabricatorEmailTagsSetting::SETTINGKEY,
$mailtags);
return id(new AphrontRedirectResponse())
->setURI($this->getPanelURI('?saved=true'));
}
$mailtags = $preferences->getSettingValue(
PhabricatorEmailTagsSetting::SETTINGKEY);
$form = id(new AphrontFormView())
->setUser($viewer);
$form->appendRemarkupInstructions(
pht(
'You can adjust **Application Settings** here to customize when '.
'you are emailed and notified.'.
"\n\n".
"| Setting | Effect\n".
"| ------- | -------\n".
"| Email | You will receive an email and a notification, but the ".
"notification will be marked \"read\".\n".
"| Notify | You will receive an unread notification only.\n".
"| Ignore | You will receive nothing.\n".
"\n\n".
'If an update makes several changes (like adding CCs to a task, '.
'closing it, and adding a comment) you will receive the strongest '.
'notification any of the changes is configured to deliver.'.
"\n\n".
'These preferences **only** apply to objects you are connected to '.
'(for example, Revisions where you are a reviewer or tasks you are '.
'CC\'d on). To receive email alerts when other objects are created, '.
'configure [[ /herald/ | Herald Rules ]].'));
$editors = $this->getAllEditorsWithTags($user);
// Find all the tags shared by more than one application, and put them
// in a "common" group.
$all_tags = array();
foreach ($editors as $editor) {
foreach ($editor->getMailTagsMap() as $tag => $name) {
if (empty($all_tags[$tag])) {
$all_tags[$tag] = array(
'count' => 0,
'name' => $name,
);
}
$all_tags[$tag]['count'];
}
}
$common_tags = array();
foreach ($all_tags as $tag => $info) {
if ($info['count'] > 1) {
$common_tags[$tag] = $info['name'];
}
}
// Build up the groups of application-specific options.
$tag_groups = array();
foreach ($editors as $editor) {
$tag_groups[] = array(
$editor->getEditorObjectsDescription(),
array_diff_key($editor->getMailTagsMap(), $common_tags),
);
}
// Sort them, then put "Common" at the top.
$tag_groups = isort($tag_groups, 0);
if ($common_tags) {
array_unshift($tag_groups, array(pht('Common'), $common_tags));
}
// Finally, build the controls.
foreach ($tag_groups as $spec) {
list($label, $map) = $spec;
$control = $this->buildMailTagControl($label, $map, $mailtags);
$form->appendChild($control);
}
$form
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Save Preferences')));
$form_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Email Preferences'))
->setFormSaved($request->getStr('saved'))
->setFormErrors($errors)
->setBackground(PHUIObjectBoxView::WHITE_CONFIG)
->setForm($form);
return $form_box;
}
private function getAllEditorsWithTags(PhabricatorUser $user = null) {
$editors = id(new PhutilClassMapQuery())
->setAncestorClass('PhabricatorApplicationTransactionEditor')
->setFilterMethod('getMailTagsMap')
->execute();
foreach ($editors as $key => $editor) {
// Remove editors for applications which are not installed.
$app = $editor->getEditorApplicationClass();
if ($app !== null && $user !== null) {
if (!PhabricatorApplication::isClassInstalledForViewer($app, $user)) {
unset($editors[$key]);
}
}
}
return $editors;
}
private function getAllTags(PhabricatorUser $user = null) {
$tags = array();
foreach ($this->getAllEditorsWithTags($user) as $editor) {
$tags += $editor->getMailTagsMap();
}
return $tags;
}
private function buildMailTagControl(
$control_label,
array $tags,
array $prefs) {
$value_email = PhabricatorEmailTagsSetting::VALUE_EMAIL;
$value_notify = PhabricatorEmailTagsSetting::VALUE_NOTIFY;
$value_ignore = PhabricatorEmailTagsSetting::VALUE_IGNORE;
$content = array();
foreach ($tags as $key => $label) {
$select = AphrontFormSelectControl::renderSelectTag(
(int)idx($prefs, $key, $value_email),
array(
$value_email => pht("\xE2\x9A\xAB Email"),
$value_notify => pht("\xE2\x97\x90 Notify"),
$value_ignore => pht("\xE2\x9A\xAA Ignore"),
),
array(
'name' => 'mailtags['.$key.']',
));
$content[] = phutil_tag(
'div',
array(
'class' => 'psb',
),
array(
$select,
' ',
$label,
));
}
$control = new AphrontFormStaticControl();
$control->setLabel($control_label);
$control->setValue($content);
return $control;
}
}
diff --git a/src/applications/settings/panel/PhabricatorExternalAccountsSettingsPanel.php b/src/applications/settings/panel/PhabricatorExternalAccountsSettingsPanel.php
index e380248a83..1215487208 100644
--- a/src/applications/settings/panel/PhabricatorExternalAccountsSettingsPanel.php
+++ b/src/applications/settings/panel/PhabricatorExternalAccountsSettingsPanel.php
@@ -1,137 +1,141 @@
<?php
final class PhabricatorExternalAccountsSettingsPanel
extends PhabricatorSettingsPanel {
public function getPanelKey() {
return 'external';
}
public function getPanelName() {
return pht('External Accounts');
}
+ public function getPanelMenuIcon() {
+ return 'fa-users';
+ }
+
public function getPanelGroupKey() {
return PhabricatorSettingsAuthenticationPanelGroup::PANELGROUPKEY;
}
public function processRequest(AphrontRequest $request) {
$viewer = $request->getUser();
$providers = PhabricatorAuthProvider::getAllProviders();
$accounts = id(new PhabricatorExternalAccountQuery())
->setViewer($viewer)
->withUserPHIDs(array($viewer->getPHID()))
->needImages(true)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->execute();
$linked_head = pht('Linked Accounts and Authentication');
$linked = id(new PHUIObjectItemListView())
->setUser($viewer)
->setNoDataString(pht('You have no linked accounts.'));
$login_accounts = 0;
foreach ($accounts as $account) {
if ($account->isUsableForLogin()) {
$login_accounts++;
}
}
foreach ($accounts as $account) {
$item = new PHUIObjectItemView();
$provider = idx($providers, $account->getProviderKey());
if ($provider) {
$item->setHeader($provider->getProviderName());
$can_unlink = $provider->shouldAllowAccountUnlink();
if (!$can_unlink) {
$item->addAttribute(pht('Permanently Linked'));
}
} else {
$item->setHeader(
pht('Unknown Account ("%s")', $account->getProviderKey()));
$can_unlink = true;
}
$can_login = $account->isUsableForLogin();
if (!$can_login) {
$item->addAttribute(
pht(
'Disabled (an administrator has disabled login for this '.
'account provider).'));
}
$can_unlink = $can_unlink && (!$can_login || ($login_accounts > 1));
$can_refresh = $provider && $provider->shouldAllowAccountRefresh();
if ($can_refresh) {
$item->addAction(
id(new PHUIListItemView())
->setIcon('fa-refresh')
->setHref('/auth/refresh/'.$account->getProviderKey().'/'));
}
$item->addAction(
id(new PHUIListItemView())
->setIcon('fa-times')
->setWorkflow(true)
->setDisabled(!$can_unlink)
->setHref('/auth/unlink/'.$account->getProviderKey().'/'));
if ($provider) {
$provider->willRenderLinkedAccount($viewer, $item, $account);
}
$linked->addItem($item);
}
$linkable_head = pht('Add External Account');
$linkable = id(new PHUIObjectItemListView())
->setUser($viewer)
->setNoDataString(
pht('Your account is linked with all available providers.'));
$accounts = mpull($accounts, null, 'getProviderKey');
$providers = PhabricatorAuthProvider::getAllEnabledProviders();
$providers = msort($providers, 'getProviderName');
foreach ($providers as $key => $provider) {
if (isset($accounts[$key])) {
continue;
}
if (!$provider->shouldAllowAccountLink()) {
continue;
}
$link_uri = '/auth/link/'.$provider->getProviderKey().'/';
$item = id(new PHUIObjectItemView())
->setHeader($provider->getProviderName())
->setHref($link_uri)
->addAction(
id(new PHUIListItemView())
->setIcon('fa-link')
->setHref($link_uri));
$linkable->addItem($item);
}
$linked_box = $this->newBox($linked_head, $linked);
$linkable_box = $this->newBox($linkable_head, $linkable);
return array(
$linked_box,
$linkable_box,
);
}
}
diff --git a/src/applications/settings/panel/PhabricatorLanguageSettingsPanel.php b/src/applications/settings/panel/PhabricatorLanguageSettingsPanel.php
index 9b846bd4b6..65a0be4e79 100644
--- a/src/applications/settings/panel/PhabricatorLanguageSettingsPanel.php
+++ b/src/applications/settings/panel/PhabricatorLanguageSettingsPanel.php
@@ -1,24 +1,28 @@
<?php
final class PhabricatorLanguageSettingsPanel
extends PhabricatorEditEngineSettingsPanel {
const PANELKEY = 'language';
public function getPanelName() {
return pht('Language');
}
+ public function getPanelMenuIcon() {
+ return 'fa-globe';
+ }
+
public function getPanelGroupKey() {
return PhabricatorSettingsAccountPanelGroup::PANELGROUPKEY;
}
public function isManagementPanel() {
return true;
}
public function isTemplatePanel() {
return true;
}
}
diff --git a/src/applications/settings/panel/PhabricatorMultiFactorSettingsPanel.php b/src/applications/settings/panel/PhabricatorMultiFactorSettingsPanel.php
index 06f1547afa..93a95254a9 100644
--- a/src/applications/settings/panel/PhabricatorMultiFactorSettingsPanel.php
+++ b/src/applications/settings/panel/PhabricatorMultiFactorSettingsPanel.php
@@ -1,329 +1,333 @@
<?php
final class PhabricatorMultiFactorSettingsPanel
extends PhabricatorSettingsPanel {
public function getPanelKey() {
return 'multifactor';
}
public function getPanelName() {
return pht('Multi-Factor Auth');
}
+ public function getPanelMenuIcon() {
+ return 'fa-lock';
+ }
+
public function getPanelGroupKey() {
return PhabricatorSettingsAuthenticationPanelGroup::PANELGROUPKEY;
}
public function processRequest(AphrontRequest $request) {
if ($request->getExists('new') || $request->getExists('providerPHID')) {
return $this->processNew($request);
}
if ($request->getExists('edit')) {
return $this->processEdit($request);
}
if ($request->getExists('delete')) {
return $this->processDelete($request);
}
$user = $this->getUser();
$viewer = $request->getUser();
$factors = id(new PhabricatorAuthFactorConfigQuery())
->setViewer($viewer)
->withUserPHIDs(array($user->getPHID()))
->setOrderVector(array('-id'))
->execute();
$rows = array();
$rowc = array();
$highlight_id = $request->getInt('id');
foreach ($factors as $factor) {
$provider = $factor->getFactorProvider();
if ($factor->getID() == $highlight_id) {
$rowc[] = 'highlighted';
} else {
$rowc[] = null;
}
$rows[] = array(
javelin_tag(
'a',
array(
'href' => $this->getPanelURI('?edit='.$factor->getID()),
'sigil' => 'workflow',
),
$factor->getFactorName()),
$provider->getDisplayName(),
phabricator_datetime($factor->getDateCreated(), $viewer),
javelin_tag(
'a',
array(
'href' => $this->getPanelURI('?delete='.$factor->getID()),
'sigil' => 'workflow',
'class' => 'small button button-grey',
),
pht('Remove')),
);
}
$table = new AphrontTableView($rows);
$table->setNoDataString(
pht("You haven't added any authentication factors to your account yet."));
$table->setHeaders(
array(
pht('Name'),
pht('Type'),
pht('Created'),
'',
));
$table->setColumnClasses(
array(
'wide pri',
'',
'right',
'action',
));
$table->setRowClasses($rowc);
$table->setDeviceVisibility(
array(
true,
false,
false,
true,
));
$help_uri = PhabricatorEnv::getDoclink(
'User Guide: Multi-Factor Authentication');
$buttons = array();
$buttons[] = id(new PHUIButtonView())
->setTag('a')
->setIcon('fa-plus')
->setText(pht('Add Auth Factor'))
->setHref($this->getPanelURI('?new=true'))
->setWorkflow(true)
->setColor(PHUIButtonView::GREY);
$buttons[] = id(new PHUIButtonView())
->setTag('a')
->setIcon('fa-book')
->setText(pht('Help'))
->setHref($help_uri)
->setColor(PHUIButtonView::GREY);
return $this->newBox(pht('Authentication Factors'), $table, $buttons);
}
private function processNew(AphrontRequest $request) {
$viewer = $request->getUser();
$user = $this->getUser();
$cancel_uri = $this->getPanelURI();
// Check that we have providers before we send the user through the MFA
// gate, so you don't authenticate and then immediately get roadblocked.
$providers = id(new PhabricatorAuthFactorProviderQuery())
->setViewer($viewer)
->withStatuses(array(PhabricatorAuthFactorProvider::STATUS_ACTIVE))
->execute();
if (!$providers) {
return $this->newDialog()
->setTitle(pht('No MFA Providers'))
->appendParagraph(
pht(
'There are no active MFA providers. At least one active provider '.
'must be available to add new MFA factors.'))
->addCancelButton($cancel_uri);
}
$providers = mpull($providers, null, 'getPHID');
$token = id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession(
$viewer,
$request,
$cancel_uri);
$selected_phid = $request->getStr('providerPHID');
if (empty($providers[$selected_phid])) {
$selected_provider = null;
} else {
$selected_provider = $providers[$selected_phid];
}
if (!$selected_provider) {
$menu = id(new PHUIObjectItemListView())
->setViewer($viewer)
->setBig(true)
->setFlush(true);
foreach ($providers as $provider_phid => $provider) {
$provider_uri = id(new PhutilURI($this->getPanelURI()))
->setQueryParam('providerPHID', $provider_phid);
$item = id(new PHUIObjectItemView())
->setHeader($provider->getDisplayName())
->setHref($provider_uri)
->setClickable(true)
->setImageIcon($provider->newIconView())
->addAttribute($provider->getDisplayDescription());
$menu->addItem($item);
}
return $this->newDialog()
->setTitle(pht('Choose Factor Type'))
->appendChild($menu)
->addCancelButton($cancel_uri);
}
$form = id(new AphrontFormView())
->setViewer($viewer);
$config = $selected_provider->processAddFactorForm(
$form,
$request,
$user);
if ($config) {
$config->save();
$log = PhabricatorUserLog::initializeNewLog(
$viewer,
$user->getPHID(),
PhabricatorUserLog::ACTION_MULTI_ADD);
$log->save();
$user->updateMultiFactorEnrollment();
// Terminate other sessions so they must log in and survive the
// multi-factor auth check.
id(new PhabricatorAuthSessionEngine())->terminateLoginSessions(
$user,
new PhutilOpaqueEnvelope(
$request->getCookie(PhabricatorCookies::COOKIE_SESSION)));
return id(new AphrontRedirectResponse())
->setURI($this->getPanelURI('?id='.$config->getID()));
}
return $this->newDialog()
->addHiddenInput('providerPHID', $selected_provider->getPHID())
->setWidth(AphrontDialogView::WIDTH_FORM)
->setTitle(pht('Add Authentication Factor'))
->appendChild($form->buildLayoutView())
->addSubmitButton(pht('Continue'))
->addCancelButton($cancel_uri);
}
private function processEdit(AphrontRequest $request) {
$viewer = $request->getUser();
$user = $this->getUser();
$factor = id(new PhabricatorAuthFactorConfig())->loadOneWhere(
'id = %d AND userPHID = %s',
$request->getInt('edit'),
$user->getPHID());
if (!$factor) {
return new Aphront404Response();
}
$e_name = true;
$errors = array();
if ($request->isFormPost()) {
$name = $request->getStr('name');
if (!strlen($name)) {
$e_name = pht('Required');
$errors[] = pht(
'Authentication factors must have a name to identify them.');
}
if (!$errors) {
$factor->setFactorName($name);
$factor->save();
$user->updateMultiFactorEnrollment();
return id(new AphrontRedirectResponse())
->setURI($this->getPanelURI('?id='.$factor->getID()));
}
} else {
$name = $factor->getFactorName();
}
$form = id(new AphrontFormView())
->setUser($viewer)
->appendChild(
id(new AphrontFormTextControl())
->setName('name')
->setLabel(pht('Name'))
->setValue($name)
->setError($e_name));
$dialog = id(new AphrontDialogView())
->setUser($viewer)
->addHiddenInput('edit', $factor->getID())
->setTitle(pht('Edit Authentication Factor'))
->setErrors($errors)
->appendChild($form->buildLayoutView())
->addSubmitButton(pht('Save'))
->addCancelButton($this->getPanelURI());
return id(new AphrontDialogResponse())
->setDialog($dialog);
}
private function processDelete(AphrontRequest $request) {
$viewer = $request->getUser();
$user = $this->getUser();
$token = id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession(
$viewer,
$request,
$this->getPanelURI());
$factor = id(new PhabricatorAuthFactorConfig())->loadOneWhere(
'id = %d AND userPHID = %s',
$request->getInt('delete'),
$user->getPHID());
if (!$factor) {
return new Aphront404Response();
}
if ($request->isFormPost()) {
$factor->delete();
$log = PhabricatorUserLog::initializeNewLog(
$viewer,
$user->getPHID(),
PhabricatorUserLog::ACTION_MULTI_REMOVE);
$log->save();
$user->updateMultiFactorEnrollment();
return id(new AphrontRedirectResponse())
->setURI($this->getPanelURI());
}
$dialog = id(new AphrontDialogView())
->setUser($viewer)
->addHiddenInput('delete', $factor->getID())
->setTitle(pht('Delete Authentication Factor'))
->appendParagraph(
pht(
'Really remove the authentication factor %s from your account?',
phutil_tag('strong', array(), $factor->getFactorName())))
->addSubmitButton(pht('Remove Factor'))
->addCancelButton($this->getPanelURI());
return id(new AphrontDialogResponse())
->setDialog($dialog);
}
}
diff --git a/src/applications/settings/panel/PhabricatorNotificationsSettingsPanel.php b/src/applications/settings/panel/PhabricatorNotificationsSettingsPanel.php
index 797bcafcb3..d0165dc3f1 100644
--- a/src/applications/settings/panel/PhabricatorNotificationsSettingsPanel.php
+++ b/src/applications/settings/panel/PhabricatorNotificationsSettingsPanel.php
@@ -1,179 +1,183 @@
<?php
final class PhabricatorNotificationsSettingsPanel
extends PhabricatorSettingsPanel {
public function isEnabled() {
$servers = PhabricatorNotificationServerRef::getEnabledAdminServers();
if (!$servers) {
return false;
}
return PhabricatorApplication::isClassInstalled(
'PhabricatorNotificationsApplication');
}
public function getPanelKey() {
return 'notifications';
}
public function getPanelName() {
return pht('Notifications');
}
+ public function getPanelMenuIcon() {
+ return 'fa-bell-o';
+ }
+
public function getPanelGroupKey() {
return PhabricatorSettingsApplicationsPanelGroup::PANELGROUPKEY;
}
public function processRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$preferences = $this->getPreferences();
$notifications_key = PhabricatorNotificationsSetting::SETTINGKEY;
$notifications_value = $preferences->getSettingValue($notifications_key);
if ($request->isFormPost()) {
$this->writeSetting(
$preferences,
$notifications_key,
$request->getInt($notifications_key));
return id(new AphrontRedirectResponse())
->setURI($this->getPanelURI('?saved=true'));
}
$title = pht('Notifications');
$control_id = celerity_generate_unique_node_id();
$status_id = celerity_generate_unique_node_id();
$browser_status_id = celerity_generate_unique_node_id();
$cancel_ask = pht(
'The dialog asking for permission to send desktop notifications was '.
'closed without granting permission. Only application notifications '.
'will be sent.');
$accept_ask = pht(
'Click "Save Preference" to persist these changes.');
$reject_ask = pht(
'Permission for desktop notifications was denied. Only application '.
'notifications will be sent.');
$no_support = pht(
'This web browser does not support desktop notifications. Only '.
'application notifications will be sent for this browser regardless of '.
'this preference.');
$default_status = phutil_tag(
'span',
array(),
array(
pht('This browser has not yet granted permission to send desktop '.
'notifications for this Phabricator instance.'),
phutil_tag('br'),
phutil_tag('br'),
javelin_tag(
'button',
array(
'sigil' => 'desktop-notifications-permission-button',
'class' => 'green',
),
pht('Grant Permission')),
));
$granted_status = phutil_tag(
'span',
array(),
pht('This browser has been granted permission to send desktop '.
'notifications for this Phabricator instance.'));
$denied_status = phutil_tag(
'span',
array(),
pht('This browser has denied permission to send desktop notifications '.
'for this Phabricator instance. Consult your browser settings / '.
'documentation to figure out how to clear this setting, do so, '.
'and then re-visit this page to grant permission.'));
$message_id = celerity_generate_unique_node_id();
$message_container = phutil_tag(
'span',
array(
'id' => $message_id,
));
$saved_box = null;
if ($request->getBool('saved')) {
$saved_box = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
->appendChild(pht('Changes saved.'));
}
$status_box = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
->setID($status_id)
->setIsHidden(true)
->appendChild($message_container);
$status_box = id(new PHUIBoxView())
->addClass('mll mlr')
->appendChild($status_box);
$control_config = array(
'controlID' => $control_id,
'statusID' => $status_id,
'messageID' => $message_id,
'browserStatusID' => $browser_status_id,
'defaultMode' => 0,
'desktop' => 1,
'desktopOnly' => 2,
'cancelAsk' => $cancel_ask,
'grantedAsk' => $accept_ask,
'deniedAsk' => $reject_ask,
'defaultStatus' => $default_status,
'deniedStatus' => $denied_status,
'grantedStatus' => $granted_status,
'noSupport' => $no_support,
);
$form = id(new AphrontFormView())
->setUser($viewer)
->appendChild(
id(new AphrontFormSelectControl())
->setLabel($title)
->setControlID($control_id)
->setName($notifications_key)
->setValue($notifications_value)
->setOptions(PhabricatorNotificationsSetting::getOptionsMap())
->setCaption(
pht(
'Phabricator can send real-time notifications to your web browser '.
'or to your desktop. Select where you want to receive these '.
'real-time updates.'))
->initBehavior(
'desktop-notifications-control',
$control_config))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Save Preference')));
$button = id(new PHUIButtonView())
->setTag('a')
->setIcon('fa-send-o')
->setWorkflow(true)
->setText(pht('Send Test Notification'))
->setHref('/notification/test/')
->setColor(PHUIButtonView::GREY);
$form_content = array($saved_box, $status_box, $form);
$form_box = $this->newBox(
pht('Notifications'), $form_content, array($button));
$browser_status_box = id(new PHUIInfoView())
->setID($browser_status_id)
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
->setIsHidden(true)
->appendChild($default_status);
return array(
$form_box,
$browser_status_box,
);
}
}
diff --git a/src/applications/settings/panel/PhabricatorPasswordSettingsPanel.php b/src/applications/settings/panel/PhabricatorPasswordSettingsPanel.php
index 79d7610f2f..37393d5d4f 100644
--- a/src/applications/settings/panel/PhabricatorPasswordSettingsPanel.php
+++ b/src/applications/settings/panel/PhabricatorPasswordSettingsPanel.php
@@ -1,218 +1,222 @@
<?php
final class PhabricatorPasswordSettingsPanel extends PhabricatorSettingsPanel {
public function getPanelKey() {
return 'password';
}
public function getPanelName() {
return pht('Password');
}
+ public function getPanelMenuIcon() {
+ return 'fa-key';
+ }
+
public function getPanelGroupKey() {
return PhabricatorSettingsAuthenticationPanelGroup::PANELGROUPKEY;
}
public function isEnabled() {
// There's no sense in showing a change password panel if this install
// doesn't support password authentication.
if (!PhabricatorPasswordAuthProvider::getPasswordProvider()) {
return false;
}
return true;
}
public function processRequest(AphrontRequest $request) {
$viewer = $request->getUser();
$user = $this->getUser();
$content_source = PhabricatorContentSource::newFromRequest($request);
$token = id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession(
$viewer,
$request,
'/settings/');
$min_len = PhabricatorEnv::getEnvConfig('account.minimum-password-length');
$min_len = (int)$min_len;
// NOTE: Users can also change passwords through the separate "set/reset"
// interface which is reached by logging in with a one-time token after
// registration or password reset. If this flow changes, that flow may
// also need to change.
$account_type = PhabricatorAuthPassword::PASSWORD_TYPE_ACCOUNT;
$password_objects = id(new PhabricatorAuthPasswordQuery())
->setViewer($viewer)
->withObjectPHIDs(array($user->getPHID()))
->withPasswordTypes(array($account_type))
->withIsRevoked(false)
->execute();
if ($password_objects) {
$password_object = head($password_objects);
} else {
$password_object = PhabricatorAuthPassword::initializeNewPassword(
$user,
$account_type);
}
$e_old = true;
$e_new = true;
$e_conf = true;
$errors = array();
if ($request->isFormPost()) {
// Rate limit guesses about the old password. This page requires MFA and
// session compromise already, so this is mostly just to stop researchers
// from reporting this as a vulnerability.
PhabricatorSystemActionEngine::willTakeAction(
array($viewer->getPHID()),
new PhabricatorAuthChangePasswordAction(),
1);
$envelope = new PhutilOpaqueEnvelope($request->getStr('old_pw'));
$engine = id(new PhabricatorAuthPasswordEngine())
->setViewer($viewer)
->setContentSource($content_source)
->setPasswordType($account_type)
->setObject($user);
if (!strlen($envelope->openEnvelope())) {
$errors[] = pht('You must enter your current password.');
$e_old = pht('Required');
} else if (!$engine->isValidPassword($envelope)) {
$errors[] = pht('The old password you entered is incorrect.');
$e_old = pht('Invalid');
} else {
$e_old = null;
// Refund the user an action credit for getting the password right.
PhabricatorSystemActionEngine::willTakeAction(
array($viewer->getPHID()),
new PhabricatorAuthChangePasswordAction(),
-1);
}
$pass = $request->getStr('new_pw');
$conf = $request->getStr('conf_pw');
$password_envelope = new PhutilOpaqueEnvelope($pass);
$confirm_envelope = new PhutilOpaqueEnvelope($conf);
try {
$engine->checkNewPassword($password_envelope, $confirm_envelope);
$e_new = null;
$e_conf = null;
} catch (PhabricatorAuthPasswordException $ex) {
$errors[] = $ex->getMessage();
$e_new = $ex->getPasswordError();
$e_conf = $ex->getConfirmError();
}
if (!$errors) {
$password_object
->setPassword($password_envelope, $user)
->save();
$next = $this->getPanelURI('?saved=true');
id(new PhabricatorAuthSessionEngine())->terminateLoginSessions(
$user,
new PhutilOpaqueEnvelope(
$request->getCookie(PhabricatorCookies::COOKIE_SESSION)));
return id(new AphrontRedirectResponse())->setURI($next);
}
}
if ($password_object->getID()) {
try {
$can_upgrade = $password_object->canUpgrade();
} catch (PhabricatorPasswordHasherUnavailableException $ex) {
$can_upgrade = false;
$errors[] = pht(
'Your password is currently hashed using an algorithm which is '.
'no longer available on this install.');
$errors[] = pht(
'Because the algorithm implementation is missing, your password '.
'can not be used or updated.');
$errors[] = pht(
'To set a new password, request a password reset link from the '.
'login screen and then follow the instructions.');
}
if ($can_upgrade) {
$errors[] = pht(
'The strength of your stored password hash can be upgraded. '.
'To upgrade, either: log out and log in using your password; or '.
'change your password.');
}
}
$len_caption = null;
if ($min_len) {
$len_caption = pht('Minimum password length: %d characters.', $min_len);
}
$form = id(new AphrontFormView())
->setViewer($viewer)
->appendChild(
id(new AphrontFormPasswordControl())
->setLabel(pht('Old Password'))
->setError($e_old)
->setName('old_pw'))
->appendChild(
id(new AphrontFormPasswordControl())
->setDisableAutocomplete(true)
->setLabel(pht('New Password'))
->setError($e_new)
->setName('new_pw'))
->appendChild(
id(new AphrontFormPasswordControl())
->setDisableAutocomplete(true)
->setLabel(pht('Confirm Password'))
->setCaption($len_caption)
->setError($e_conf)
->setName('conf_pw'))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Change Password')));
$properties = id(new PHUIPropertyListView());
$properties->addProperty(
pht('Current Algorithm'),
PhabricatorPasswordHasher::getCurrentAlgorithmName(
$password_object->newPasswordEnvelope()));
$properties->addProperty(
pht('Best Available Algorithm'),
PhabricatorPasswordHasher::getBestAlgorithmName());
$info_view = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
->appendChild(
pht('Changing your password will terminate any other outstanding '.
'login sessions.'));
$algo_box = $this->newBox(pht('Password Algorithms'), $properties);
$form_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Change Password'))
->setFormSaved($request->getStr('saved'))
->setFormErrors($errors)
->setBackground(PHUIObjectBoxView::WHITE_CONFIG)
->setForm($form);
return array(
$form_box,
$algo_box,
$info_view,
);
}
}
diff --git a/src/applications/settings/panel/PhabricatorSSHKeysSettingsPanel.php b/src/applications/settings/panel/PhabricatorSSHKeysSettingsPanel.php
index 13944411ed..131f602974 100644
--- a/src/applications/settings/panel/PhabricatorSSHKeysSettingsPanel.php
+++ b/src/applications/settings/panel/PhabricatorSSHKeysSettingsPanel.php
@@ -1,51 +1,55 @@
<?php
final class PhabricatorSSHKeysSettingsPanel extends PhabricatorSettingsPanel {
public function isManagementPanel() {
if ($this->getUser()->getIsMailingList()) {
return false;
}
return true;
}
public function getPanelKey() {
return 'ssh';
}
public function getPanelName() {
return pht('SSH Public Keys');
}
+ public function getPanelMenuIcon() {
+ return 'fa-file-text-o';
+ }
+
public function getPanelGroupKey() {
return PhabricatorSettingsAuthenticationPanelGroup::PANELGROUPKEY;
}
public function processRequest(AphrontRequest $request) {
$user = $this->getUser();
$viewer = $request->getUser();
$keys = id(new PhabricatorAuthSSHKeyQuery())
->setViewer($viewer)
->withObjectPHIDs(array($user->getPHID()))
->withIsActive(true)
->execute();
$table = id(new PhabricatorAuthSSHKeyTableView())
->setUser($viewer)
->setKeys($keys)
->setCanEdit(true)
->setNoDataString(pht("You haven't added any SSH Public Keys."));
$panel = new PHUIObjectBoxView();
$header = new PHUIHeaderView();
$ssh_actions = PhabricatorAuthSSHKeyTableView::newKeyActionsMenu(
$viewer,
$user);
return $this->newBox(pht('SSH Public Keys'), $table, array($ssh_actions));
}
}
diff --git a/src/applications/settings/panel/PhabricatorSessionsSettingsPanel.php b/src/applications/settings/panel/PhabricatorSessionsSettingsPanel.php
index 314d68f69d..fb10572e11 100644
--- a/src/applications/settings/panel/PhabricatorSessionsSettingsPanel.php
+++ b/src/applications/settings/panel/PhabricatorSessionsSettingsPanel.php
@@ -1,139 +1,143 @@
<?php
final class PhabricatorSessionsSettingsPanel extends PhabricatorSettingsPanel {
public function getPanelKey() {
return 'sessions';
}
public function getPanelName() {
return pht('Sessions');
}
+ public function getPanelMenuIcon() {
+ return 'fa-user';
+ }
+
public function getPanelGroupKey() {
return PhabricatorSettingsLogsPanelGroup::PANELGROUPKEY;
}
public function isEnabled() {
return true;
}
public function processRequest(AphrontRequest $request) {
$viewer = $request->getUser();
$accounts = id(new PhabricatorExternalAccountQuery())
->setViewer($viewer)
->withUserPHIDs(array($viewer->getPHID()))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->execute();
$identity_phids = mpull($accounts, 'getPHID');
$identity_phids[] = $viewer->getPHID();
$sessions = id(new PhabricatorAuthSessionQuery())
->setViewer($viewer)
->withIdentityPHIDs($identity_phids)
->execute();
$handles = id(new PhabricatorHandleQuery())
->setViewer($viewer)
->withPHIDs($identity_phids)
->execute();
$current_key = PhabricatorAuthSession::newSessionDigest(
new PhutilOpaqueEnvelope(
$request->getCookie(PhabricatorCookies::COOKIE_SESSION)));
$rows = array();
$rowc = array();
foreach ($sessions as $session) {
$is_current = phutil_hashes_are_identical(
$session->getSessionKey(),
$current_key);
if ($is_current) {
$rowc[] = 'highlighted';
$button = phutil_tag(
'a',
array(
'class' => 'small button button-grey disabled',
),
pht('Current'));
} else {
$rowc[] = null;
$button = javelin_tag(
'a',
array(
'href' => '/auth/session/terminate/'.$session->getID().'/',
'class' => 'small button button-grey',
'sigil' => 'workflow',
),
pht('Terminate'));
}
$hisec = ($session->getHighSecurityUntil() - time());
$rows[] = array(
$handles[$session->getUserPHID()]->renderLink(),
substr($session->getSessionKey(), 0, 6),
$session->getType(),
($hisec > 0)
? phutil_format_relative_time($hisec)
: null,
phabricator_datetime($session->getSessionStart(), $viewer),
phabricator_date($session->getSessionExpires(), $viewer),
$button,
);
}
$table = new AphrontTableView($rows);
$table->setNoDataString(pht("You don't have any active sessions."));
$table->setRowClasses($rowc);
$table->setHeaders(
array(
pht('Identity'),
pht('Session'),
pht('Type'),
pht('HiSec'),
pht('Created'),
pht('Expires'),
pht(''),
));
$table->setColumnClasses(
array(
'wide',
'n',
'',
'right',
'right',
'right',
'action',
));
$buttons = array();
$buttons[] = id(new PHUIButtonView())
->setTag('a')
->setIcon('fa-warning')
->setText(pht('Terminate All Sessions'))
->setHref('/auth/session/terminate/all/')
->setWorkflow(true)
->setColor(PHUIButtonView::RED);
$hisec = ($viewer->getSession()->getHighSecurityUntil() - time());
if ($hisec > 0) {
$buttons[] = id(new PHUIButtonView())
->setTag('a')
->setIcon('fa-lock')
->setText(pht('Leave High Security'))
->setHref('/auth/session/downgrade/')
->setWorkflow(true)
->setColor(PHUIButtonView::RED);
}
return $this->newBox(pht('Active Login Sessions'), $table, $buttons);
}
}
diff --git a/src/applications/settings/panel/PhabricatorSettingsPanel.php b/src/applications/settings/panel/PhabricatorSettingsPanel.php
index 19ac6fec62..8250418812 100644
--- a/src/applications/settings/panel/PhabricatorSettingsPanel.php
+++ b/src/applications/settings/panel/PhabricatorSettingsPanel.php
@@ -1,301 +1,311 @@
<?php
/**
* Defines a settings panel. Settings panels appear in the Settings application,
* and behave like lightweight controllers -- generally, they render some sort
* of form with options in it, and then update preferences when the user
* submits the form. By extending this class, you can add new settings
* panels.
*
* @task config Panel Configuration
* @task panel Panel Implementation
* @task internal Internals
*/
abstract class PhabricatorSettingsPanel extends Phobject {
private $user;
private $viewer;
private $controller;
private $navigation;
private $overrideURI;
private $preferences;
public function setUser(PhabricatorUser $user) {
$this->user = $user;
return $this;
}
public function getUser() {
return $this->user;
}
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
}
public function getViewer() {
return $this->viewer;
}
public function setOverrideURI($override_uri) {
$this->overrideURI = $override_uri;
return $this;
}
final public function setController(PhabricatorController $controller) {
$this->controller = $controller;
return $this;
}
final public function getController() {
return $this->controller;
}
final public function setNavigation(AphrontSideNavFilterView $navigation) {
$this->navigation = $navigation;
return $this;
}
final public function getNavigation() {
return $this->navigation;
}
public function setPreferences(PhabricatorUserPreferences $preferences) {
$this->preferences = $preferences;
return $this;
}
public function getPreferences() {
return $this->preferences;
}
final public static function getAllPanels() {
$panels = id(new PhutilClassMapQuery())
->setAncestorClass(__CLASS__)
->setUniqueMethod('getPanelKey')
->execute();
return msortv($panels, 'getPanelOrderVector');
}
final public static function getAllDisplayPanels() {
$panels = array();
$groups = PhabricatorSettingsPanelGroup::getAllPanelGroupsWithPanels();
foreach ($groups as $group) {
foreach ($group->getPanels() as $key => $panel) {
$panels[$key] = $panel;
}
}
return $panels;
}
final public function getPanelGroup() {
$group_key = $this->getPanelGroupKey();
$groups = PhabricatorSettingsPanelGroup::getAllPanelGroupsWithPanels();
$group = idx($groups, $group_key);
if (!$group) {
throw new Exception(
pht(
'No settings panel group with key "%s" exists!',
$group_key));
}
return $group;
}
/* -( Panel Configuration )------------------------------------------------ */
/**
* Return a unique string used in the URI to identify this panel, like
* "example".
*
* @return string Unique panel identifier (used in URIs).
* @task config
*/
public function getPanelKey() {
return $this->getPhobjectClassConstant('PANELKEY');
}
/**
* Return a human-readable description of the panel's contents, like
* "Example Settings".
*
* @return string Human-readable panel name.
* @task config
*/
abstract public function getPanelName();
+ /**
+ * Return an icon for the panel in the menu.
+ *
+ * @return string Icon identifier.
+ * @task config
+ */
+ public function getPanelMenuIcon() {
+ return 'fa-wrench';
+ }
+
/**
* Return a panel group key constant for this panel.
*
* @return const Panel group key.
* @task config
*/
abstract public function getPanelGroupKey();
/**
* Return false to prevent this panel from being displayed or used. You can
* do, e.g., configuration checks here, to determine if the feature your
* panel controls is unavailable in this install. By default, all panels are
* enabled.
*
* @return bool True if the panel should be shown.
* @task config
*/
public function isEnabled() {
return true;
}
/**
* Return true if this panel is available to users while editing their own
* settings.
*
* @return bool True to enable management on behalf of a user.
* @task config
*/
public function isUserPanel() {
return true;
}
/**
* Return true if this panel is available to administrators while managing
* bot and mailing list accounts.
*
* @return bool True to enable management on behalf of accounts.
* @task config
*/
public function isManagementPanel() {
return false;
}
/**
* Return true if this panel is available while editing settings templates.
*
* @return bool True to allow editing in templates.
* @task config
*/
public function isTemplatePanel() {
return false;
}
/* -( Panel Implementation )----------------------------------------------- */
/**
* Process a user request for this settings panel. Implement this method like
* a lightweight controller. If you return an @{class:AphrontResponse}, the
* response will be used in whole. If you return anything else, it will be
* treated as a view and composed into a normal settings page.
*
* Generally, render your settings panel by returning a form, then return
* a redirect when the user saves settings.
*
* @param AphrontRequest Incoming request.
* @return wild Response to request, either as an
* @{class:AphrontResponse} or something which can
* be composed into a @{class:AphrontView}.
* @task panel
*/
abstract public function processRequest(AphrontRequest $request);
/**
* Get the URI for this panel.
*
* @param string? Optional path to append.
* @return string Relative URI for the panel.
* @task panel
*/
final public function getPanelURI($path = '') {
$path = ltrim($path, '/');
if ($this->overrideURI) {
return rtrim($this->overrideURI, '/').'/'.$path;
}
$key = $this->getPanelKey();
$key = phutil_escape_uri($key);
$user = $this->getUser();
if ($user) {
if ($user->isLoggedIn()) {
$username = $user->getUsername();
return "/settings/user/{$username}/page/{$key}/{$path}";
} else {
// For logged-out users, we can't put their username in the URI. This
// page will prompt them to login, then redirect them to the correct
// location.
return "/settings/panel/{$key}/";
}
} else {
$builtin = $this->getPreferences()->getBuiltinKey();
return "/settings/builtin/{$builtin}/page/{$key}/{$path}";
}
}
/* -( Internals )---------------------------------------------------------- */
/**
* Generates a key to sort the list of panels.
*
* @return string Sortable key.
* @task internal
*/
final public function getPanelOrderVector() {
return id(new PhutilSortVector())
->addString($this->getPanelName());
}
protected function newDialog() {
return $this->getController()->newDialog();
}
protected function writeSetting(
PhabricatorUserPreferences $preferences,
$key,
$value) {
$viewer = $this->getViewer();
$request = $this->getController()->getRequest();
$editor = id(new PhabricatorUserPreferencesEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true);
$xactions = array();
$xactions[] = $preferences->newTransaction($key, $value);
$editor->applyTransactions($preferences, $xactions);
}
public function newBox($title, $content, $actions = array()) {
$header = id(new PHUIHeaderView())
->setHeader($title);
foreach ($actions as $action) {
$header->addActionLink($action);
}
$view = id(new PHUIObjectBoxView())
->setHeader($header)
->appendChild($content)
->setBackground(PHUIObjectBoxView::WHITE_CONFIG);
return $view;
}
}
diff --git a/src/applications/settings/panel/PhabricatorTokensSettingsPanel.php b/src/applications/settings/panel/PhabricatorTokensSettingsPanel.php
index f2021bafa5..91064a432f 100644
--- a/src/applications/settings/panel/PhabricatorTokensSettingsPanel.php
+++ b/src/applications/settings/panel/PhabricatorTokensSettingsPanel.php
@@ -1,85 +1,89 @@
<?php
final class PhabricatorTokensSettingsPanel extends PhabricatorSettingsPanel {
public function getPanelKey() {
return 'tokens';
}
public function getPanelName() {
return pht('Temporary Tokens');
}
+ public function getPanelMenuIcon() {
+ return 'fa-ticket';
+ }
+
public function getPanelGroupKey() {
return PhabricatorSettingsLogsPanelGroup::PANELGROUPKEY;
}
public function processRequest(AphrontRequest $request) {
$viewer = $request->getUser();
$tokens = id(new PhabricatorAuthTemporaryTokenQuery())
->setViewer($viewer)
->withTokenResources(array($viewer->getPHID()))
->execute();
$rows = array();
foreach ($tokens as $token) {
if ($token->isRevocable()) {
$button = javelin_tag(
'a',
array(
'href' => '/auth/token/revoke/'.$token->getID().'/',
'class' => 'small button button-grey',
'sigil' => 'workflow',
),
pht('Revoke'));
} else {
$button = javelin_tag(
'a',
array(
'class' => 'small button button-grey disabled',
),
pht('Revoke'));
}
if ($token->getTokenExpires() >= time()) {
$expiry = phabricator_datetime($token->getTokenExpires(), $viewer);
} else {
$expiry = pht('Expired');
}
$rows[] = array(
$token->getTokenReadableTypeName(),
$expiry,
$button,
);
}
$table = new AphrontTableView($rows);
$table->setNoDataString(pht("You don't have any active tokens."));
$table->setHeaders(
array(
pht('Type'),
pht('Expires'),
pht(''),
));
$table->setColumnClasses(
array(
'wide',
'right',
'action',
));
$button = id(new PHUIButtonView())
->setTag('a')
->setIcon('fa-warning')
->setText(pht('Revoke All'))
->setHref('/auth/token/revoke/all/')
->setWorkflow(true)
->setColor(PHUIButtonView::RED);
return $this->newBox(pht('Temporary Tokens'), $table, array($button));
}
}

File Metadata

Mime Type
text/x-diff
Expires
Wed, Jul 2, 6:53 PM (19 h, 58 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
165104
Default Alt Text
(97 KB)

Event Timeline