3 final class PhabricatorAuthInviteAction
extends Phobject
{
8 private $issues = array();
11 const ACTION_SEND
= 'invite.send';
12 const ACTION_ERROR
= 'invite.error';
13 const ACTION_IGNORE
= 'invite.ignore';
15 const ISSUE_PARSE
= 'invite.parse';
16 const ISSUE_DUPLICATE
= 'invite.duplicate';
17 const ISSUE_UNVERIFIED
= 'invite.unverified';
18 const ISSUE_VERIFIED
= 'invite.verified';
19 const ISSUE_INVITED
= 'invite.invited';
20 const ISSUE_ACCEPTED
= 'invite.accepted';
22 public function getRawInput() {
23 return $this->rawInput
;
26 public function getEmailAddress() {
27 return $this->emailAddress
;
30 public function getUserPHID() {
31 return $this->userPHID
;
34 public function getIssues() {
38 public function setAction($action) {
39 $this->action
= $action;
43 public function getAction() {
47 public function willSend() {
48 return ($this->action
== self
::ACTION_SEND
);
51 public function getShortNameForIssue($issue) {
53 self
::ISSUE_PARSE
=> pht('Not a Valid Email Address'),
54 self
::ISSUE_DUPLICATE
=> pht('Address Duplicated in Input'),
55 self
::ISSUE_UNVERIFIED
=> pht('Unverified User Email'),
56 self
::ISSUE_VERIFIED
=> pht('Verified User Email'),
57 self
::ISSUE_INVITED
=> pht('Previously Invited'),
58 self
::ISSUE_ACCEPTED
=> pht('Already Accepted Invite'),
61 return idx($map, $issue);
64 public function getShortNameForAction($action) {
66 self
::ACTION_SEND
=> pht('Will Send Invite'),
67 self
::ACTION_ERROR
=> pht('Address Error'),
68 self
::ACTION_IGNORE
=> pht('Will Ignore Address'),
71 return idx($map, $action);
74 public function getIconForAction($action) {
76 case self
::ACTION_SEND
:
77 $icon = 'fa-envelope-o';
80 case self
::ACTION_IGNORE
:
84 case self
::ACTION_ERROR
:
85 $icon = 'fa-exclamation-triangle';
90 return id(new PHUIIconView())
91 ->setIcon("{$icon} {$color}");
94 public static function newActionListFromAddresses(
95 PhabricatorUser
$viewer,
99 foreach ($addresses as $address) {
100 $result = new PhabricatorAuthInviteAction();
101 $result->rawInput
= $address;
103 $email = new PhutilEmailAddress($address);
104 $result->emailAddress
= phutil_utf8_strtolower($email->getAddress());
106 if (!preg_match('/^\S+@\S+\.\S+\z/', $result->emailAddress
)) {
107 $result->issues
[] = self
::ISSUE_PARSE
;
110 $results[] = $result;
113 // Identify duplicates.
114 $address_groups = mgroup($results, 'getEmailAddress');
115 foreach ($address_groups as $address => $group) {
116 if (count($group) > 1) {
117 foreach ($group as $action) {
118 $action->issues
[] = self
::ISSUE_DUPLICATE
;
123 // Identify addresses which are already in the system.
124 $addresses = mpull($results, 'getEmailAddress');
125 $email_objects = id(new PhabricatorUserEmail())->loadAllWhere(
129 $email_map = array();
130 foreach ($email_objects as $email_object) {
131 $address_key = phutil_utf8_strtolower($email_object->getAddress());
132 $email_map[$address_key] = $email_object;
135 // Identify outstanding invites.
136 $invites = id(new PhabricatorAuthInviteQuery())
138 ->withEmailAddresses($addresses)
140 $invite_map = mpull($invites, null, 'getEmailAddress');
142 foreach ($results as $action) {
143 $email = idx($email_map, $action->getEmailAddress());
145 if ($email->getUserPHID()) {
146 $action->userPHID
= $email->getUserPHID();
147 if ($email->getIsVerified()) {
148 $action->issues
[] = self
::ISSUE_VERIFIED
;
150 $action->issues
[] = self
::ISSUE_UNVERIFIED
;
155 $invite = idx($invite_map, $action->getEmailAddress());
157 if ($invite->getAcceptedByPHID()) {
158 $action->issues
[] = self
::ISSUE_ACCEPTED
;
159 if (!$action->userPHID
) {
160 // This could be different from the user who is currently attached
161 // to the email address if the address was removed or added to a
162 // different account later. Only show it if the address was
163 // removed, since the current status is more up-to-date otherwise.
164 $action->userPHID
= $invite->getAcceptedByPHID();
167 $action->issues
[] = self
::ISSUE_INVITED
;
172 foreach ($results as $result) {
173 foreach ($result->getIssues() as $issue) {
175 case self
::ISSUE_PARSE
:
176 $result->action
= self
::ACTION_ERROR
;
178 case self
::ISSUE_ACCEPTED
:
179 case self
::ISSUE_VERIFIED
:
180 $result->action
= self
::ACTION_IGNORE
;
184 if (!$result->action
) {
185 $result->action
= self
::ACTION_SEND
;
192 public function sendInvite(PhabricatorUser
$actor, $template) {
193 if (!$this->willSend()) {
194 throw new Exception(pht('Invite action is not a send action!'));
197 if (!preg_match('/{\$INVITE_URI}/', $template)) {
198 throw new Exception(pht('Invite template does not include invite URI!'));
201 PhabricatorWorker
::scheduleTask(
202 'PhabricatorAuthInviteWorker',
204 'address' => $this->getEmailAddress(),
205 'template' => $template,
206 'authorPHID' => $actor->getPHID(),