diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2947,6 +2947,7 @@ 'PhabricatorMailManagementSendTestWorkflow' => 'applications/metamta/management/PhabricatorMailManagementSendTestWorkflow.php', 'PhabricatorMailManagementShowInboundWorkflow' => 'applications/metamta/management/PhabricatorMailManagementShowInboundWorkflow.php', 'PhabricatorMailManagementShowOutboundWorkflow' => 'applications/metamta/management/PhabricatorMailManagementShowOutboundWorkflow.php', + 'PhabricatorMailManagementUnverifyWorkflow' => 'applications/metamta/management/PhabricatorMailManagementUnverifyWorkflow.php', 'PhabricatorMailManagementVolumeWorkflow' => 'applications/metamta/management/PhabricatorMailManagementVolumeWorkflow.php', 'PhabricatorMailManagementWorkflow' => 'applications/metamta/management/PhabricatorMailManagementWorkflow.php', 'PhabricatorMailOutboundMailHeraldAdapter' => 'applications/metamta/herald/PhabricatorMailOutboundMailHeraldAdapter.php', @@ -3772,6 +3773,7 @@ 'PhabricatorSettingsDeveloperPanelGroup' => 'applications/settings/panelgroup/PhabricatorSettingsDeveloperPanelGroup.php', 'PhabricatorSettingsEditEngine' => 'applications/settings/editor/PhabricatorSettingsEditEngine.php', 'PhabricatorSettingsEmailPanelGroup' => 'applications/settings/panelgroup/PhabricatorSettingsEmailPanelGroup.php', + 'PhabricatorSettingsIssueController' => 'applications/settings/controller/PhabricatorSettingsIssueController.php', 'PhabricatorSettingsListController' => 'applications/settings/controller/PhabricatorSettingsListController.php', 'PhabricatorSettingsLogsPanelGroup' => 'applications/settings/panelgroup/PhabricatorSettingsLogsPanelGroup.php', 'PhabricatorSettingsMainController' => 'applications/settings/controller/PhabricatorSettingsMainController.php', @@ -8009,6 +8011,7 @@ 'PhabricatorMailManagementSendTestWorkflow' => 'PhabricatorMailManagementWorkflow', 'PhabricatorMailManagementShowInboundWorkflow' => 'PhabricatorMailManagementWorkflow', 'PhabricatorMailManagementShowOutboundWorkflow' => 'PhabricatorMailManagementWorkflow', + 'PhabricatorMailManagementUnverifyWorkflow' => 'PhabricatorMailManagementWorkflow', 'PhabricatorMailManagementVolumeWorkflow' => 'PhabricatorMailManagementWorkflow', 'PhabricatorMailManagementWorkflow' => 'PhabricatorManagementWorkflow', 'PhabricatorMailOutboundMailHeraldAdapter' => 'HeraldAdapter', @@ -9021,6 +9024,7 @@ 'PhabricatorSettingsDeveloperPanelGroup' => 'PhabricatorSettingsPanelGroup', 'PhabricatorSettingsEditEngine' => 'PhabricatorEditEngine', 'PhabricatorSettingsEmailPanelGroup' => 'PhabricatorSettingsPanelGroup', + 'PhabricatorSettingsIssueController' => 'PhabricatorController', 'PhabricatorSettingsListController' => 'PhabricatorController', 'PhabricatorSettingsLogsPanelGroup' => 'PhabricatorSettingsPanelGroup', 'PhabricatorSettingsMainController' => 'PhabricatorController', diff --git a/src/applications/metamta/management/PhabricatorMailManagementUnverifyWorkflow.php b/src/applications/metamta/management/PhabricatorMailManagementUnverifyWorkflow.php new file mode 100644 --- /dev/null +++ b/src/applications/metamta/management/PhabricatorMailManagementUnverifyWorkflow.php @@ -0,0 +1,110 @@ +setName('unverify') + ->setSynopsis( + pht('Unverify an email address so it no longer receives mail.')) + ->setExamples('**unverify** __address__ ...') + ->setArguments( + array( + array( + 'name' => 'addresses', + 'wildcard' => true, + 'help' => pht('Address (or addresses) to unverify.'), + ), + )); + } + + public function execute(PhutilArgumentParser $args) { + $console = PhutilConsole::getConsole(); + $viewer = $this->getViewer(); + + $addresses = $args->getArg('addresses'); + if (!$addresses) { + throw new PhutilArgumentUsageException( + pht('Specify one or more email addresses to unverify.')); + } + + foreach ($addresses as $address) { + $email = id(new PhabricatorUserEmail())->loadOneWhere( + 'address = %s', + $address); + if (!$email) { + echo tsprintf( + "%s\n", + pht( + 'Address "%s" is unknown.', + $address)); + continue; + } + + $user_phid = $email->getUserPHID(); + + $user = id(new PhabricatorPeopleQuery()) + ->setViewer($viewer) + ->withPHIDs(array($user_phid)) + ->executeOne(); + + if (!$user) { + echo tsprintf( + "%s\n", + pht( + 'Address "%s" belongs to invalid user "%s".', + $address, + $user_phid)); + continue; + } + + if (!$email->getIsVerified()) { + echo tsprintf( + "%s\n", + pht( + 'Address "%s" (owned by "%s") is already unveriifed.', + $address, + $user->getUsername())); + continue; + } + + $email->openTransaction(); + + $email + ->setIsVerified(0) + ->save(); + + if ($email->getIsPrimary()) { + $user + ->setIsEmailVerified(0) + ->save(); + } + + $email->saveTransaction(); + + if ($email->getIsPrimary()) { + echo tsprintf( + "%s\n", + pht( + 'Unverified "%s", the primary address for "%s".', + $address, + $user->getUsername())); + } else { + echo tsprintf( + "%s\n", + pht( + 'Unverified "%s", an address for "%s".', + $address, + $user->getUsername())); + } + } + + echo tsprintf( + "%s\n", + pht('Done.')); + + return 0; + } + +} diff --git a/src/applications/settings/application/PhabricatorSettingsApplication.php b/src/applications/settings/application/PhabricatorSettingsApplication.php --- a/src/applications/settings/application/PhabricatorSettingsApplication.php +++ b/src/applications/settings/application/PhabricatorSettingsApplication.php @@ -41,6 +41,7 @@ 'adjust/' => 'PhabricatorSettingsAdjustController', 'timezone/(?P[^/]+)/' => 'PhabricatorSettingsTimezoneController', + 'issue/' => 'PhabricatorSettingsIssueController', ), ); } diff --git a/src/applications/settings/controller/PhabricatorSettingsIssueController.php b/src/applications/settings/controller/PhabricatorSettingsIssueController.php new file mode 100644 --- /dev/null +++ b/src/applications/settings/controller/PhabricatorSettingsIssueController.php @@ -0,0 +1,103 @@ +getViewer(); + + $setup_uri = id(new PhabricatorEmailAddressesSettingsPanel()) + ->setViewer($viewer) + ->setUser($viewer) + ->getPanelURI(); + + $issues = array(); + if (!$viewer->getIsEmailVerified()) { + // We could specifically detect that the user has missed email because + // their address is unverified here and point them at Mail so they can + // look at messages they missed. + + // We could also detect that an administrator unverified their address + // and let that come with a message. + + // For now, just make sure the unverified address does not escape notice. + $issues[] = array( + 'title' => pht('Primary Email Unverified'), + 'summary' => pht( + 'Your primary email address is unverified. You will not be able '. + 'to receive email until you verify it.'), + 'uri' => $setup_uri, + ); + } + + if ($issues) { + require_celerity_resource('phabricator-notification-menu-css'); + + $items = array(); + foreach ($issues as $issue) { + $classes = array(); + $classes[] = 'phabricator-notification'; + $classes[] = 'phabricator-notification-unread'; + + $uri = $issue['uri']; + $title = $issue['title']; + $summary = $issue['summary']; + + $items[] = javelin_tag( + 'div', + array( + 'class' => + 'phabricator-notification phabricator-notification-unread', + 'sigil' => 'notification', + 'meta' => array( + 'href' => $uri, + ), + ), + array( + phutil_tag('strong', array(), pht('%s:', $title)), + ' ', + $summary, + )); + } + + $content = phutil_tag( + 'div', + array( + 'class' => 'setup-issue-menu', + ), + $items); + } else { + $content = phutil_tag( + 'div', + array( + 'class' => 'phabricator-notification no-notifications', + ), + pht('You have no account setup issues.')); + } + + $header = phutil_tag( + 'div', + array( + 'class' => 'phabricator-notification-header', + ), + phutil_tag( + 'a', + array( + 'href' => $setup_uri, + ), + pht('Account Setup Issues'))); + + $content = array( + $header, + $content, + ); + + $json = array( + 'content' => hsprintf('%s', $content), + 'number' => count($issues), + ); + + return id(new AphrontAjaxResponse())->setContent($json); + } + +} diff --git a/src/view/page/menu/PhabricatorMainMenuView.php b/src/view/page/menu/PhabricatorMainMenuView.php --- a/src/view/page/menu/PhabricatorMainMenuView.php +++ b/src/view/page/menu/PhabricatorMainMenuView.php @@ -595,10 +595,74 @@ } } + $user_dropdown = null; + $user_tag = null; + if ($viewer->isLoggedIn()) { + if (!$viewer->getIsEmailVerified()) { + $bubble_id = celerity_generate_unique_node_id(); + $count_id = celerity_generate_unique_node_id(); + $dropdown_id = celerity_generate_unique_node_id(); + + $settings_uri = id(new PhabricatorEmailAddressesSettingsPanel()) + ->setViewer($viewer) + ->setUser($viewer) + ->getPanelURI(); + + $user_icon = javelin_tag( + 'span', + array( + 'class' => 'phabricator-main-menu-setup-icon phui-icon-view '. + 'phui-font-fa fa-user', + 'sigil' => 'menu-icon', + )); + + $user_count = javelin_tag( + 'span', + array( + 'class' => 'phabricator-main-menu-setup-count', + 'id' => $count_id, + ), + 1); + + $user_tag = phutil_tag( + 'a', + array( + 'href' => $settings_uri, + 'class' => 'setup-unread', + 'id' => $bubble_id, + ), + array( + $user_icon, + $user_count, + )); + + Javelin::initBehavior( + 'aphlict-dropdown', + array( + 'bubbleID' => $bubble_id, + 'countID' => $count_id, + 'dropdownID' => $dropdown_id, + 'loadingText' => pht('Loading...'), + 'uri' => '/settings/issue/', + 'unreadClass' => 'setup-unread', + )); + + $user_dropdown = javelin_tag( + 'div', + array( + 'id' => $dropdown_id, + 'class' => 'phabricator-notification-menu', + 'sigil' => 'phabricator-notification-menu', + 'style' => 'display: none;', + )); + } + } + $dropdowns = array( $notification_dropdown, $message_notification_dropdown, $setup_notification_dropdown, + $user_dropdown, ); return array( @@ -606,6 +670,7 @@ $bubble_tag, $message_tag, $setup_tag, + $user_tag, ), $dropdowns, $aural,