public function appendFieldsToForm(AphrontFormView $form)
 {
     $enabled = array();
     foreach ($this->fields as $field) {
         if ($field->shouldEnableForRole(PhabricatorCustomField::ROLE_EDIT)) {
             $enabled[] = $field;
         }
     }
     $phids = array();
     foreach ($enabled as $field_key => $field) {
         $phids[$field_key] = $field->getRequiredHandlePHIDsForEdit();
     }
     $all_phids = array_mergev($phids);
     if ($all_phids) {
         $handles = id(new PhabricatorHandleQuery())->setViewer($this->viewer)->withPHIDs($all_phids)->execute();
     } else {
         $handles = array();
     }
     foreach ($enabled as $field_key => $field) {
         $field_handles = array_select_keys($handles, $phids[$field_key]);
         $instructions = $field->getInstructionsForEdit();
         if (strlen($instructions)) {
             $form->appendRemarkupInstructions($instructions);
         }
         $form->appendChild($field->renderEditControl($field_handles));
     }
 }
 public function processAddFactorForm(AphrontFormView $form, AphrontRequest $request, PhabricatorUser $user)
 {
     $totp_token_type = PhabricatorAuthTOTPKeyTemporaryTokenType::TOKENTYPE;
     $key = $request->getStr('totpkey');
     if (strlen($key)) {
         // If the user is providing a key, make sure it's a key we generated.
         // This raises the barrier to theoretical attacks where an attacker might
         // provide a known key (such attacks are already prevented by CSRF, but
         // this is a second barrier to overcome).
         // (We store and verify the hash of the key, not the key itself, to limit
         // how useful the data in the table is to an attacker.)
         $temporary_token = id(new PhabricatorAuthTemporaryTokenQuery())->setViewer($user)->withTokenResources(array($user->getPHID()))->withTokenTypes(array($totp_token_type))->withExpired(false)->withTokenCodes(array(PhabricatorHash::digest($key)))->executeOne();
         if (!$temporary_token) {
             // If we don't have a matching token, regenerate the key below.
             $key = null;
         }
     }
     if (!strlen($key)) {
         $key = self::generateNewTOTPKey();
         // Mark this key as one we generated, so the user is allowed to submit
         // a response for it.
         $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
         id(new PhabricatorAuthTemporaryToken())->setTokenResource($user->getPHID())->setTokenType($totp_token_type)->setTokenExpires(time() + phutil_units('1 hour in seconds'))->setTokenCode(PhabricatorHash::digest($key))->save();
         unset($unguarded);
     }
     $code = $request->getStr('totpcode');
     $e_code = true;
     if ($request->getExists('totp')) {
         $okay = self::verifyTOTPCode($user, new PhutilOpaqueEnvelope($key), $code);
         if ($okay) {
             $config = $this->newConfigForUser($user)->setFactorName(pht('Mobile App (TOTP)'))->setFactorSecret($key);
             return $config;
         } else {
             if (!strlen($code)) {
                 $e_code = pht('Required');
             } else {
                 $e_code = pht('Invalid');
             }
         }
     }
     $form->addHiddenInput('totp', true);
     $form->addHiddenInput('totpkey', $key);
     $form->appendRemarkupInstructions(pht('First, download an authenticator application on your phone. Two ' . 'applications which work well are **Authy** and **Google ' . 'Authenticator**, but any other TOTP application should also work.'));
     $form->appendInstructions(pht('Launch the application on your phone, and add a new entry for ' . 'this Phabricator install. When prompted, scan the QR code or ' . 'manually enter the key shown below into the application.'));
     $prod_uri = new PhutilURI(PhabricatorEnv::getProductionURI('/'));
     $issuer = $prod_uri->getDomain();
     $uri = urisprintf('otpauth://totp/%s:%s?secret=%s&issuer=%s', $issuer, $user->getUsername(), $key, $issuer);
     $qrcode = $this->renderQRCode($uri);
     $form->appendChild($qrcode);
     $form->appendChild(id(new AphrontFormStaticControl())->setLabel(pht('Key'))->setValue(phutil_tag('strong', array(), $key)));
     $form->appendInstructions(pht('(If given an option, select that this key is "Time Based", not ' . '"Counter Based".)'));
     $form->appendInstructions(pht('After entering the key, the application should display a numeric ' . 'code. Enter that code below to confirm that you have configured ' . 'the authenticator correctly:'));
     $form->appendChild(id(new PHUIFormNumberControl())->setLabel(pht('TOTP Code'))->setName('totpcode')->setValue($code)->setError($e_code));
 }
 public function appendToForm(AphrontFormView $form)
 {
     return $form->appendRemarkupInstructions($this->getValue());
 }
 public function processRequest(AphrontRequest $request)
 {
     $viewer = $this->getViewer();
     $user = $this->getUser();
     $preferences = $user->loadPreferences();
     $pref_re_prefix = PhabricatorUserPreferences::PREFERENCE_RE_PREFIX;
     $pref_vary = PhabricatorUserPreferences::PREFERENCE_VARY_SUBJECT;
     $prefs_html_email = PhabricatorUserPreferences::PREFERENCE_HTML_EMAILS;
     $errors = array();
     if ($request->isFormPost()) {
         if (PhabricatorMetaMTAMail::shouldMultiplexAllMail()) {
             if ($request->getStr($pref_re_prefix) == 'default') {
                 $preferences->unsetPreference($pref_re_prefix);
             } else {
                 $preferences->setPreference($pref_re_prefix, $request->getBool($pref_re_prefix));
             }
             if ($request->getStr($pref_vary) == 'default') {
                 $preferences->unsetPreference($pref_vary);
             } else {
                 $preferences->setPreference($pref_vary, $request->getBool($pref_vary));
             }
             if ($request->getStr($prefs_html_email) == 'default') {
                 $preferences->unsetPreference($prefs_html_email);
             } else {
                 $preferences->setPreference($prefs_html_email, $request->getBool($prefs_html_email));
             }
         }
         $preferences->save();
         return id(new AphrontRedirectResponse())->setURI($this->getPanelURI('?saved=true'));
     }
     $re_prefix_default = PhabricatorEnv::getEnvConfig('metamta.re-prefix') ? pht('Enabled') : pht('Disabled');
     $vary_default = PhabricatorEnv::getEnvConfig('metamta.vary-subjects') ? pht('Vary') : pht('Do Not Vary');
     $html_emails_default = pht('Plain Text');
     $re_prefix_value = $preferences->getPreference($pref_re_prefix);
     if ($re_prefix_value === null) {
         $re_prefix_value = 'default';
     } else {
         $re_prefix_value = $re_prefix_value ? 'true' : 'false';
     }
     $vary_value = $preferences->getPreference($pref_vary);
     if ($vary_value === null) {
         $vary_value = 'default';
     } else {
         $vary_value = $vary_value ? 'true' : 'false';
     }
     $html_emails_value = $preferences->getPreference($prefs_html_email);
     if ($html_emails_value === null) {
         $html_emails_value = 'default';
     } else {
         $html_emails_value = $html_emails_value ? 'true' : 'false';
     }
     $form = new AphrontFormView();
     $form->setUser($viewer);
     if (PhabricatorMetaMTAMail::shouldMultiplexAllMail()) {
         $html_email_control = id(new AphrontFormSelectControl())->setName($prefs_html_email)->setOptions(array('default' => pht('Default (%s)', $html_emails_default), 'true' => pht('Send HTML Email'), 'false' => pht('Send Plain Text Email')))->setValue($html_emails_value);
         $re_control = id(new AphrontFormSelectControl())->setName($pref_re_prefix)->setOptions(array('default' => pht('Use Server Default (%s)', $re_prefix_default), 'true' => pht('Enable "Re:" prefix'), 'false' => pht('Disable "Re:" prefix')))->setValue($re_prefix_value);
         $vary_control = id(new AphrontFormSelectControl())->setName($pref_vary)->setOptions(array('default' => pht('Use Server Default (%s)', $vary_default), 'true' => pht('Vary Subjects'), 'false' => pht('Do Not Vary Subjects')))->setValue($vary_value);
     } else {
         $html_email_control = id(new AphrontFormStaticControl())->setValue(pht('Server Default (%s)', $html_emails_default));
         $re_control = id(new AphrontFormStaticControl())->setValue(pht('Server Default (%s)', $re_prefix_default));
         $vary_control = id(new AphrontFormStaticControl())->setValue(pht('Server Default (%s)', $vary_default));
     }
     $form->appendRemarkupInstructions(pht('These settings fine-tune some technical aspects of how email is ' . 'formatted. You may be able to adjust them to make mail more ' . 'useful or improve threading.'));
     if (!PhabricatorMetaMTAMail::shouldMultiplexAllMail()) {
         $form->appendRemarkupInstructions(pht('NOTE: This install of Phabricator is configured to send a ' . 'single mail message to all recipients, so all settings are ' . 'locked at the server default value.'));
     }
     $form->appendRemarkupInstructions(pht("You can use the **HTML Email** setting to control whether " . "Phabricator send you HTML email (which has more color and " . "formatting) or plain text email (which is more compatible).\n" . "\n" . "WARNING: This feature is new and experimental! If you enable " . "it, mail may not render properly and replying to mail may not " . "work as well."))->appendChild($html_email_control->setLabel(pht('HTML Email')))->appendRemarkupInstructions('')->appendRemarkupInstructions(pht('The **Add "Re:" Prefix** setting adds "Re:" in front of all ' . 'messages, even if they are not replies. If you use **Mail.app** on ' . 'Mac OS X, this may improve mail threading.' . "\n\n" . "| Setting                | Example Mail Subject\n" . "|------------------------|----------------\n" . "| Enable \"Re:\" Prefix  | " . "`Re: [Differential] [Accepted] D123: Example Revision`\n" . "| Disable \"Re:\" Prefix | " . "`[Differential] [Accepted] D123: Example Revision`"))->appendChild($re_control->setLabel(pht('Add "Re:" Prefix')))->appendRemarkupInstructions('')->appendRemarkupInstructions(pht('With **Vary Subjects** enabled, most mail subject lines will ' . 'include a brief description of their content, like **[Closed]** ' . 'for a notification about someone closing a task.' . "\n\n" . "| Setting              | Example Mail Subject\n" . "|----------------------|----------------\n" . "| Vary Subjects        | " . "`[Maniphest] [Closed] T123: Example Task`\n" . "| Do Not Vary Subjects | " . "`[Maniphest] T123: Example Task`\n" . "\n" . 'This can make mail more useful, but some clients have difficulty ' . 'threading these messages. Disabling this option may improve ' . 'threading, at the cost of less useful subject lines.'))->appendChild($vary_control->setLabel(pht('Vary Subjects')));
     $form->appendChild(id(new AphrontFormSubmitControl())->setValue(pht('Save Preferences')));
     $form_box = id(new PHUIObjectBoxView())->setHeaderText(pht('Email Format'))->setFormSaved($request->getStr('saved'))->setFormErrors($errors)->setForm($form);
     return id(new AphrontNullView())->appendChild(array($form_box));
 }
 public function appendToForm(AphrontFormView $form)
 {
     $control = $this->renderControl();
     if ($control !== null) {
         if ($this->getIsPreview()) {
             if ($this->getIsHidden()) {
                 $control->addClass('aphront-form-preview-hidden')->setError(pht('Hidden'));
             } else {
                 if ($this->getIsLocked()) {
                     $control->setError(pht('Locked'));
                 }
             }
         }
         $instructions = $this->getControlInstructions();
         if (strlen($instructions)) {
             $form->appendRemarkupInstructions($instructions);
         }
         $form->appendControl($control);
     }
     return $this;
 }
 public function extendEditForm(AphrontRequest $request, AphrontFormView $form, array $values, array $issues)
 {
     self::assertLDAPExtensionInstalled();
     $labels = $this->getPropertyLabels();
     $captions = array(self::KEY_HOSTNAME => pht('Example: %s%sFor LDAPS, use: %s', phutil_tag('tt', array(), pht('ldap.example.com')), phutil_tag('br'), phutil_tag('tt', array(), pht('ldaps://ldaps.example.com/'))), self::KEY_DISTINGUISHED_NAME => pht('Example: %s', phutil_tag('tt', array(), pht('ou=People, dc=example, dc=com'))), self::KEY_USERNAME_ATTRIBUTE => pht('Example: %s', phutil_tag('tt', array(), pht('sn'))), self::KEY_REALNAME_ATTRIBUTES => pht('Example: %s', phutil_tag('tt', array(), pht('firstname, lastname'))), self::KEY_REFERRALS => pht('Follow referrals. Disable this for Windows AD 2003.'), self::KEY_START_TLS => pht('Start TLS after binding to the LDAP server.'), self::KEY_ALWAYS_SEARCH => pht('Always bind and search, even without a username and password.'));
     $types = array(self::KEY_REFERRALS => 'checkbox', self::KEY_START_TLS => 'checkbox', self::KEY_SEARCH_ATTRIBUTES => 'textarea', self::KEY_REALNAME_ATTRIBUTES => 'list', self::KEY_ANONYMOUS_PASSWORD => 'password', self::KEY_ALWAYS_SEARCH => 'checkbox');
     $instructions = array(self::KEY_SEARCH_ATTRIBUTES => pht("When a user types their LDAP username and password into Phabricator, " . "Phabricator can either bind to LDAP with those credentials directly " . "(which is simpler, but not as powerful) or bind to LDAP with " . "anonymous credentials, then search for record matching the supplied " . "credentials (which is more complicated, but more powerful).\n\n" . "For many installs, direct binding is sufficient. However, you may " . "want to search first if:\n\n" . "  - You want users to be able to login with either their username " . "    or their email address.\n" . "  - The login/username is not part of the distinguished name in " . "    your LDAP records.\n" . "  - You want to restrict logins to a subset of users (like only " . "    those in certain departments).\n" . "  - Your LDAP server is configured in some other way that prevents " . "    direct binding from working correctly.\n\n" . "**To bind directly**, enter the LDAP attribute corresponding to the " . "login name into the **Search Attributes** box below. Often, this is " . "something like `sn` or `uid`. This is the simplest configuration, " . "but will only work if the username is part of the distinguished " . "name, and won't let you apply complex restrictions to logins.\n\n" . "  lang=text,name=Simple Direct Binding\n" . "  sn\n\n" . "**To search first**, provide an anonymous username and password " . "below (or check the **Always Search** checkbox), then enter one " . "or more search queries into this field, one per line. " . "After binding, these queries will be used to identify the " . "record associated with the login name the user typed.\n\n" . "Searches will be tried in order until a matching record is found. " . "Each query can be a simple attribute name (like `sn` or `mail`), " . "which will search for a matching record, or it can be a complex " . "query that uses the string `\${login}` to represent the login " . "name.\n\n" . "A common simple configuration is just an attribute name, like " . "`sn`, which will work the same way direct binding works:\n\n" . "  lang=text,name=Simple Example\n" . "  sn\n\n" . "A slightly more complex configuration might let the user login with " . "either their login name or email address:\n\n" . "  lang=text,name=Match Several Attributes\n" . "  mail\n" . "  sn\n\n" . "If your LDAP directory is more complex, or you want to perform " . "sophisticated filtering, you can use more complex queries. Depending " . "on your directory structure, this example might allow users to login " . "with either their email address or username, but only if they're in " . "specific departments:\n\n" . "  lang=text,name=Complex Example\n" . "  (&(mail=\${login})(|(departmentNumber=1)(departmentNumber=2)))\n" . "  (&(sn=\${login})(|(departmentNumber=1)(departmentNumber=2)))\n\n" . "All of the attribute names used here are just examples: your LDAP " . "server may use different attribute names."), self::KEY_ALWAYS_SEARCH => pht('To search for an LDAP record before authenticating, either check ' . 'the **Always Search** checkbox or enter an anonymous ' . 'username and password to use to perform the search.'), self::KEY_USERNAME_ATTRIBUTE => pht('Optionally, specify a username attribute to use to prefill usernames ' . 'when registering a new account. This is purely cosmetic and does not ' . 'affect the login process, but you can configure it to make sure ' . 'users get the same default username as their LDAP username, so ' . 'usernames remain consistent across systems.'), self::KEY_REALNAME_ATTRIBUTES => pht('Optionally, specify one or more comma-separated attributes to use to ' . 'prefill the "Real Name" field when registering a new account. This ' . 'is purely cosmetic and does not affect the login process, but can ' . 'make registration a little easier.'));
     foreach ($labels as $key => $label) {
         $caption = idx($captions, $key);
         $type = idx($types, $key);
         $value = idx($values, $key);
         $control = null;
         switch ($type) {
             case 'checkbox':
                 $control = id(new AphrontFormCheckboxControl())->addCheckbox($key, 1, hsprintf('<strong>%s:</strong> %s', $label, $caption), $value);
                 break;
             case 'list':
                 $control = id(new AphrontFormTextControl())->setName($key)->setLabel($label)->setCaption($caption)->setValue($value ? implode(', ', $value) : null);
                 break;
             case 'password':
                 $control = id(new AphrontFormPasswordControl())->setName($key)->setLabel($label)->setCaption($caption)->setDisableAutocomplete(true)->setValue($value);
                 break;
             case 'textarea':
                 $control = id(new AphrontFormTextAreaControl())->setName($key)->setLabel($label)->setCaption($caption)->setValue($value);
                 break;
             default:
                 $control = id(new AphrontFormTextControl())->setName($key)->setLabel($label)->setCaption($caption)->setValue($value);
                 break;
         }
         $instruction_text = idx($instructions, $key);
         if (strlen($instruction_text)) {
             $form->appendRemarkupInstructions($instruction_text);
         }
         $form->appendChild($control);
     }
 }
 public function extendEditForm(AphrontRequest $request, AphrontFormView $form, array $values, array $issues)
 {
     $is_setup = $this->isSetup();
     $e_required = $request->isFormPost() ? null : true;
     $v_name = $values[self::PROPERTY_MEDIAWIKI_NAME];
     if ($is_setup) {
         $e_name = idx($issues, self::PROPERTY_MEDIAWIKI_NAME, $e_required);
     } else {
         $e_name = null;
     }
     $v_uri = $values[self::PROPERTY_MEDIAWIKI_URI];
     $e_uri = idx($issues, self::PROPERTY_MEDIAWIKI_URI, $e_required);
     $config = $this->getProviderConfig();
     if ($is_setup) {
         $form->appendRemarkupInstructions(pht("**MediaWiki Instance Name**\n\n" . "Choose a permanent name for this instance of MediaWiki." . "Phabricator uses this name internally to keep track of " . "this instance of MediaWiki, in case the URL changes later." . "\n\n" . "Use lowercase letters, digits, and period. For example: " . "\n\n`mediawiki`, `mediawiki.mycompany` " . "or `mediawiki.engineering` are reasonable names."))->appendChild(id(new AphrontFormTextControl())->setLabel(pht('MediaWiki Instance Name'))->setValue($v_name)->setName(self::PROPERTY_MEDIAWIKI_NAME)->setError($e_name));
     } else {
         $form->appendChild(id(new AphrontFormTextControl())->setLabel(pht('MediaWiki Instance Name'))->setValue($v_name)->setName(self::PROPERTY_MEDIAWIKI_NAME)->setDisabled(true)->setError($e_name));
     }
     $form->appendChild(id(new AphrontFormTextControl())->setLabel(pht('MediaWiki Base URI'))->setValue($v_uri)->setName(self::PROPERTY_MEDIAWIKI_URI)->setPlaceholder('https://www.mediawiki.org/w')->setCaption(pht('The full URL to your MediaWiki innstall, up to but not including "index.php"'))->setError($e_uri));
     if (!$is_setup) {
         if (!strlen($config->getProperty(self::PROPERTY_CONSUMER_KEY))) {
             $form->appendRemarkupInstructions(pht('NOTE: Copy the keys generated by the MediaWiki OAuth' . ' consumer registration and paste them here.'));
         }
         $form->appendChild(id(new AphrontFormTextControl())->setLabel(pht('Consumer Key'))->setName(self::PROPERTY_CONSUMER_KEY)->setValue($values[self::PROPERTY_CONSUMER_KEY]))->appendChild(id(new AphrontFormTextControl())->setLabel(pht('Secret Key'))->setName(self::PROPERTY_CONSUMER_SECRET)->setValue($values[self::PROPERTY_CONSUMER_SECRET]));
     }
 }
 public function extendEditForm(AphrontRequest $request, AphrontFormView $form, array $values, array $issues)
 {
     if (!function_exists('openssl_pkey_new')) {
         // TODO: This could be a bit prettier.
         throw new Exception(pht("The PHP 'openssl' extension is not installed. You must install " . "this extension in order to add a JIRA authentication provider, " . "because JIRA OAuth requests use the RSA-SHA1 signing algorithm. " . "Install the 'openssl' extension, restart your webserver, and try " . "again."));
     }
     $form->appendRemarkupInstructions(pht('NOTE: This provider **only supports JIRA 6**. It will not work with ' . 'JIRA 5 or earlier.'));
     $is_setup = $this->isSetup();
     $viewer = $request->getViewer();
     $e_required = $request->isFormPost() ? null : true;
     $v_name = $values[self::PROPERTY_JIRA_NAME];
     if ($is_setup) {
         $e_name = idx($issues, self::PROPERTY_JIRA_NAME, $e_required);
     } else {
         $e_name = null;
     }
     $v_uri = $values[self::PROPERTY_JIRA_URI];
     $e_uri = idx($issues, self::PROPERTY_JIRA_URI, $e_required);
     if ($is_setup) {
         $form->appendRemarkupInstructions(pht("**JIRA Instance Name**\n\n" . "Choose a permanent name for this instance of JIRA. Phabricator " . "uses this name internally to keep track of this instance of " . "JIRA, in case the URL changes later.\n\n" . "Use lowercase letters, digits, and period. For example, " . "`jira`, `jira.mycompany` or `jira.engineering` are reasonable " . "names."))->appendChild(id(new AphrontFormTextControl())->setLabel(pht('JIRA Instance Name'))->setValue($v_name)->setName(self::PROPERTY_JIRA_NAME)->setError($e_name));
     } else {
         $form->appendChild(id(new AphrontFormStaticControl())->setLabel(pht('JIRA Instance Name'))->setValue($v_name));
     }
     $form->appendChild(id(new AphrontFormTextControl())->setLabel(pht('JIRA Base URI'))->setValue($v_uri)->setName(self::PROPERTY_JIRA_URI)->setCaption(pht('The URI where JIRA is installed. For example: %s', phutil_tag('tt', array(), 'https://jira.mycompany.com/')))->setError($e_uri));
     if (!$is_setup) {
         $config = $this->getProviderConfig();
         $ckey = $config->getProperty(self::PROPERTY_CONSUMER_KEY);
         $ckey = phutil_tag('tt', array(), $ckey);
         $pkey = $config->getProperty(self::PROPERTY_PUBLIC_KEY);
         $pkey = phutil_escape_html_newlines($pkey);
         $pkey = phutil_tag('tt', array(), $pkey);
         $form->appendRemarkupInstructions(pht('NOTE: **To complete setup**, copy and paste these keys into JIRA ' . 'according to the instructions below.'))->appendChild(id(new AphrontFormStaticControl())->setLabel(pht('Consumer Key'))->setValue($ckey))->appendChild(id(new AphrontFormStaticControl())->setLabel(pht('Public Key'))->setValue($pkey));
         $form->appendRemarkupInstructions(pht('= Integration Options = ' . "\n" . 'Configure how to record Revisions on JIRA tasks.' . "\n\n" . 'Note you\'ll have to restart the daemons for this to take ' . 'effect.'))->appendChild(id(new AphrontFormCheckboxControl())->addCheckbox(self::PROPERTY_REPORT_LINK, 1, new PHUIRemarkupView($viewer, pht('Create **Issue Link** to the Revision, as an "implemented ' . 'in" relationship.')), $this->shouldCreateJIRALink()))->appendChild(id(new AphrontFormCheckboxControl())->addCheckbox(self::PROPERTY_REPORT_COMMENT, 1, new PHUIRemarkupView($viewer, pht('**Post a comment** in the JIRA task, similar to the ' . 'emails Phabricator sends.')), $this->shouldCreateJIRAComment()));
     }
 }
 public function processRequest(AphrontRequest $request)
 {
     $user = $request->getUser();
     $token = id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession($user, $request, '/settings/');
     $min_len = PhabricatorEnv::getEnvConfig('account.minimum-password-length');
     $min_len = (int) $min_len;
     // NOTE: To change your password, you need to prove you own the account,
     // either by providing the old password or by carrying a token to
     // the workflow from a password reset email.
     $key = $request->getStr('key');
     $token = null;
     if ($key) {
         $token = id(new PhabricatorAuthTemporaryTokenQuery())->setViewer($user)->withObjectPHIDs(array($user->getPHID()))->withTokenTypes(array(PhabricatorAuthSessionEngine::PASSWORD_TEMPORARY_TOKEN_TYPE))->withTokenCodes(array(PhabricatorHash::digest($key)))->withExpired(false)->executeOne();
     }
     $e_old = true;
     $e_new = true;
     $e_conf = true;
     $errors = array();
     if ($request->isFormPost()) {
         if (!$token) {
             $envelope = new PhutilOpaqueEnvelope($request->getStr('old_pw'));
             if (!$user->comparePassword($envelope)) {
                 $errors[] = pht('The old password you entered is incorrect.');
                 $e_old = pht('Invalid');
             }
         }
         $pass = $request->getStr('new_pw');
         $conf = $request->getStr('conf_pw');
         if (strlen($pass) < $min_len) {
             $errors[] = pht('Your new password is too short.');
             $e_new = pht('Too Short');
         } else {
             if ($pass !== $conf) {
                 $errors[] = pht('New password and confirmation do not match.');
                 $e_conf = pht('Invalid');
             } else {
                 if (PhabricatorCommonPasswords::isCommonPassword($pass)) {
                     $e_new = pht('Very Weak');
                     $e_conf = pht('Very Weak');
                     $errors[] = pht('Your new password is very weak: it is one of the most common ' . 'passwords in use. Choose a stronger password.');
                 }
             }
         }
         if (!$errors) {
             // This write is unguarded because the CSRF token has already
             // been checked in the call to $request->isFormPost() and
             // the CSRF token depends on the password hash, so when it
             // is changed here the CSRF token check will fail.
             $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
             $envelope = new PhutilOpaqueEnvelope($pass);
             id(new PhabricatorUserEditor())->setActor($user)->changePassword($user, $envelope);
             unset($unguarded);
             if ($token) {
                 // Destroy the token.
                 $token->delete();
                 // If this is a password set/reset, kick the user to the home page
                 // after we update their account.
                 $next = '/';
             } else {
                 $next = $this->getPanelURI('?saved=true');
             }
             id(new PhabricatorAuthSessionEngine())->terminateLoginSessions($user, $request->getCookie(PhabricatorCookies::COOKIE_SESSION));
             return id(new AphrontRedirectResponse())->setURI($next);
         }
     }
     $hash_envelope = new PhutilOpaqueEnvelope($user->getPasswordHash());
     if (strlen($hash_envelope->openEnvelope())) {
         try {
             $can_upgrade = PhabricatorPasswordHasher::canUpgradeHash($hash_envelope);
         } catch (PhabricatorPasswordHasherUnavailableException $ex) {
             $can_upgrade = false;
             // Only show this stuff if we aren't on the reset workflow. We can
             // do resets regardless of the old hasher's availability.
             if (!$token) {
                 $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 = new AphrontFormView();
     $form->setUser($user)->addHiddenInput('key', $key);
     if (!$token) {
         $form->appendChild(id(new AphrontFormPasswordControl())->setLabel(pht('Old Password'))->setError($e_old)->setName('old_pw'));
     }
     $form->appendChild(id(new AphrontFormPasswordControl())->setDisableAutocomplete(true)->setLabel(pht('New Password'))->setError($e_new)->setName('new_pw'));
     $form->appendChild(id(new AphrontFormPasswordControl())->setDisableAutocomplete(true)->setLabel(pht('Confirm Password'))->setCaption($len_caption)->setError($e_conf)->setName('conf_pw'));
     $form->appendChild(id(new AphrontFormSubmitControl())->setValue(pht('Change Password')));
     $form->appendChild(id(new AphrontFormStaticControl())->setLabel(pht('Current Algorithm'))->setValue(PhabricatorPasswordHasher::getCurrentAlgorithmName(new PhutilOpaqueEnvelope($user->getPasswordHash()))));
     $form->appendChild(id(new AphrontFormStaticControl())->setLabel(pht('Best Available Algorithm'))->setValue(PhabricatorPasswordHasher::getBestAlgorithmName()));
     $form->appendRemarkupInstructions(pht('NOTE: Changing your password will terminate any other outstanding ' . 'login sessions.'));
     $form_box = id(new PHUIObjectBoxView())->setHeaderText(pht('Change Password'))->setFormSaved($request->getStr('saved'))->setFormErrors($errors)->setForm($form);
     return array($form_box);
 }
 public function processRequest(AphrontRequest $request)
 {
     $viewer = $this->getViewer();
     $user = $this->getUser();
     $preferences = $user->loadPreferences();
     $pref_no_mail = PhabricatorUserPreferences::PREFERENCE_NO_MAIL;
     $pref_no_self_mail = PhabricatorUserPreferences::PREFERENCE_NO_SELF_MAIL;
     $value_email = PhabricatorUserPreferences::MAILTAG_PREFERENCE_EMAIL;
     $errors = array();
     if ($request->isFormPost()) {
         $preferences->setPreference($pref_no_mail, $request->getStr($pref_no_mail));
         $preferences->setPreference($pref_no_self_mail, $request->getStr($pref_no_self_mail));
         $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);
         }
         $preferences->setPreference('mailtags', $mailtags);
         $preferences->save();
         return id(new AphrontRedirectResponse())->setURI($this->getPanelURI('?saved=true'));
     }
     $form = new AphrontFormView();
     $form->setUser($viewer)->appendRemarkupInstructions(pht('These settings let you control how Phabricator notifies you about ' . 'events. You can configure Phabricator to send you an email, ' . 'just send a web notification, or not notify you at all.'))->appendRemarkupInstructions(pht('If you disable **Email Notifications**, Phabricator will never ' . 'send email to notify you about events. This preference overrides ' . 'all your other settings.' . "\n\n" . "//You may still receive some administrative email, like password " . "reset email.//"))->appendChild(id(new AphrontFormSelectControl())->setLabel(pht('Email Notifications'))->setName($pref_no_mail)->setOptions(array('0' => pht('Send me email notifications'), '1' => pht('Never send email notifications')))->setValue($preferences->getPreference($pref_no_mail, 0)))->appendRemarkupInstructions(pht('If you disable **Self Actions**, Phabricator will not notify ' . 'you about actions you take.'))->appendChild(id(new AphrontFormSelectControl())->setLabel(pht('Self Actions'))->setName($pref_no_self_mail)->setOptions(array('0' => pht('Send me an email when I take an action'), '1' => pht('Do not send me an email when I take an action')))->setValue($preferences->getPreference($pref_no_self_mail, 0)));
     $mailtags = $preferences->getPreference('mailtags', array());
     $form->appendChild(id(new PHUIFormDividerControl()));
     $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)->setForm($form);
     return id(new AphrontNullView())->appendChild(array($form_box));
 }