3 * StatusNet - the distributed open-source microblogging tool
4 * Copyright (C) 2011, StatusNet, Inc.
6 * Register a user by their email address
10 * This program is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU Affero General Public License as published by
12 * the Free Software Foundation, either version 3 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Affero General Public License for more details.
20 * You should have received a copy of the GNU Affero General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23 * @category Email registration
25 * @author Evan Prodromou <evan@status.net>
26 * @copyright 2011 StatusNet, Inc.
27 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
28 * @link http://status.net/
31 if (!defined('STATUSNET')) {
32 // This check helps protect against security problems;
33 // your code file can't be executed directly from the web.
40 * There are four cases where we're called:
42 * 1. GET, no arguments. Initial registration; ask for an email address.
43 * 2. POST, email address argument. Initial registration; send an email to confirm.
44 * 3. GET, code argument. Confirming an invitation or a registration; look them up,
45 * create the relevant user if possible, login as that user, and
46 * show a password-entry form.
47 * 4. POST, password argument. After confirmation, set the password for the new
48 * user, and redirect to a registration complete action with some instructions.
52 * @author Evan Prodromou <evan@status.net>
53 * @copyright 2011 StatusNet, Inc.
54 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
55 * @link http://status.net/
58 class EmailregisterAction extends Action
61 const SETPASSWORD = 2;
62 const NEWREGISTER = 3;
63 const CONFIRMINVITE = 4;
64 const CONFIRMREGISTER = 5;
66 const CONFIRMTYPE = 'register';
71 protected $invitation;
72 protected $confirmation;
79 function prepare($argarray)
81 parent::prepare($argarray);
83 if ($this->isPost()) {
85 $this->checkSessionToken();
87 $this->email = $this->trimmed('email');
89 if (!empty($this->email)) {
90 $this->email = common_canonical_email($this->email);
91 $this->state = self::NEWEMAIL;
93 $this->state = self::SETPASSWORD;
95 $this->code = $this->trimmed('code');
97 if (empty($this->code)) {
98 throw new ClientException(_('No confirmation code.'));
101 $this->invitation = Invitation::staticGet('code', $this->code);
103 if (empty($this->invitation)) {
105 $this->confirmation = Confirm_address::staticGet('code', $this->code);
107 if (empty($this->confirmation)) {
108 throw new ClientException(_('No such confirmation code.'), 403);
112 $this->password1 = $this->trimmed('password1');
113 $this->password2 = $this->trimmed('password2');
115 $this->tos = $this->boolean('tos');
118 $this->code = $this->trimmed('code');
120 if (empty($this->code)) {
121 $this->state = self::NEWREGISTER;
123 $this->invitation = Invitation::staticGet('code', $this->code);
124 if (!empty($this->invitation)) {
125 $this->state = self::CONFIRMINVITE;
127 $this->state = self::CONFIRMREGISTER;
128 $this->confirmation = Confirm_address::staticGet('code', $this->code);
130 if (empty($this->confirmation)) {
131 throw new ClientException(_('No such confirmation code.'), 405);
142 switch ($this->state) {
143 case self::NEWREGISTER:
145 // TRANS: Title for registration page.
146 return _m('TITLE','Register');
148 case self::SETPASSWORD:
149 case self::CONFIRMINVITE:
150 case self::CONFIRMREGISTER:
151 // TRANS: Title for page where to change password.
152 return _m('TITLE','Complete registration');
160 * @param array $argarray is ignored since it's now passed in in prepare()
165 function handle($argarray=null)
167 $cur = common_current_user();
170 common_redirect(common_local_url('all', array('nickname' => $cur->nickname)));
174 switch ($this->state) {
175 case self::NEWREGISTER:
176 $this->showRegistrationForm();
179 $this->registerUser();
181 case self::CONFIRMINVITE:
182 $this->confirmRegistration();
184 case self::CONFIRMREGISTER:
185 $this->confirmRegistration();
187 case self::SETPASSWORD:
188 $this->setPassword();
194 function showRegistrationForm()
196 $this->form = new EmailRegistrationForm($this, $this->email);
200 function registerUser()
202 $old = User::staticGet('email', $this->email);
205 $this->error = sprintf(_('A user with that email address already exists. You can use the '.
206 '<a href="%s">password recovery</a> tool to recover a missing password.'),
207 common_local_url('recoverpassword'));
208 $this->showRegistrationForm();
215 if (Event::handle('StartValidateUserEmail', array(null, $this->email, &$valid))) {
216 $valid = Validate::email($this->email, common_config('email', 'check_domain'));
217 Event::handle('EndValidateUserEmail', array(null, $this->email, &$valid));
220 $this->error = _('Not a valid email address.');
221 $this->showRegistrationForm();
224 } catch (ClientException $e) {
225 $this->error = $e->getMessage();
226 $this->showRegistrationForm();
230 $confirm = Confirm_address::getAddress($this->email, self::CONFIRMTYPE);
232 if (empty($confirm)) {
233 $confirm = Confirm_address::saveNew(null, $this->email, 'register');
234 $prompt = sprintf(_('An email was sent to %s to confirm that address. Check your email inbox for instructions.'),
237 $prompt = sprintf(_('The address %s was already registered but not confirmed. The confirmation code was resent.'),
241 $this->sendConfirmEmail($confirm);
243 $this->complete = $prompt;
248 function confirmRegistration()
250 if (!empty($this->invitation)) {
251 $email = $this->invitation->address;
252 } else if (!empty($this->confirmation)) {
253 $email = $this->confirmation->address;
256 $nickname = $this->nicknameFromEmail($email);
258 $this->form = new ConfirmRegistrationForm($this,
265 function setPassword()
267 if (Event::handle('StartRegistrationTry', array($this))) {
268 if (!empty($this->invitation)) {
269 $email = $this->invitation->address;
270 } else if (!empty($this->confirmation)) {
271 $email = $this->confirmation->address;
273 throw new Exception('No confirmation thing.');
277 $this->error = _('You must accept the terms of service and privacy policy to register.');
278 $nickname = $this->nicknameFromEmail($email);
279 $this->form = new ConfirmRegistrationForm($this, $nickname, $this->email, $this->code);
284 $nickname = $this->nicknameFromEmail($email);
286 $this->user = User::register(array('nickname' => $nickname,
288 'email_confirmed' => true));
290 if (empty($this->user)) {
291 throw new Exception("Failed to register user.");
294 common_set_user($this->user);
295 // this is a real login
296 common_real_login(true);
298 // Re-init language env in case it changed (not yet, but soon)
299 common_init_language();
301 if (!empty($this->invitation)) {
302 $inviter = User::staticGet('id', $this->invitation->user_id);
303 if (!empty($inviter)) {
304 Subscription::start($inviter->getProfile(),
305 $this->user->getProfile());
308 $this->invitation->delete();
309 } else if (!empty($this->confirmation)) {
310 $this->confirmation->delete();
312 throw new Exception('No confirmation thing.');
315 Event::handle('EndRegistrationTry', array($this));
318 common_redirect(common_local_url('doc', array('title' => 'welcome')),
322 function sendConfirmEmail($confirm)
324 $sitename = common_config('site', 'name');
326 $recipients = array($confirm->address);
328 $headers['From'] = mail_notify_from();
329 $headers['To'] = trim($confirm->address);
330 $headers['Subject'] = sprintf(_('Confirm your registration on %1$s'), $sitename);
332 $confirmUrl = common_local_url('register', array('code' => $confirm->code));
334 $body = sprintf(_('Someone (probably you) has requested an account on %1$s using this email address.'.
336 'To confirm the address, click the following URL or copy it into the address bar of your browser.'.
340 'If it was not you, you can safely ignore this message.'),
344 mail_send($recipients, $headers, $body);
347 function showContent()
349 if ($this->complete) {
350 $this->elementStart('p', 'success');
351 $this->raw($this->complete);
352 $this->elementEnd('p');
355 $this->elementStart('p', 'error');
356 $this->raw($this->error);
357 $this->elementEnd('p');
360 if (!empty($this->form)) {
367 * Return true if read only.
371 * @param array $args other arguments
373 * @return boolean is read only action?
376 function isReadOnly($args)
381 function nicknameFromEmail($email)
383 $parts = explode('@', $email);
385 $nickname = $parts[0];
387 $nickname = preg_replace('/[^A-Za-z0-9]/', '', $nickname);
389 $nickname = Nickname::normalize($nickname);
391 $original = $nickname;
395 while (User::staticGet('nickname', $nickname)) {
397 $nickname = $original . $n;
406 * Shows different login/register actions.
411 function showLocalNav()
413 $nav = new LoginGroupNav($this);