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/
57 class EmailregisterAction extends Action
60 const SETPASSWORD = 2;
61 const NEWREGISTER = 3;
62 const CONFIRMINVITE = 4;
63 const CONFIRMREGISTER = 5;
65 const CONFIRMTYPE = 'register';
70 protected $invitation;
71 protected $confirmation;
78 function prepare($argarray)
80 parent::prepare($argarray);
82 if ($this->isPost()) {
84 $this->checkSessionToken();
86 $this->email = $this->trimmed('email');
88 if (!empty($this->email)) {
89 $this->email = common_canonical_email($this->email);
90 $this->state = self::NEWEMAIL;
92 $this->state = self::SETPASSWORD;
94 $this->code = $this->trimmed('code');
96 if (empty($this->code)) {
97 // TRANS: Client exception thrown when no confirmation code was provided.
98 throw new ClientException(_m('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 // TRANS: Client exception thrown when given confirmation code was not issued.
109 throw new ClientException(_m('No such confirmation code.'), 403);
113 $this->password1 = $this->trimmed('password1');
114 $this->password2 = $this->trimmed('password2');
116 $this->tos = $this->boolean('tos');
119 $this->code = $this->trimmed('code');
121 if (empty($this->code)) {
122 $this->state = self::NEWREGISTER;
124 $this->invitation = Invitation::staticGet('code', $this->code);
125 if (!empty($this->invitation)) {
126 $this->state = self::CONFIRMINVITE;
128 $this->state = self::CONFIRMREGISTER;
129 $this->confirmation = Confirm_address::staticGet('code', $this->code);
131 if (empty($this->confirmation)) {
132 // TRANS: Client exception thrown when given confirmation code was not issued.
133 throw new ClientException(_m('No such confirmation code.'), 405);
144 switch ($this->state) {
145 case self::NEWREGISTER:
147 // TRANS: Title for registration page.
148 return _m('TITLE','Register');
150 case self::SETPASSWORD:
151 case self::CONFIRMINVITE:
152 case self::CONFIRMREGISTER:
153 // TRANS: Title for page where to register with a confirmation code.
154 return _m('TITLE','Complete registration');
162 * @param array $argarray is ignored since it's now passed in in prepare()
167 function handle($argarray=null)
169 $cur = common_current_user();
172 common_redirect(common_local_url('all', array('nickname' => $cur->nickname)));
176 switch ($this->state) {
177 case self::NEWREGISTER:
178 $this->showRegistrationForm();
181 $this->registerUser();
183 case self::CONFIRMINVITE:
184 $this->confirmRegistration();
186 case self::CONFIRMREGISTER:
187 $this->confirmRegistration();
189 case self::SETPASSWORD:
190 $this->setPassword();
196 function showRegistrationForm()
198 $this->form = new EmailRegistrationForm($this, $this->email);
202 function registerUser()
204 $old = User::staticGet('email', $this->email);
207 // TRANS: Error text when trying to register with an already registered e-mail address.
208 $this->error = sprintf(_m('A user with that email address already exists. You can use the '.
209 '<a href="%s">password recovery</a> tool to recover a missing password.'),
210 common_local_url('recoverpassword'));
211 $this->showRegistrationForm();
218 if (Event::handle('StartValidateUserEmail', array(null, $this->email, &$valid))) {
219 $valid = Validate::email($this->email, common_config('email', 'check_domain'));
220 Event::handle('EndValidateUserEmail', array(null, $this->email, &$valid));
223 // TRANS: Error text when trying to register with an invalid e-mail address.
224 $this->error = _m('Not a valid email address.');
225 $this->showRegistrationForm();
228 } catch (ClientException $e) {
229 $this->error = $e->getMessage();
230 $this->showRegistrationForm();
234 $confirm = Confirm_address::getAddress($this->email, self::CONFIRMTYPE);
236 if (empty($confirm)) {
237 $confirm = Confirm_address::saveNew(null, $this->email, 'register');
238 // TRANS: Confirmation text after initial registration.
239 $prompt = sprintf(_m('An email was sent to %s to confirm that address. Check your email inbox for instructions.'),
242 // TRANS: Confirmation text after re-requesting an e-mail confirmation code.
243 $prompt = sprintf(_m('The address %s was already registered but not confirmed. The confirmation code was resent.'),
247 $this->sendConfirmEmail($confirm);
249 $this->complete = $prompt;
254 function confirmRegistration()
256 if (!empty($this->invitation)) {
257 $email = $this->invitation->address;
258 } else if (!empty($this->confirmation)) {
259 $email = $this->confirmation->address;
262 $nickname = $this->nicknameFromEmail($email);
264 $this->form = new ConfirmRegistrationForm($this,
271 function setPassword()
273 if (!empty($this->invitation)) {
274 $email = $this->invitation->address;
275 } else if (!empty($this->confirmation)) {
276 $email = $this->confirmation->address;
278 throw new Exception('No confirmation thing.');
282 $this->error = _('You must accept the terms of service and privacy policy to register.');
284 } else if (empty($this->password1)) {
285 $this->error = _('You must set a password');
286 } else if (strlen($this->password1) < 6) {
287 $this->error = _('Password must be 6 or more characters.');
288 } else if ($this->password1 != $this->password2) {
289 $this->error = _('Passwords do not match.');
292 if (!empty($this->error)) {
293 $nickname = $this->nicknameFromEmail($email);
294 $this->form = new ConfirmRegistrationForm($this, $nickname, $this->email, $this->code);
299 $nickname = $this->nicknameFromEmail($email);
302 $this->user = User::register(array('nickname' => $nickname,
304 'password' => $this->password1,
305 'email_confirmed' => true));
306 } catch (ClientException $e) {
307 $this->error = $e->getMessage();
308 $nickname = $this->nicknameFromEmail($email);
309 $this->form = new ConfirmRegistrationForm($this, $nickname, $this->email, $this->code);
314 if (empty($this->user)) {
315 throw new Exception("Failed to register user.");
318 common_set_user($this->user);
319 // this is a real login
320 common_real_login(true);
322 // Re-init language env in case it changed (not yet, but soon)
323 common_init_language();
325 if (!empty($this->invitation)) {
326 $inviter = User::staticGet('id', $this->invitation->user_id);
327 if (!empty($inviter)) {
328 Subscription::start($inviter->getProfile(),
329 $this->user->getProfile());
332 $this->invitation->delete();
333 } else if (!empty($this->confirmation)) {
334 $this->confirmation->delete();
336 throw new Exception('No confirmation thing.');
339 common_redirect(common_local_url('doc', array('title' => 'welcome')),
343 function sendConfirmEmail($confirm)
345 $sitename = common_config('site', 'name');
347 $recipients = array($confirm->address);
349 $headers['From'] = mail_notify_from();
350 $headers['To'] = trim($confirm->address);
351 // TRANS: Subject for confirmation e-mail.
352 // TRANS: %s is the StatusNet sitename.
353 $headers['Subject'] = sprintf(_m('Confirm your registration on %s'), $sitename);
355 $confirmUrl = common_local_url('register', array('code' => $confirm->code));
357 // TRANS: Body for confirmation e-mail.
358 // TRANS: %1$s is the StatusNet sitename, %2$s is the confirmation URL.
359 $body = sprintf(_m('Someone (probably you) has requested an account on %1$s using this email address.'.
361 'To confirm the address, click the following URL or copy it into the address bar of your browser.'.
365 'If it was not you, you can safely ignore this message.'),
369 mail_send($recipients, $headers, $body);
372 function showContent()
374 if ($this->complete) {
375 $this->elementStart('p', 'success');
376 $this->raw($this->complete);
377 $this->elementEnd('p');
380 $this->elementStart('p', 'error');
381 $this->raw($this->error);
382 $this->elementEnd('p');
385 if (!empty($this->form)) {
392 * Return true if read only.
396 * @param array $args other arguments
398 * @return boolean is read only action?
400 function isReadOnly($args)
405 function nicknameFromEmail($email)
407 $parts = explode('@', $email);
409 $nickname = $parts[0];
411 $nickname = preg_replace('/[^A-Za-z0-9]/', '', $nickname);
413 $nickname = Nickname::normalize($nickname);
415 $original = $nickname;
419 while (User::staticGet('nickname', $nickname)) {
421 $nickname = $original . $n;
430 * Shows different login/register actions.
434 function showLocalNav()
436 $nav = new LoginGroupNav($this);