public function handleRequest(AphrontRequest $request)
 {
     $viewer = $this->getViewer();
     $this->requireApplicationCapability(PeopleCreateUsersCapability::CAPABILITY);
     $is_confirm = false;
     $errors = array();
     $confirm_errors = array();
     $e_emails = true;
     $message = $request->getStr('message');
     $emails = $request->getStr('emails');
     $severity = PHUIInfoView::SEVERITY_ERROR;
     if ($request->isFormPost()) {
         // NOTE: We aren't using spaces as a delimiter here because email
         // addresses with names often include spaces.
         $email_list = preg_split('/[,;\\n]+/', $emails);
         foreach ($email_list as $key => $email) {
             if (!strlen(trim($email))) {
                 unset($email_list[$key]);
             }
         }
         if ($email_list) {
             $e_emails = null;
         } else {
             $e_emails = pht('Required');
             $errors[] = pht('To send invites, you must enter at least one email address.');
         }
         if (!$errors) {
             $is_confirm = true;
             $actions = PhabricatorAuthInviteAction::newActionListFromAddresses($viewer, $email_list);
             $any_valid = false;
             $all_valid = true;
             foreach ($actions as $action) {
                 if ($action->willSend()) {
                     $any_valid = true;
                 } else {
                     $all_valid = false;
                 }
             }
             if (!$any_valid) {
                 $confirm_errors[] = pht('None of the provided addresses are valid invite recipients. ' . 'Review the table below for details. Revise the address list ' . 'to continue.');
             } else {
                 if ($all_valid) {
                     $confirm_errors[] = pht('All of the addresses appear to be valid invite recipients. ' . 'Confirm the actions below to continue.');
                     $severity = PHUIInfoView::SEVERITY_NOTICE;
                 } else {
                     $confirm_errors[] = pht('Some of the addresses you entered do not appear to be ' . 'valid recipients. Review the table below. You can revise ' . 'the address list, or ignore these errors and continue.');
                     $severity = PHUIInfoView::SEVERITY_WARNING;
                 }
             }
             if ($any_valid && $request->getBool('confirm')) {
                 // TODO: The copywriting on this mail could probably be more
                 // engaging and we could have a fancy HTML version.
                 $template = array();
                 $template[] = pht('%s has invited you to join Phabricator.', $viewer->getFullName());
                 if (strlen(trim($message))) {
                     $template[] = $message;
                 }
                 $template[] = pht('To register an account and get started, follow this link:');
                 // This isn't a variable; it will be replaced later on in the
                 // daemons once they generate the URI.
                 $template[] = '{$INVITE_URI}';
                 $template[] = pht('If you already have an account, you can follow the link to ' . 'quickly verify this email address.');
                 $template = implode("\n\n", $template);
                 foreach ($actions as $action) {
                     if ($action->willSend()) {
                         $action->sendInvite($viewer, $template);
                     }
                 }
                 // TODO: This is a bit anticlimactic. We don't really have anything
                 // to show the user because the action is happening in the background
                 // and the invites won't exist yet. After T5166 we can show a
                 // better progress bar.
                 return id(new AphrontRedirectResponse())->setURI($this->getApplicationURI());
             }
         }
     }
     if ($is_confirm) {
         $title = pht('Confirm Invites');
     } else {
         $title = pht('Invite Users');
     }
     $crumbs = $this->buildApplicationCrumbs();
     if ($is_confirm) {
         $crumbs->addTextCrumb(pht('Confirm'));
     } else {
         $crumbs->addTextCrumb(pht('Invite Users'));
     }
     $crumbs->setBorder(true);
     $confirm_box = null;
     $info_view = null;
     if ($is_confirm) {
         $handles = array();
         if ($actions) {
             $handles = $this->loadViewerHandles(mpull($actions, 'getUserPHID'));
         }
         $invite_table = id(new PhabricatorAuthInviteActionTableView())->setUser($viewer)->setInviteActions($actions)->setHandles($handles);
         $confirm_form = null;
         if ($any_valid) {
             $confirm_form = id(new AphrontFormView())->setUser($viewer)->addHiddenInput('message', $message)->addHiddenInput('emails', $emails)->addHiddenInput('confirm', true)->appendRemarkupInstructions(pht('If everything looks good, click **Send Invitations** to ' . 'deliver email invitations these users. Otherwise, edit the ' . 'email list or personal message at the bottom of the page to ' . 'revise the invitations.'))->appendChild(id(new AphrontFormSubmitControl())->setValue(pht('Send Invitations')));
         }
         $info_view = id(new PHUIInfoView())->setErrors($confirm_errors)->setSeverity($severity);
         $confirm_box = id(new PHUIObjectBoxView())->setHeaderText(pht('Confirm Invites'))->setTable($invite_table)->appendChild($confirm_form)->setBackground(PHUIObjectBoxView::BLUE_PROPERTY);
     }
     $form = id(new AphrontFormView())->setUser($viewer)->appendRemarkupInstructions(pht('To invite users to Phabricator, enter their email addresses below. ' . 'Separate addresses with commas or newlines.'))->appendChild(id(new AphrontFormTextAreaControl())->setLabel(pht('Email Addresses'))->setName(pht('emails'))->setValue($emails)->setError($e_emails)->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL))->appendRemarkupInstructions(pht('You can optionally include a heartfelt personal message in ' . 'the email.'))->appendChild(id(new AphrontFormTextAreaControl())->setLabel(pht('Message'))->setName(pht('message'))->setValue($message))->appendChild(id(new AphrontFormSubmitControl())->setValue($is_confirm ? pht('Update Preview') : pht('Continue'))->addCancelButton($this->getApplicationURI('invite/')));
     $header = id(new PHUIHeaderView())->setHeader($title)->setHeaderIcon('fa-group');
     $box = id(new PHUIObjectBoxView())->setHeaderText($is_confirm ? pht('Revise Invites') : pht('Invite Users'))->setFormErrors($errors)->setForm($form)->setBackground(PHUIObjectBoxView::BLUE_PROPERTY);
     $view = id(new PHUITwoColumnView())->setHeader($header)->setFooter(array($info_view, $confirm_box, $box));
     return $this->newPage()->setTitle($title)->setCrumbs($crumbs)->appendChild($view);
 }
 public static function newActionListFromAddresses(PhabricatorUser $viewer, array $addresses)
 {
     $results = array();
     foreach ($addresses as $address) {
         $result = new PhabricatorAuthInviteAction();
         $result->rawInput = $address;
         $email = new PhutilEmailAddress($address);
         $result->emailAddress = phutil_utf8_strtolower($email->getAddress());
         if (!preg_match('/^\\S+@\\S+\\.\\S+\\z/', $result->emailAddress)) {
             $result->issues[] = self::ISSUE_PARSE;
         }
         $results[] = $result;
     }
     // Identify duplicates.
     $address_groups = mgroup($results, 'getEmailAddress');
     foreach ($address_groups as $address => $group) {
         if (count($group) > 1) {
             foreach ($group as $action) {
                 $action->issues[] = self::ISSUE_DUPLICATE;
             }
         }
     }
     // Identify addresses which are already in the system.
     $addresses = mpull($results, 'getEmailAddress');
     $email_objects = id(new PhabricatorUserEmail())->loadAllWhere('address IN (%Ls)', $addresses);
     $email_map = array();
     foreach ($email_objects as $email_object) {
         $address_key = phutil_utf8_strtolower($email_object->getAddress());
         $email_map[$address_key] = $email_object;
     }
     // Identify outstanding invites.
     $invites = id(new PhabricatorAuthInviteQuery())->setViewer($viewer)->withEmailAddresses($addresses)->execute();
     $invite_map = mpull($invites, null, 'getEmailAddress');
     foreach ($results as $action) {
         $email = idx($email_map, $action->getEmailAddress());
         if ($email) {
             if ($email->getUserPHID()) {
                 $action->userPHID = $email->getUserPHID();
                 if ($email->getIsVerified()) {
                     $action->issues[] = self::ISSUE_VERIFIED;
                 } else {
                     $action->issues[] = self::ISSUE_UNVERIFIED;
                 }
             }
         }
         $invite = idx($invite_map, $action->getEmailAddress());
         if ($invite) {
             if ($invite->getAcceptedByPHID()) {
                 $action->issues[] = self::ISSUE_ACCEPTED;
                 if (!$action->userPHID) {
                     // This could be different from the user who is currently attached
                     // to the email address if the address was removed or added to a
                     // different account later. Only show it if the address was
                     // removed, since the current status is more up-to-date otherwise.
                     $action->userPHID = $invite->getAcceptedByPHID();
                 }
             } else {
                 $action->issues[] = self::ISSUE_INVITED;
             }
         }
     }
     foreach ($results as $result) {
         foreach ($result->getIssues() as $issue) {
             switch ($issue) {
                 case self::ISSUE_PARSE:
                     $result->action = self::ACTION_ERROR;
                     break;
                 case self::ISSUE_ACCEPTED:
                 case self::ISSUE_VERIFIED:
                     $result->action = self::ACTION_IGNORE;
                     break;
             }
         }
         if (!$result->action) {
             $result->action = self::ACTION_SEND;
         }
     }
     return $results;
 }