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

Configure Feed

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

Improve handling of email verification and "activated" accounts

Summary:
Small step forward which improves existing stuff or lays groudwork for future stuff:

- Currently, to check for email verification, we have to single-query the email address on every page. Instead, denoramlize it into the user object.
- Migrate all the existing users.
- When the user verifies an email, mark them as `isEmailVerified` if the email is their primary email.
- Just make the checks look at the `isEmailVerified` field.
- Add a new check, `isUserActivated()`, to cover email-verified plus disabled. Currently, a non-verified-but-not-disabled user could theoretically use Conduit over SSH, if anyone deployed it. Tighten that up.
- Add an `isApproved` flag, which is always true for now. In a future diff, I want to add a default-on admin approval queue for new accounts, to prevent configuration mistakes. The way it will work is:
- When the queue is enabled, registering users are created with `isApproved = false`.
- Admins are sent an email, "[Phabricator] New User Approval (alincoln)", telling them that a new user is waiting for approval.
- They go to the web UI and approve the user.
- Manually-created accounts are auto-approved.
- The email will have instructions for disabling the queue.

I think this queue will be helpful for new installs and give them peace of mind, and when you go to disable it we have a better opportunity to warn you about exactly what that means.

Generally, I want to improve the default safety of registration, since if you just blindly coast through the path of least resistance right now your install ends up pretty open, and realistically few installs are on VPNs.

Test Plan:
- Ran migration, verified `isEmailVerified` populated correctly.
- Created a new user, checked DB for verified (not verified).
- Verified, checked DB (now verified).
- Used Conduit, People, Diffusion.

Reviewers: btrahan

Reviewed By: btrahan

CC: chad, aran

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

