Index: resources/sql/patches/20131112.userverified.1.col.sql =================================================================== --- /dev/null +++ resources/sql/patches/20131112.userverified.1.col.sql @@ -0,0 +1,10 @@ +ALTER TABLE {$NAMESPACE}_user.user + ADD isEmailVerified INT UNSIGNED NOT NULL; + +ALTER TABLE {$NAMESPACE}_user.user + ADD isApproved INT UNSIGNED NOT NULL; + +ALTER TABLE {$NAMESPACE}_user.user + ADD KEY `key_approved` (isApproved); + +UPDATE {$NAMESPACE}_user.user SET isApproved = 1; Index: resources/sql/patches/20131112.userverified.2.mig.php =================================================================== --- /dev/null +++ resources/sql/patches/20131112.userverified.2.mig.php @@ -0,0 +1,33 @@ +establishConnection('w'); + +foreach (new LiskMigrationIterator($table) as $user) { + $username = $user->getUsername(); + echo "Migrating {$username}...\n"; + if ($user->getIsEmailVerified()) { + // Email already verified. + continue; + } + + $primary = $user->loadPrimaryEmail(); + if (!$primary) { + // No primary email. + continue; + } + + if (!$primary->getIsVerified()) { + // Primary email not verified. + continue; + } + + // Primary email is verified, so mark the account as verified. + queryfx( + $conn_w, + 'UPDATE %T SET isEmailVerified = 1 WHERE id = %d', + $table->getTableName(), + $user->getID()); +} + +echo "Done.\n"; Index: scripts/ssh/ssh-exec.php =================================================================== --- scripts/ssh/ssh-exec.php +++ scripts/ssh/ssh-exec.php @@ -38,8 +38,8 @@ throw new Exception("Invalid username."); } - if ($user->getIsDisabled()) { - throw new Exception("You have been exiled."); + if (!$user->isUserActivated()) { + throw new Exception(pht("Your account is not activated.")); } if ($args->getArg('ssh-command')) { Index: src/__celerity_resource_map__.php =================================================================== --- src/__celerity_resource_map__.php +++ src/__celerity_resource_map__.php @@ -857,7 +857,7 @@ ), 'aphront-dialog-view-css' => array( - 'uri' => '/res/830fa2de/rsrc/css/aphront/dialog-view.css', + 'uri' => '/res/6b6a41c6/rsrc/css/aphront/dialog-view.css', 'type' => 'css', 'requires' => array( @@ -1889,7 +1889,7 @@ ), 'javelin-behavior-maniphest-batch-selector' => array( - 'uri' => '/res/423c5f1b/rsrc/js/application/maniphest/behavior-batch-selector.js', + 'uri' => '/res/a82658b3/rsrc/js/application/maniphest/behavior-batch-selector.js', 'type' => 'js', 'requires' => array( @@ -1917,7 +1917,7 @@ ), 'javelin-behavior-maniphest-subpriority-editor' => array( - 'uri' => '/res/1fa4961f/rsrc/js/application/maniphest/behavior-subpriorityeditor.js', + 'uri' => '/res/95f3d4a6/rsrc/js/application/maniphest/behavior-subpriorityeditor.js', 'type' => 'js', 'requires' => array( @@ -4328,7 +4328,7 @@ ), array( 'packages' => array( - 'f0d63822' => + 'd831cac3' => array( 'name' => 'core.pkg.css', 'symbols' => @@ -4377,7 +4377,7 @@ 41 => 'phabricator-tag-view-css', 42 => 'phui-list-view-css', ), - 'uri' => '/res/pkg/f0d63822/core.pkg.css', + 'uri' => '/res/pkg/d831cac3/core.pkg.css', 'type' => 'css', ), '2c1dba03' => @@ -4552,7 +4552,7 @@ 'uri' => '/res/pkg/49898640/maniphest.pkg.css', 'type' => 'css', ), - '0a694954' => + '0474f45c' => array( 'name' => 'maniphest.pkg.js', 'symbols' => @@ -4563,21 +4563,21 @@ 3 => 'javelin-behavior-maniphest-transaction-expand', 4 => 'javelin-behavior-maniphest-subpriority-editor', ), - 'uri' => '/res/pkg/0a694954/maniphest.pkg.js', + 'uri' => '/res/pkg/0474f45c/maniphest.pkg.js', 'type' => 'js', ), ), 'reverse' => array( - 'aphront-dialog-view-css' => 'f0d63822', - 'aphront-error-view-css' => 'f0d63822', - 'aphront-list-filter-view-css' => 'f0d63822', - 'aphront-pager-view-css' => 'f0d63822', - 'aphront-panel-view-css' => 'f0d63822', - 'aphront-table-view-css' => 'f0d63822', - 'aphront-tokenizer-control-css' => 'f0d63822', - 'aphront-tooltip-css' => 'f0d63822', - 'aphront-typeahead-control-css' => 'f0d63822', + 'aphront-dialog-view-css' => 'd831cac3', + 'aphront-error-view-css' => 'd831cac3', + 'aphront-list-filter-view-css' => 'd831cac3', + 'aphront-pager-view-css' => 'd831cac3', + 'aphront-panel-view-css' => 'd831cac3', + 'aphront-table-view-css' => 'd831cac3', + 'aphront-tokenizer-control-css' => 'd831cac3', + 'aphront-tooltip-css' => 'd831cac3', + 'aphront-typeahead-control-css' => 'd831cac3', 'differential-changeset-view-css' => '1084b12b', 'differential-core-view-css' => '1084b12b', 'differential-inline-comment-editor' => '5e9e5c4e', @@ -4591,7 +4591,7 @@ 'differential-table-of-contents-css' => '1084b12b', 'diffusion-commit-view-css' => '7aa115b4', 'diffusion-icons-css' => '7aa115b4', - 'global-drag-and-drop-css' => 'f0d63822', + 'global-drag-and-drop-css' => 'd831cac3', 'inline-comment-summary-css' => '1084b12b', 'javelin-aphlict' => '2c1dba03', 'javelin-behavior' => '3e3be199', @@ -4623,11 +4623,11 @@ 'javelin-behavior-konami' => '2c1dba03', 'javelin-behavior-lightbox-attachments' => '2c1dba03', 'javelin-behavior-load-blame' => '5e9e5c4e', - 'javelin-behavior-maniphest-batch-selector' => '0a694954', - 'javelin-behavior-maniphest-subpriority-editor' => '0a694954', - 'javelin-behavior-maniphest-transaction-controls' => '0a694954', - 'javelin-behavior-maniphest-transaction-expand' => '0a694954', - 'javelin-behavior-maniphest-transaction-preview' => '0a694954', + 'javelin-behavior-maniphest-batch-selector' => '0474f45c', + 'javelin-behavior-maniphest-subpriority-editor' => '0474f45c', + 'javelin-behavior-maniphest-transaction-controls' => '0474f45c', + 'javelin-behavior-maniphest-transaction-expand' => '0474f45c', + 'javelin-behavior-maniphest-transaction-preview' => '0474f45c', 'javelin-behavior-phabricator-active-nav' => '2c1dba03', 'javelin-behavior-phabricator-autofocus' => '2c1dba03', 'javelin-behavior-phabricator-gesture' => '2c1dba03', @@ -4666,56 +4666,56 @@ 'javelin-util' => '3e3be199', 'javelin-vector' => '3e3be199', 'javelin-workflow' => '3e3be199', - 'lightbox-attachment-css' => 'f0d63822', + 'lightbox-attachment-css' => 'd831cac3', 'maniphest-task-summary-css' => '49898640', - 'phabricator-action-list-view-css' => 'f0d63822', - 'phabricator-application-launch-view-css' => 'f0d63822', + 'phabricator-action-list-view-css' => 'd831cac3', + 'phabricator-application-launch-view-css' => 'd831cac3', 'phabricator-busy' => '2c1dba03', 'phabricator-content-source-view-css' => '1084b12b', - 'phabricator-core-css' => 'f0d63822', - 'phabricator-crumbs-view-css' => 'f0d63822', + 'phabricator-core-css' => 'd831cac3', + 'phabricator-crumbs-view-css' => 'd831cac3', 'phabricator-drag-and-drop-file-upload' => '5e9e5c4e', 'phabricator-dropdown-menu' => '2c1dba03', 'phabricator-file-upload' => '2c1dba03', - 'phabricator-filetree-view-css' => 'f0d63822', - 'phabricator-flag-css' => 'f0d63822', + 'phabricator-filetree-view-css' => 'd831cac3', + 'phabricator-flag-css' => 'd831cac3', 'phabricator-hovercard' => '2c1dba03', - 'phabricator-jump-nav' => 'f0d63822', + 'phabricator-jump-nav' => 'd831cac3', 'phabricator-keyboard-shortcut' => '2c1dba03', 'phabricator-keyboard-shortcut-manager' => '2c1dba03', - 'phabricator-main-menu-view' => 'f0d63822', + 'phabricator-main-menu-view' => 'd831cac3', 'phabricator-menu-item' => '2c1dba03', - 'phabricator-nav-view-css' => 'f0d63822', + 'phabricator-nav-view-css' => 'd831cac3', 'phabricator-notification' => '2c1dba03', - 'phabricator-notification-css' => 'f0d63822', - 'phabricator-notification-menu-css' => 'f0d63822', + 'phabricator-notification-css' => 'd831cac3', + 'phabricator-notification-menu-css' => 'd831cac3', 'phabricator-object-selector-css' => '1084b12b', 'phabricator-phtize' => '2c1dba03', 'phabricator-prefab' => '2c1dba03', 'phabricator-project-tag-css' => '49898640', - 'phabricator-remarkup-css' => 'f0d63822', + 'phabricator-remarkup-css' => 'd831cac3', 'phabricator-shaped-request' => '5e9e5c4e', - 'phabricator-side-menu-view-css' => 'f0d63822', - 'phabricator-standard-page-view' => 'f0d63822', - 'phabricator-tag-view-css' => 'f0d63822', + 'phabricator-side-menu-view-css' => 'd831cac3', + 'phabricator-standard-page-view' => 'd831cac3', + 'phabricator-tag-view-css' => 'd831cac3', 'phabricator-textareautils' => '2c1dba03', 'phabricator-tooltip' => '2c1dba03', - 'phabricator-transaction-view-css' => 'f0d63822', - 'phabricator-zindex-css' => 'f0d63822', - 'phui-button-css' => 'f0d63822', - 'phui-form-css' => 'f0d63822', - 'phui-form-view-css' => 'f0d63822', - 'phui-header-view-css' => 'f0d63822', - 'phui-icon-view-css' => 'f0d63822', - 'phui-list-view-css' => 'f0d63822', - 'phui-object-item-list-view-css' => 'f0d63822', - 'phui-property-list-view-css' => 'f0d63822', - 'phui-spacing-css' => 'f0d63822', - 'sprite-apps-large-css' => 'f0d63822', - 'sprite-gradient-css' => 'f0d63822', - 'sprite-icons-css' => 'f0d63822', - 'sprite-menu-css' => 'f0d63822', - 'sprite-status-css' => 'f0d63822', - 'syntax-highlighting-css' => 'f0d63822', + 'phabricator-transaction-view-css' => 'd831cac3', + 'phabricator-zindex-css' => 'd831cac3', + 'phui-button-css' => 'd831cac3', + 'phui-form-css' => 'd831cac3', + 'phui-form-view-css' => 'd831cac3', + 'phui-header-view-css' => 'd831cac3', + 'phui-icon-view-css' => 'd831cac3', + 'phui-list-view-css' => 'd831cac3', + 'phui-object-item-list-view-css' => 'd831cac3', + 'phui-property-list-view-css' => 'd831cac3', + 'phui-spacing-css' => 'd831cac3', + 'sprite-apps-large-css' => 'd831cac3', + 'sprite-gradient-css' => 'd831cac3', + 'sprite-icons-css' => 'd831cac3', + 'sprite-menu-css' => 'd831cac3', + 'sprite-status-css' => 'd831cac3', + 'syntax-highlighting-css' => 'd831cac3', ), )); Index: src/applications/auth/controller/PhabricatorEmailVerificationController.php =================================================================== --- src/applications/auth/controller/PhabricatorEmailVerificationController.php +++ src/applications/auth/controller/PhabricatorEmailVerificationController.php @@ -39,8 +39,20 @@ $continue = pht('Continue to Phabricator'); } else { $guard = AphrontWriteGuard::beginScopedUnguardedWrites(); - $email->setIsVerified(1); - $email->save(); + $email->openTransaction(); + + $email->setIsVerified(1); + $email->save(); + + // If the user just verified their primary email address, mark their + // account as email verified. + $user_primary = $user->loadPrimaryEmail(); + if ($user_primary->getID() == $email->getID()) { + $user->setIsEmailVerified(1); + $user->save(); + } + + $email->saveTransaction(); unset($guard); $title = pht('Address Verified'); Index: src/applications/auth/controller/PhabricatorMustVerifyEmailController.php =================================================================== --- src/applications/auth/controller/PhabricatorMustVerifyEmailController.php +++ src/applications/auth/controller/PhabricatorMustVerifyEmailController.php @@ -31,42 +31,33 @@ $sent = new AphrontErrorView(); $sent->setSeverity(AphrontErrorView::SEVERITY_NOTICE); $sent->setTitle(pht('Email Sent')); - $sent->appendChild(phutil_tag( - 'p', - array(), + $sent->appendChild( pht( 'Another verification email was sent to %s.', - phutil_tag('strong', array(), $email_address)))); + phutil_tag('strong', array(), $email_address))); } - $error_view = new AphrontRequestFailureView(); - $error_view->setHeader(pht('Check Your Email')); - $error_view->appendChild(phutil_tag('p', array(), pht( - 'You must verify your email address to login. You should have a new '. - 'email message from Phabricator with verification instructions in your '. - 'inbox (%s).', phutil_tag('strong', array(), $email_address)))); - $error_view->appendChild(phutil_tag('p', array(), pht( + $must_verify = pht( + 'You must verify your email address to login. You should have a '. + 'new email message from Phabricator with verification instructions '. + 'in your inbox (%s).', + phutil_tag('strong', array(), $email_address)); + + $send_again = pht( 'If you did not receive an email, you can click the button below '. - 'to try sending another one.'))); - $error_view->appendChild(phutil_tag_div( - 'aphront-failure-continue', - phabricator_form( - $user, - array( - 'action' => '/login/mustverify/', - 'method' => 'POST', - ), - phutil_tag( - 'button', - array( - ), - pht('Send Another Email'))))); + 'to try sending another one.'); + $dialog = id(new AphrontDialogView()) + ->setUser($user) + ->setTitle(pht('Check Your Email')) + ->appendParagraph($must_verify) + ->appendParagraph($send_again) + ->addSubmitButton(pht('Send Another Email')); return $this->buildApplicationPage( array( $sent, - $error_view, + $dialog, ), array( 'title' => pht('Must Verify Email'), Index: src/applications/base/controller/PhabricatorController.php =================================================================== --- src/applications/base/controller/PhabricatorController.php +++ src/applications/base/controller/PhabricatorController.php @@ -114,12 +114,7 @@ if ($user->isLoggedIn()) { if ($this->shouldRequireEmailVerification()) { - $email = $user->loadPrimaryEmail(); - if (!$email) { - throw new Exception( - "No primary email address associated with this account!"); - } - if (!$email->getIsVerified()) { + if (!$user->getIsEmailVerified()) { $controller = new PhabricatorMustVerifyEmailController($request); $this->setCurrentApplication($auth_application); return $this->delegateToController($controller); Index: src/applications/base/controller/__tests__/PhabricatorAccessControlTestCase.php =================================================================== --- src/applications/base/controller/__tests__/PhabricatorAccessControlTestCase.php +++ src/applications/base/controller/__tests__/PhabricatorAccessControlTestCase.php @@ -31,7 +31,7 @@ $u_unverified = $this->generateNewTestUser() ->setUsername('unverified') ->save(); - $u_unverified->loadPrimaryEmail()->setIsVerified(0)->save(); + $u_unverified->setIsEmailVerified(0)->save(); $u_normal = $this->generateNewTestUser() ->setUsername('normal') Index: src/applications/conduit/controller/PhabricatorConduitAPIController.php =================================================================== --- src/applications/conduit/controller/PhabricatorConduitAPIController.php +++ src/applications/conduit/controller/PhabricatorConduitAPIController.php @@ -313,24 +313,11 @@ ConduitAPIRequest $request, PhabricatorUser $user) { - if ($user->getIsDisabled()) { + if (!$user->isUserActivated()) { return array( 'ERR-USER-DISABLED', - 'User is disabled.'); - } - - if (PhabricatorUserEmail::isEmailVerificationRequired()) { - $email = $user->loadPrimaryEmail(); - if (!$email) { - return array( - 'ERR-USER-NOEMAIL', - 'User has no primary email address.'); - } - if (!$email->getIsVerified()) { - return array( - 'ERR-USER-UNVERIFIED', - 'User has unverified email address.'); - } + pht('User account is not activated.'), + ); } $request->setUser($user); Index: src/applications/diffusion/controller/DiffusionServeController.php =================================================================== --- src/applications/diffusion/controller/DiffusionServeController.php +++ src/applications/diffusion/controller/DiffusionServeController.php @@ -382,6 +382,11 @@ return null; } + if (!$user->isUserActivated()) { + // User is not activated. + return null; + } + $password_entry = id(new PhabricatorRepositoryVCSPassword()) ->loadOneWhere('userPHID = %s', $user->getPHID()); if (!$password_entry) { @@ -394,11 +399,6 @@ return null; } - if ($user->getIsDisabled()) { - // User is disabled. - return null; - } - return $user; } Index: src/applications/herald/query/HeraldRuleQuery.php =================================================================== --- src/applications/herald/query/HeraldRuleQuery.php +++ src/applications/herald/query/HeraldRuleQuery.php @@ -219,7 +219,7 @@ $rule->attachValidAuthor(false); continue; } - if ($users[$author_phid]->getIsDisabled()) { + if (!$users[$author_phid]->isUserActivated()) { $rule->attachValidAuthor(false); continue; } Index: src/applications/metamta/query/PhabricatorMetaMTAActorQuery.php =================================================================== --- src/applications/metamta/query/PhabricatorMetaMTAActorQuery.php +++ src/applications/metamta/query/PhabricatorMetaMTAActorQuery.php @@ -82,6 +82,11 @@ $actor->setUndeliverable( pht('This user is a bot; bot accounts do not receive mail.')); } + + // NOTE: We do send email to unapproved users, and to unverified users, + // because it would otherwise be impossible to get them to verify their + // email addresses. Possibly we should white-list this kind of mail and + // deny all other types of mail. } $email = idx($emails, $phid); Index: src/applications/metamta/receiver/PhabricatorMailReceiver.php =================================================================== --- src/applications/metamta/receiver/PhabricatorMailReceiver.php +++ src/applications/metamta/receiver/PhabricatorMailReceiver.php @@ -19,16 +19,13 @@ PhabricatorMetaMTAReceivedMail $mail, PhabricatorUser $sender) { - if ($sender->getIsDisabled()) { + if (!$sender->isUserActivated()) { throw new PhabricatorMetaMTAReceivedMailProcessingException( MetaMTAReceivedMailStatus::STATUS_DISABLED_SENDER, pht( - "Sender '%s' has a disabled user account.", + "Sender '%s' does not have an activated user account.", $sender->getUsername())); } - - - return; } /** Index: src/applications/people/conduit/ConduitAPI_user_Method.php =================================================================== --- src/applications/people/conduit/ConduitAPI_user_Method.php +++ src/applications/people/conduit/ConduitAPI_user_Method.php @@ -32,6 +32,14 @@ $roles[] = 'unverified'; } + if ($user->getIsApproved()) { + $roles[] = 'approved'; + } + + if ($user->isUserActivated()) { + $roles[] = 'activated'; + } + $return = array( 'phid' => $user->getPHID(), 'userName' => $user->getUserName(), Index: src/applications/people/controller/PhabricatorPeopleEditController.php =================================================================== --- src/applications/people/controller/PhabricatorPeopleEditController.php +++ src/applications/people/controller/PhabricatorPeopleEditController.php @@ -325,7 +325,9 @@ if ($user->getIsDisabled()) { $roles[] = pht('Disabled'); } - + if (!$user->getIsApproved()) { + $roles[] = pht('Not Approved'); + } if (!$roles) { $roles[] = pht('Normal User'); } Index: src/applications/people/controller/PhabricatorPeopleListController.php =================================================================== --- src/applications/people/controller/PhabricatorPeopleListController.php +++ src/applications/people/controller/PhabricatorPeopleListController.php @@ -61,6 +61,10 @@ $item->addIcon('disable', pht('Disabled')); } + if (!$user->getIsApproved()) { + $item->addIcon('raise-priority', pht('Not Approved')); + } + if ($user->getIsAdmin()) { $item->addIcon('highlight', pht('Admin')); } Index: src/applications/people/customfield/PhabricatorUserRolesField.php =================================================================== --- src/applications/people/customfield/PhabricatorUserRolesField.php +++ src/applications/people/customfield/PhabricatorUserRolesField.php @@ -31,6 +31,9 @@ if ($user->getIsDisabled()) { $roles[] = pht('Disabled'); } + if (!$user->getIsApproved()) { + $roles[] = pht('Not Approved'); + } if ($user->getIsSystemAgent()) { $roles[] = pht('Bot'); } Index: src/applications/people/editor/PhabricatorUserEditor.php =================================================================== --- src/applications/people/editor/PhabricatorUserEditor.php +++ src/applications/people/editor/PhabricatorUserEditor.php @@ -41,6 +41,12 @@ // Always set a new user's email address to primary. $email->setIsPrimary(1); + // If the primary address is already verified, also set the verified flag + // on the user themselves. + if ($email->getIsVerified()) { + $user->setIsEmailVerified(1); + } + $this->willAddEmail($email); $user->openTransaction(); Index: src/applications/people/event/PhabricatorPeopleHovercardEventListener.php =================================================================== --- src/applications/people/event/PhabricatorPeopleHovercardEventListener.php +++ src/applications/people/event/PhabricatorPeopleHovercardEventListener.php @@ -35,6 +35,8 @@ if ($user->getIsDisabled()) { $hovercard->addField(pht('Account'), pht('Disabled')); + } else if (!$user->isUserActivated()) { + $hovercard->addField(pht('Account'), pht('Not Activated')); } else { $statuses = id(new PhabricatorUserStatus())->loadCurrentStatuses( array($user->getPHID())); Index: src/applications/people/phid/PhabricatorPeoplePHIDTypeUser.php =================================================================== --- src/applications/people/phid/PhabricatorPeoplePHIDTypeUser.php +++ src/applications/people/phid/PhabricatorPeoplePHIDTypeUser.php @@ -38,7 +38,7 @@ $handle->setFullName( $user->getUsername().' ('.$user->getRealName().')'); $handle->setImageURI($user->loadProfileImageURI()); - $handle->setDisabled($user->getIsDisabled()); + $handle->setDisabled(!$user->isUserActivated()); if ($user->hasStatus()) { $status = $user->getStatus(); $handle->setStatus($status->getTextStatus()); Index: src/applications/people/remarkup/PhabricatorRemarkupRuleMention.php =================================================================== --- src/applications/people/remarkup/PhabricatorRemarkupRuleMention.php +++ src/applications/people/remarkup/PhabricatorRemarkupRuleMention.php @@ -107,7 +107,7 @@ ->setName('@'.$user->getUserName()) ->setHref('/p/'.$user->getUserName().'/'); - if ($user->getIsDisabled()) { + if (!$user->isUserActivated()) { $tag->setDotColor(PhabricatorTagView::COLOR_GREY); } else { $status = idx($user_statuses, $user->getPHID()); Index: src/applications/people/search/PhabricatorUserSearchIndexer.php =================================================================== --- src/applications/people/search/PhabricatorUserSearchIndexer.php +++ src/applications/people/search/PhabricatorUserSearchIndexer.php @@ -20,7 +20,7 @@ // TODO: Index the blurbs from their profile or something? Probably not // actually useful... - if (!$user->getIsDisabled()) { + if ($user->isUserActivated()) { $doc->addRelationship( PhabricatorSearchRelationship::RELATIONSHIP_OPEN, $user->getPHID(), Index: src/applications/people/storage/PhabricatorUser.php =================================================================== --- src/applications/people/storage/PhabricatorUser.php +++ src/applications/people/storage/PhabricatorUser.php @@ -29,6 +29,8 @@ protected $isSystemAgent = 0; protected $isAdmin = 0; protected $isDisabled = 0; + protected $isEmailVerified = 0; + protected $isApproved = 1; private $profileImage = null; private $profile = null; @@ -51,11 +53,41 @@ return (bool)$this->isDisabled; case 'isSystemAgent': return (bool)$this->isSystemAgent; + case 'isEmailVerified': + return (bool)$this->isEmailVerified; + case 'isApproved': + return (bool)$this->isApproved; default: return parent::readField($field); } } + + /** + * Is this a live account which has passed required approvals? Returns true + * if this is an enabled, verified (if required), approved (if required) + * account, and false otherwise. + * + * @return bool True if this is a standard, usable account. + */ + public function isUserActivated() { + if ($this->getIsDisabled()) { + return false; + } + + if (!$this->getIsApproved()) { + return false; + } + + if (PhabricatorUserEmail::isEmailVerificationRequired()) { + if (!$this->getIsEmailVerified()) { + return false; + } + } + + return true; + } + public function getConfiguration() { return array( self::CONFIG_AUX_PHID => true, Index: src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php =================================================================== --- src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php +++ src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php @@ -1748,6 +1748,14 @@ 'type' => 'sql', 'name' => $this->getPatchPath('20131107.buildlog.sql'), ), + '20131112.userverified.1.col.sql' => array( + 'type' => 'sql', + 'name' => $this->getPatchPath('20131112.userverified.1.col.sql'), + ), + '20131112.userverified.2.mig.php' => array( + 'type' => 'php', + 'name' => $this->getPatchPath('20131112.userverified.2.mig.php'), + ), ); } } Index: src/infrastructure/testing/PhabricatorTestCase.php =================================================================== --- src/infrastructure/testing/PhabricatorTestCase.php +++ src/infrastructure/testing/PhabricatorTestCase.php @@ -105,6 +105,11 @@ 'phabricator.show-beta-applications', true); + // Reset application settings to defaults, particularly policies. + $this->env->overrideEnvConfig( + 'phabricator.application-settings', + array()); + // TODO: Remove this when we remove "releeph.installed". $this->env->overrideEnvConfig('releeph.installed', true); } Index: src/view/AphrontDialogView.php =================================================================== --- src/view/AphrontDialogView.php +++ src/view/AphrontDialogView.php @@ -111,6 +111,16 @@ return $this; } + public function appendParagraph($paragraph) { + return $this->appendChild( + phutil_tag( + 'p', + array( + 'class' => 'aphront-dialog-view-paragraph', + ), + $paragraph)); + } + final public function render() { require_celerity_resource('aphront-dialog-view-css'); Index: webroot/rsrc/css/aphront/dialog-view.css =================================================================== --- webroot/rsrc/css/aphront/dialog-view.css +++ webroot/rsrc/css/aphront/dialog-view.css @@ -124,3 +124,7 @@ .aphront-capability-details { margin: 20px 0 4px; } + +.aphront-dialog-view-paragraph + .aphront-dialog-view-paragraph { + margin-top: 16px; +}