+237 -123
+10
resources/sql/patches/20131112.userverified.1.col.sql
··· 1 + ALTER TABLE {$NAMESPACE}_user.user 2 + ADD isEmailVerified INT UNSIGNED NOT NULL; 3 + 4 + ALTER TABLE {$NAMESPACE}_user.user 5 + ADD isApproved INT UNSIGNED NOT NULL; 6 + 7 + ALTER TABLE {$NAMESPACE}_user.user 8 + ADD KEY `key_approved` (isApproved); 9 + 10 + UPDATE {$NAMESPACE}_user.user SET isApproved = 1;
+33
resources/sql/patches/20131112.userverified.2.mig.php
··· 1 + <?php 2 + 3 + $table = new PhabricatorUser(); 4 + $conn_w = $table->establishConnection('w'); 5 + 6 + foreach (new LiskMigrationIterator($table) as $user) { 7 + $username = $user->getUsername(); 8 + echo "Migrating {$username}...\n"; 9 + if ($user->getIsEmailVerified()) { 10 + // Email already verified. 11 + continue; 12 + } 13 + 14 + $primary = $user->loadPrimaryEmail(); 15 + if (!$primary) { 16 + // No primary email. 17 + continue; 18 + } 19 + 20 + if (!$primary->getIsVerified()) { 21 + // Primary email not verified. 22 + continue; 23 + } 24 + 25 + // Primary email is verified, so mark the account as verified. 26 + queryfx( 27 + $conn_w, 28 + 'UPDATE %T SET isEmailVerified = 1 WHERE id = %d', 29 + $table->getTableName(), 30 + $user->getID()); 31 + } 32 + 33 + echo "Done.\n";
+2 -2
scripts/ssh/ssh-exec.php
··· 38 38 throw new Exception("Invalid username."); 39 39 } 40 40 41 - if ($user->getIsDisabled()) { 42 - throw new Exception("You have been exiled."); 41 + if (!$user->isUserActivated()) { 42 + throw new Exception(pht("Your account is not activated.")); 43 43 } 44 44 45 45 if ($args->getArg('ssh-command')) {
+55 -55
src/__celerity_resource_map__.php
··· 857 857 ), 858 858 'aphront-dialog-view-css' => 859 859 array( 860 - 'uri' => '/res/830fa2de/rsrc/css/aphront/dialog-view.css', 860 + 'uri' => '/res/6b6a41c6/rsrc/css/aphront/dialog-view.css', 861 861 'type' => 'css', 862 862 'requires' => 863 863 array( ··· 1889 1889 ), 1890 1890 'javelin-behavior-maniphest-batch-selector' => 1891 1891 array( 1892 - 'uri' => '/res/423c5f1b/rsrc/js/application/maniphest/behavior-batch-selector.js', 1892 + 'uri' => '/res/a82658b3/rsrc/js/application/maniphest/behavior-batch-selector.js', 1893 1893 'type' => 'js', 1894 1894 'requires' => 1895 1895 array( ··· 1917 1917 ), 1918 1918 'javelin-behavior-maniphest-subpriority-editor' => 1919 1919 array( 1920 - 'uri' => '/res/1fa4961f/rsrc/js/application/maniphest/behavior-subpriorityeditor.js', 1920 + 'uri' => '/res/95f3d4a6/rsrc/js/application/maniphest/behavior-subpriorityeditor.js', 1921 1921 'type' => 'js', 1922 1922 'requires' => 1923 1923 array( ··· 4328 4328 ), array( 4329 4329 'packages' => 4330 4330 array( 4331 - 'f0d63822' => 4331 + 'd831cac3' => 4332 4332 array( 4333 4333 'name' => 'core.pkg.css', 4334 4334 'symbols' => ··· 4377 4377 41 => 'phabricator-tag-view-css', 4378 4378 42 => 'phui-list-view-css', 4379 4379 ), 4380 - 'uri' => '/res/pkg/f0d63822/core.pkg.css', 4380 + 'uri' => '/res/pkg/d831cac3/core.pkg.css', 4381 4381 'type' => 'css', 4382 4382 ), 4383 4383 '2c1dba03' => ··· 4552 4552 'uri' => '/res/pkg/49898640/maniphest.pkg.css', 4553 4553 'type' => 'css', 4554 4554 ), 4555 - '0a694954' => 4555 + '0474f45c' => 4556 4556 array( 4557 4557 'name' => 'maniphest.pkg.js', 4558 4558 'symbols' => ··· 4563 4563 3 => 'javelin-behavior-maniphest-transaction-expand', 4564 4564 4 => 'javelin-behavior-maniphest-subpriority-editor', 4565 4565 ), 4566 - 'uri' => '/res/pkg/0a694954/maniphest.pkg.js', 4566 + 'uri' => '/res/pkg/0474f45c/maniphest.pkg.js', 4567 4567 'type' => 'js', 4568 4568 ), 4569 4569 ), 4570 4570 'reverse' => 4571 4571 array( 4572 - 'aphront-dialog-view-css' => 'f0d63822', 4573 - 'aphront-error-view-css' => 'f0d63822', 4574 - 'aphront-list-filter-view-css' => 'f0d63822', 4575 - 'aphront-pager-view-css' => 'f0d63822', 4576 - 'aphront-panel-view-css' => 'f0d63822', 4577 - 'aphront-table-view-css' => 'f0d63822', 4578 - 'aphront-tokenizer-control-css' => 'f0d63822', 4579 - 'aphront-tooltip-css' => 'f0d63822', 4580 - 'aphront-typeahead-control-css' => 'f0d63822', 4572 + 'aphront-dialog-view-css' => 'd831cac3', 4573 + 'aphront-error-view-css' => 'd831cac3', 4574 + 'aphront-list-filter-view-css' => 'd831cac3', 4575 + 'aphront-pager-view-css' => 'd831cac3', 4576 + 'aphront-panel-view-css' => 'd831cac3', 4577 + 'aphront-table-view-css' => 'd831cac3', 4578 + 'aphront-tokenizer-control-css' => 'd831cac3', 4579 + 'aphront-tooltip-css' => 'd831cac3', 4580 + 'aphront-typeahead-control-css' => 'd831cac3', 4581 4581 'differential-changeset-view-css' => '1084b12b', 4582 4582 'differential-core-view-css' => '1084b12b', 4583 4583 'differential-inline-comment-editor' => '5e9e5c4e', ··· 4591 4591 'differential-table-of-contents-css' => '1084b12b', 4592 4592 'diffusion-commit-view-css' => '7aa115b4', 4593 4593 'diffusion-icons-css' => '7aa115b4', 4594 - 'global-drag-and-drop-css' => 'f0d63822', 4594 + 'global-drag-and-drop-css' => 'd831cac3', 4595 4595 'inline-comment-summary-css' => '1084b12b', 4596 4596 'javelin-aphlict' => '2c1dba03', 4597 4597 'javelin-behavior' => '3e3be199', ··· 4623 4623 'javelin-behavior-konami' => '2c1dba03', 4624 4624 'javelin-behavior-lightbox-attachments' => '2c1dba03', 4625 4625 'javelin-behavior-load-blame' => '5e9e5c4e', 4626 - 'javelin-behavior-maniphest-batch-selector' => '0a694954', 4627 - 'javelin-behavior-maniphest-subpriority-editor' => '0a694954', 4628 - 'javelin-behavior-maniphest-transaction-controls' => '0a694954', 4629 - 'javelin-behavior-maniphest-transaction-expand' => '0a694954', 4630 - 'javelin-behavior-maniphest-transaction-preview' => '0a694954', 4626 + 'javelin-behavior-maniphest-batch-selector' => '0474f45c', 4627 + 'javelin-behavior-maniphest-subpriority-editor' => '0474f45c', 4628 + 'javelin-behavior-maniphest-transaction-controls' => '0474f45c', 4629 + 'javelin-behavior-maniphest-transaction-expand' => '0474f45c', 4630 + 'javelin-behavior-maniphest-transaction-preview' => '0474f45c', 4631 4631 'javelin-behavior-phabricator-active-nav' => '2c1dba03', 4632 4632 'javelin-behavior-phabricator-autofocus' => '2c1dba03', 4633 4633 'javelin-behavior-phabricator-gesture' => '2c1dba03', ··· 4666 4666 'javelin-util' => '3e3be199', 4667 4667 'javelin-vector' => '3e3be199', 4668 4668 'javelin-workflow' => '3e3be199', 4669 - 'lightbox-attachment-css' => 'f0d63822', 4669 + 'lightbox-attachment-css' => 'd831cac3', 4670 4670 'maniphest-task-summary-css' => '49898640', 4671 - 'phabricator-action-list-view-css' => 'f0d63822', 4672 - 'phabricator-application-launch-view-css' => 'f0d63822', 4671 + 'phabricator-action-list-view-css' => 'd831cac3', 4672 + 'phabricator-application-launch-view-css' => 'd831cac3', 4673 4673 'phabricator-busy' => '2c1dba03', 4674 4674 'phabricator-content-source-view-css' => '1084b12b', 4675 - 'phabricator-core-css' => 'f0d63822', 4676 - 'phabricator-crumbs-view-css' => 'f0d63822', 4675 + 'phabricator-core-css' => 'd831cac3', 4676 + 'phabricator-crumbs-view-css' => 'd831cac3', 4677 4677 'phabricator-drag-and-drop-file-upload' => '5e9e5c4e', 4678 4678 'phabricator-dropdown-menu' => '2c1dba03', 4679 4679 'phabricator-file-upload' => '2c1dba03', 4680 - 'phabricator-filetree-view-css' => 'f0d63822', 4681 - 'phabricator-flag-css' => 'f0d63822', 4680 + 'phabricator-filetree-view-css' => 'd831cac3', 4681 + 'phabricator-flag-css' => 'd831cac3', 4682 4682 'phabricator-hovercard' => '2c1dba03', 4683 - 'phabricator-jump-nav' => 'f0d63822', 4683 + 'phabricator-jump-nav' => 'd831cac3', 4684 4684 'phabricator-keyboard-shortcut' => '2c1dba03', 4685 4685 'phabricator-keyboard-shortcut-manager' => '2c1dba03', 4686 - 'phabricator-main-menu-view' => 'f0d63822', 4686 + 'phabricator-main-menu-view' => 'd831cac3', 4687 4687 'phabricator-menu-item' => '2c1dba03', 4688 - 'phabricator-nav-view-css' => 'f0d63822', 4688 + 'phabricator-nav-view-css' => 'd831cac3', 4689 4689 'phabricator-notification' => '2c1dba03', 4690 - 'phabricator-notification-css' => 'f0d63822', 4691 - 'phabricator-notification-menu-css' => 'f0d63822', 4690 + 'phabricator-notification-css' => 'd831cac3', 4691 + 'phabricator-notification-menu-css' => 'd831cac3', 4692 4692 'phabricator-object-selector-css' => '1084b12b', 4693 4693 'phabricator-phtize' => '2c1dba03', 4694 4694 'phabricator-prefab' => '2c1dba03', 4695 4695 'phabricator-project-tag-css' => '49898640', 4696 - 'phabricator-remarkup-css' => 'f0d63822', 4696 + 'phabricator-remarkup-css' => 'd831cac3', 4697 4697 'phabricator-shaped-request' => '5e9e5c4e', 4698 - 'phabricator-side-menu-view-css' => 'f0d63822', 4699 - 'phabricator-standard-page-view' => 'f0d63822', 4700 - 'phabricator-tag-view-css' => 'f0d63822', 4698 + 'phabricator-side-menu-view-css' => 'd831cac3', 4699 + 'phabricator-standard-page-view' => 'd831cac3', 4700 + 'phabricator-tag-view-css' => 'd831cac3', 4701 4701 'phabricator-textareautils' => '2c1dba03', 4702 4702 'phabricator-tooltip' => '2c1dba03', 4703 - 'phabricator-transaction-view-css' => 'f0d63822', 4704 - 'phabricator-zindex-css' => 'f0d63822', 4705 - 'phui-button-css' => 'f0d63822', 4706 - 'phui-form-css' => 'f0d63822', 4707 - 'phui-form-view-css' => 'f0d63822', 4708 - 'phui-header-view-css' => 'f0d63822', 4709 - 'phui-icon-view-css' => 'f0d63822', 4710 - 'phui-list-view-css' => 'f0d63822', 4711 - 'phui-object-item-list-view-css' => 'f0d63822', 4712 - 'phui-property-list-view-css' => 'f0d63822', 4713 - 'phui-spacing-css' => 'f0d63822', 4714 - 'sprite-apps-large-css' => 'f0d63822', 4715 - 'sprite-gradient-css' => 'f0d63822', 4716 - 'sprite-icons-css' => 'f0d63822', 4717 - 'sprite-menu-css' => 'f0d63822', 4718 - 'sprite-status-css' => 'f0d63822', 4719 - 'syntax-highlighting-css' => 'f0d63822', 4703 + 'phabricator-transaction-view-css' => 'd831cac3', 4704 + 'phabricator-zindex-css' => 'd831cac3', 4705 + 'phui-button-css' => 'd831cac3', 4706 + 'phui-form-css' => 'd831cac3', 4707 + 'phui-form-view-css' => 'd831cac3', 4708 + 'phui-header-view-css' => 'd831cac3', 4709 + 'phui-icon-view-css' => 'd831cac3', 4710 + 'phui-list-view-css' => 'd831cac3', 4711 + 'phui-object-item-list-view-css' => 'd831cac3', 4712 + 'phui-property-list-view-css' => 'd831cac3', 4713 + 'phui-spacing-css' => 'd831cac3', 4714 + 'sprite-apps-large-css' => 'd831cac3', 4715 + 'sprite-gradient-css' => 'd831cac3', 4716 + 'sprite-icons-css' => 'd831cac3', 4717 + 'sprite-menu-css' => 'd831cac3', 4718 + 'sprite-status-css' => 'd831cac3', 4719 + 'syntax-highlighting-css' => 'd831cac3', 4720 4720 ), 4721 4721 ));
+14 -2
src/applications/auth/controller/PhabricatorEmailVerificationController.php
··· 39 39 $continue = pht('Continue to Phabricator'); 40 40 } else { 41 41 $guard = AphrontWriteGuard::beginScopedUnguardedWrites(); 42 - $email->setIsVerified(1); 43 - $email->save(); 42 + $email->openTransaction(); 43 + 44 + $email->setIsVerified(1); 45 + $email->save(); 46 + 47 + // If the user just verified their primary email address, mark their 48 + // account as email verified. 49 + $user_primary = $user->loadPrimaryEmail(); 50 + if ($user_primary->getID() == $email->getID()) { 51 + $user->setIsEmailVerified(1); 52 + $user->save(); 53 + } 54 + 55 + $email->saveTransaction(); 44 56 unset($guard); 45 57 46 58 $title = pht('Address Verified');
+17 -26
src/applications/auth/controller/PhabricatorMustVerifyEmailController.php
··· 31 31 $sent = new AphrontErrorView(); 32 32 $sent->setSeverity(AphrontErrorView::SEVERITY_NOTICE); 33 33 $sent->setTitle(pht('Email Sent')); 34 - $sent->appendChild(phutil_tag( 35 - 'p', 36 - array(), 34 + $sent->appendChild( 37 35 pht( 38 36 'Another verification email was sent to %s.', 39 - phutil_tag('strong', array(), $email_address)))); 37 + phutil_tag('strong', array(), $email_address))); 40 38 } 41 39 42 - $error_view = new AphrontRequestFailureView(); 43 - $error_view->setHeader(pht('Check Your Email')); 44 - $error_view->appendChild(phutil_tag('p', array(), pht( 45 - 'You must verify your email address to login. You should have a new '. 46 - 'email message from Phabricator with verification instructions in your '. 47 - 'inbox (%s).', phutil_tag('strong', array(), $email_address)))); 48 - $error_view->appendChild(phutil_tag('p', array(), pht( 40 + $must_verify = pht( 41 + 'You must verify your email address to login. You should have a '. 42 + 'new email message from Phabricator with verification instructions '. 43 + 'in your inbox (%s).', 44 + phutil_tag('strong', array(), $email_address)); 45 + 46 + $send_again = pht( 49 47 'If you did not receive an email, you can click the button below '. 50 - 'to try sending another one.'))); 51 - $error_view->appendChild(phutil_tag_div( 52 - 'aphront-failure-continue', 53 - phabricator_form( 54 - $user, 55 - array( 56 - 'action' => '/login/mustverify/', 57 - 'method' => 'POST', 58 - ), 59 - phutil_tag( 60 - 'button', 61 - array( 62 - ), 63 - pht('Send Another Email'))))); 48 + 'to try sending another one.'); 64 49 50 + $dialog = id(new AphrontDialogView()) 51 + ->setUser($user) 52 + ->setTitle(pht('Check Your Email')) 53 + ->appendParagraph($must_verify) 54 + ->appendParagraph($send_again) 55 + ->addSubmitButton(pht('Send Another Email')); 65 56 66 57 return $this->buildApplicationPage( 67 58 array( 68 59 $sent, 69 - $error_view, 60 + $dialog, 70 61 ), 71 62 array( 72 63 'title' => pht('Must Verify Email'),
+1 -6
src/applications/base/controller/PhabricatorController.php
··· 114 114 115 115 if ($user->isLoggedIn()) { 116 116 if ($this->shouldRequireEmailVerification()) { 117 - $email = $user->loadPrimaryEmail(); 118 - if (!$email) { 119 - throw new Exception( 120 - "No primary email address associated with this account!"); 121 - } 122 - if (!$email->getIsVerified()) { 117 + if (!$user->getIsEmailVerified()) { 123 118 $controller = new PhabricatorMustVerifyEmailController($request); 124 119 $this->setCurrentApplication($auth_application); 125 120 return $this->delegateToController($controller);
+1 -1
src/applications/base/controller/__tests__/PhabricatorAccessControlTestCase.php
··· 31 31 $u_unverified = $this->generateNewTestUser() 32 32 ->setUsername('unverified') 33 33 ->save(); 34 - $u_unverified->loadPrimaryEmail()->setIsVerified(0)->save(); 34 + $u_unverified->setIsEmailVerified(0)->save(); 35 35 36 36 $u_normal = $this->generateNewTestUser() 37 37 ->setUsername('normal')
+3 -16
src/applications/conduit/controller/PhabricatorConduitAPIController.php
··· 313 313 ConduitAPIRequest $request, 314 314 PhabricatorUser $user) { 315 315 316 - if ($user->getIsDisabled()) { 316 + if (!$user->isUserActivated()) { 317 317 return array( 318 318 'ERR-USER-DISABLED', 319 - 'User is disabled.'); 320 - } 321 - 322 - if (PhabricatorUserEmail::isEmailVerificationRequired()) { 323 - $email = $user->loadPrimaryEmail(); 324 - if (!$email) { 325 - return array( 326 - 'ERR-USER-NOEMAIL', 327 - 'User has no primary email address.'); 328 - } 329 - if (!$email->getIsVerified()) { 330 - return array( 331 - 'ERR-USER-UNVERIFIED', 332 - 'User has unverified email address.'); 333 - } 319 + pht('User account is not activated.'), 320 + ); 334 321 } 335 322 336 323 $request->setUser($user);
+5 -5
src/applications/diffusion/controller/DiffusionServeController.php
··· 382 382 return null; 383 383 } 384 384 385 + if (!$user->isUserActivated()) { 386 + // User is not activated. 387 + return null; 388 + } 389 + 385 390 $password_entry = id(new PhabricatorRepositoryVCSPassword()) 386 391 ->loadOneWhere('userPHID = %s', $user->getPHID()); 387 392 if (!$password_entry) { ··· 391 396 392 397 if (!$password_entry->comparePassword($password, $user)) { 393 398 // Password doesn't match. 394 - return null; 395 - } 396 - 397 - if ($user->getIsDisabled()) { 398 - // User is disabled. 399 399 return null; 400 400 } 401 401
+1 -1
src/applications/herald/query/HeraldRuleQuery.php
··· 219 219 $rule->attachValidAuthor(false); 220 220 continue; 221 221 } 222 - if ($users[$author_phid]->getIsDisabled()) { 222 + if (!$users[$author_phid]->isUserActivated()) { 223 223 $rule->attachValidAuthor(false); 224 224 continue; 225 225 }
+5
src/applications/metamta/query/PhabricatorMetaMTAActorQuery.php
··· 82 82 $actor->setUndeliverable( 83 83 pht('This user is a bot; bot accounts do not receive mail.')); 84 84 } 85 + 86 + // NOTE: We do send email to unapproved users, and to unverified users, 87 + // because it would otherwise be impossible to get them to verify their 88 + // email addresses. Possibly we should white-list this kind of mail and 89 + // deny all other types of mail. 85 90 } 86 91 87 92 $email = idx($emails, $phid);
+2 -5
src/applications/metamta/receiver/PhabricatorMailReceiver.php
··· 19 19 PhabricatorMetaMTAReceivedMail $mail, 20 20 PhabricatorUser $sender) { 21 21 22 - if ($sender->getIsDisabled()) { 22 + if (!$sender->isUserActivated()) { 23 23 throw new PhabricatorMetaMTAReceivedMailProcessingException( 24 24 MetaMTAReceivedMailStatus::STATUS_DISABLED_SENDER, 25 25 pht( 26 - "Sender '%s' has a disabled user account.", 26 + "Sender '%s' does not have an activated user account.", 27 27 $sender->getUsername())); 28 28 } 29 - 30 - 31 - return; 32 29 } 33 30 34 31 /**
+8
src/applications/people/conduit/ConduitAPI_user_Method.php
··· 32 32 $roles[] = 'unverified'; 33 33 } 34 34 35 + if ($user->getIsApproved()) { 36 + $roles[] = 'approved'; 37 + } 38 + 39 + if ($user->isUserActivated()) { 40 + $roles[] = 'activated'; 41 + } 42 + 35 43 $return = array( 36 44 'phid' => $user->getPHID(), 37 45 'userName' => $user->getUserName(),
+3 -1
src/applications/people/controller/PhabricatorPeopleEditController.php
··· 325 325 if ($user->getIsDisabled()) { 326 326 $roles[] = pht('Disabled'); 327 327 } 328 - 328 + if (!$user->getIsApproved()) { 329 + $roles[] = pht('Not Approved'); 330 + } 329 331 if (!$roles) { 330 332 $roles[] = pht('Normal User'); 331 333 }
+4
src/applications/people/controller/PhabricatorPeopleListController.php
··· 61 61 $item->addIcon('disable', pht('Disabled')); 62 62 } 63 63 64 + if (!$user->getIsApproved()) { 65 + $item->addIcon('raise-priority', pht('Not Approved')); 66 + } 67 + 64 68 if ($user->getIsAdmin()) { 65 69 $item->addIcon('highlight', pht('Admin')); 66 70 }
+3
src/applications/people/customfield/PhabricatorUserRolesField.php
··· 31 31 if ($user->getIsDisabled()) { 32 32 $roles[] = pht('Disabled'); 33 33 } 34 + if (!$user->getIsApproved()) { 35 + $roles[] = pht('Not Approved'); 36 + } 34 37 if ($user->getIsSystemAgent()) { 35 38 $roles[] = pht('Bot'); 36 39 }
+6
src/applications/people/editor/PhabricatorUserEditor.php
··· 41 41 // Always set a new user's email address to primary. 42 42 $email->setIsPrimary(1); 43 43 44 + // If the primary address is already verified, also set the verified flag 45 + // on the user themselves. 46 + if ($email->getIsVerified()) { 47 + $user->setIsEmailVerified(1); 48 + } 49 + 44 50 $this->willAddEmail($email); 45 51 46 52 $user->openTransaction();
+2
src/applications/people/event/PhabricatorPeopleHovercardEventListener.php
··· 35 35 36 36 if ($user->getIsDisabled()) { 37 37 $hovercard->addField(pht('Account'), pht('Disabled')); 38 + } else if (!$user->isUserActivated()) { 39 + $hovercard->addField(pht('Account'), pht('Not Activated')); 38 40 } else { 39 41 $statuses = id(new PhabricatorUserStatus())->loadCurrentStatuses( 40 42 array($user->getPHID()));
+1 -1
src/applications/people/phid/PhabricatorPeoplePHIDTypeUser.php
··· 38 38 $handle->setFullName( 39 39 $user->getUsername().' ('.$user->getRealName().')'); 40 40 $handle->setImageURI($user->loadProfileImageURI()); 41 - $handle->setDisabled($user->getIsDisabled()); 41 + $handle->setDisabled(!$user->isUserActivated()); 42 42 if ($user->hasStatus()) { 43 43 $status = $user->getStatus(); 44 44 $handle->setStatus($status->getTextStatus());
+1 -1
src/applications/people/remarkup/PhabricatorRemarkupRuleMention.php
··· 107 107 ->setName('@'.$user->getUserName()) 108 108 ->setHref('/p/'.$user->getUserName().'/'); 109 109 110 - if ($user->getIsDisabled()) { 110 + if (!$user->isUserActivated()) { 111 111 $tag->setDotColor(PhabricatorTagView::COLOR_GREY); 112 112 } else { 113 113 $status = idx($user_statuses, $user->getPHID());
+1 -1
src/applications/people/search/PhabricatorUserSearchIndexer.php
··· 20 20 // TODO: Index the blurbs from their profile or something? Probably not 21 21 // actually useful... 22 22 23 - if (!$user->getIsDisabled()) { 23 + if ($user->isUserActivated()) { 24 24 $doc->addRelationship( 25 25 PhabricatorSearchRelationship::RELATIONSHIP_OPEN, 26 26 $user->getPHID(),
+32
src/applications/people/storage/PhabricatorUser.php
··· 29 29 protected $isSystemAgent = 0; 30 30 protected $isAdmin = 0; 31 31 protected $isDisabled = 0; 32 + protected $isEmailVerified = 0; 33 + protected $isApproved = 1; 32 34 33 35 private $profileImage = null; 34 36 private $profile = null; ··· 51 53 return (bool)$this->isDisabled; 52 54 case 'isSystemAgent': 53 55 return (bool)$this->isSystemAgent; 56 + case 'isEmailVerified': 57 + return (bool)$this->isEmailVerified; 58 + case 'isApproved': 59 + return (bool)$this->isApproved; 54 60 default: 55 61 return parent::readField($field); 56 62 } 63 + } 64 + 65 + 66 + /** 67 + * Is this a live account which has passed required approvals? Returns true 68 + * if this is an enabled, verified (if required), approved (if required) 69 + * account, and false otherwise. 70 + * 71 + * @return bool True if this is a standard, usable account. 72 + */ 73 + public function isUserActivated() { 74 + if ($this->getIsDisabled()) { 75 + return false; 76 + } 77 + 78 + if (!$this->getIsApproved()) { 79 + return false; 80 + } 81 + 82 + if (PhabricatorUserEmail::isEmailVerificationRequired()) { 83 + if (!$this->getIsEmailVerified()) { 84 + return false; 85 + } 86 + } 87 + 88 + return true; 57 89 } 58 90 59 91 public function getConfiguration() {
+8
src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
··· 1748 1748 'type' => 'sql', 1749 1749 'name' => $this->getPatchPath('20131107.buildlog.sql'), 1750 1750 ), 1751 + '20131112.userverified.1.col.sql' => array( 1752 + 'type' => 'sql', 1753 + 'name' => $this->getPatchPath('20131112.userverified.1.col.sql'), 1754 + ), 1755 + '20131112.userverified.2.mig.php' => array( 1756 + 'type' => 'php', 1757 + 'name' => $this->getPatchPath('20131112.userverified.2.mig.php'), 1758 + ), 1751 1759 ); 1752 1760 } 1753 1761 }
+5
src/infrastructure/testing/PhabricatorTestCase.php
··· 105 105 'phabricator.show-beta-applications', 106 106 true); 107 107 108 + // Reset application settings to defaults, particularly policies. 109 + $this->env->overrideEnvConfig( 110 + 'phabricator.application-settings', 111 + array()); 112 + 108 113 // TODO: Remove this when we remove "releeph.installed". 109 114 $this->env->overrideEnvConfig('releeph.installed', true); 110 115 }
+10
src/view/AphrontDialogView.php
··· 111 111 return $this; 112 112 } 113 113 114 + public function appendParagraph($paragraph) { 115 + return $this->appendChild( 116 + phutil_tag( 117 + 'p', 118 + array( 119 + 'class' => 'aphront-dialog-view-paragraph', 120 + ), 121 + $paragraph)); 122 + } 123 + 114 124 final public function render() { 115 125 require_celerity_resource('aphront-dialog-view-css'); 116 126
+4
webroot/rsrc/css/aphront/dialog-view.css
··· 124 124 .aphront-capability-details { 125 125 margin: 20px 0 4px; 126 126 } 127 + 128 + .aphront-dialog-view-paragraph + .aphront-dialog-view-paragraph { 129 + margin-top: 16px; 130 + }