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;
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 throw new ClientException(_('No confirmation code.'));
100 $this->invitation = Invitation::staticGet('code', $this->code);
102 if (!empty($this->invitation)) {
103 $this->state = self::CONFIRMINVITE;
105 $this->state = self::CONFIRMREGISTER;
106 $this->confirmation = Confirm_address::staticGet('code', $this->code);
108 if (empty($this->confirmation)) {
109 throw new ClientException(_('No such confirmation code.'), 405);
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 throw new ClientException(_('No such confirmation code.'), 405);
143 switch ($this->state) {
144 case self::NEWREGISTER:
146 // TRANS: Title for registration page.
147 return _m('TITLE','Register');
149 case self::SETPASSWORD:
150 case self::CONFIRMINVITE:
151 case self::CONFIRMREGISTER:
152 // TRANS: Title for page where to change password.
153 return _m('TITLE','Set password');
161 * @param array $argarray is ignored since it's now passed in in prepare()
166 function handle($argarray=null)
168 switch ($this->state) {
169 case self::NEWREGISTER:
170 $this->showRegistrationForm();
173 $this->registerUser();
175 case self::CONFIRMINVITE:
176 $this->confirmInvite();
178 case self::CONFIRMREGISTER:
179 $this->confirmRegister();
181 case self::SETPASSWORD:
182 $this->setPassword();
188 function showRegistrationForm()
190 $this->form = new EmailRegistrationForm($this, $this->email);
194 function registerUser()
196 $old = User::staticGet('email', $this->email);
199 $this->error = sprintf(_('A user with that email address already exists. You can use the '.
200 '<a href="%s">password recovery</a> tool to recover a missing password.'),
201 common_local_url('recoverpassword'));
202 $this->showRegistrationForm();
208 if (Event::handle('StartValidateUserEmail', array(null, $this->email, &$valid))) {
209 $valid = Validate::email($this->email, common_config('email', 'check_domain'));
210 Event::handle('EndValidateUserEmail', array(null, $this->email, &$valid));
214 $this->error = _('Not a valid email address.');
215 $this->showRegistrationForm();
218 $confirm = Confirm_address::getAddress($this->email, self::CONFIRMTYPE);
220 if (empty($confirm)) {
221 $confirm = Confirm_address::saveNew(null, $this->email, 'register');
222 $prompt = sprintf(_('An email was sent to %s to confirm that address. Check your email inbox for instructions.'),
225 $prompt = sprintf(_('The address %s was already registered but not confirmed. The confirmation code was resent.'),
229 $this->sendConfirmEmail($confirm);
231 $this->complete = $prompt;
236 function confirmInvite()
238 $this->form = new ConfirmRegisterForm($this, $this->invitation->code);
242 function confirmRegister()
244 $this->form = new ConfirmRegisterForm($this, $this->confirmation->code);
248 function setPassword()
251 $this->error = _('You must accept the terms of service and privacy policy to register.');
252 $this->form = new ConfirmRegisterForm($this, $this->code);
257 if (!empty($this->invitation)) {
258 $email = $this->invitation->address;
259 } else if (!empty($this->confirmation)) {
260 $email = $this->confirmation->address;
262 throw new Exception('No confirmation thing.');
265 $nickname = $this->nicknameFromEmail($email);
267 $this->user = User::registerNew(array('nickname' => $nickname,
269 'email_confirmed' => true));
271 if (empty($this->user)) {
272 throw new Exception("Failed to register user.");
275 if (!empty($this->invitation)) {
276 $inviter = User::staticGet('id', $this->invitation->user_id);
277 if (!empty($inviter)) {
278 Subscription::start($inviter->getProfile(),
279 $user->getProfile());
282 $this->invitation->delete();
283 } else if (!empty($this->confirmation)) {
284 $this->confirmation->delete();
286 throw new Exception('No confirmation thing.');
289 common_redirect(common_local_url('doc', array('file' => 'registered')),
293 function sendConfirmEmail($confirm, $new)
295 $sitename = common_config('site', 'name');
297 $recipients = array($confirm->address);
299 $headers['From'] = mail_notify_from();
300 $headers['To'] = trim($confirm->address);
301 $headers['Subject'] = sprintf(_('Confirm your registration on %1$s'), $sitename);
303 $body = sprintf(_('Someone (probably you) has requested an account on %1$s using this email address.'.
305 'To confirm the address, click the following URL or copy it into the address bar of your browser.'.
309 'If it was not you, you can safely ignore this message.'),
311 common_local_url('register', array('code' => $confirm->code)));
313 mail_send($recipients, $headers, $body);
316 function showContent()
318 if ($this->complete) {
319 $this->elementStart('p', 'success');
320 $this->raw($this->complete);
321 $this->elementEnd('p');
324 $this->elementStart('p', 'error');
325 $this->raw($this->error);
326 $this->elementEnd('p');
329 if (!empty($this->form)) {
336 * Return true if read only.
340 * @param array $args other arguments
342 * @return boolean is read only action?
345 function isReadOnly($args)
351 class EmailRegistrationForm extends Form
355 function __construct($out, $email)
357 parent::__construct($out);
358 $this->email = $email;
363 $this->out->element('p', 'instructions',
364 _('Enter your email address to register for an account.'));
366 $this->out->elementStart('fieldset', array('id' => 'new_bookmark_data'));
367 $this->out->elementStart('ul', 'form_data');
370 $this->out->input('email',
371 // TRANS: Field label on form for adding a new bookmark.
372 _m('LABEL','E-mail address'),
376 $this->out->elementEnd('ul');
377 $this->out->elementEnd('fieldset');
386 * Buttons for form actions
388 * Submit and cancel buttons (or whatever)
389 * Sub-classes should overload this to show their own buttons.
394 function formActions()
396 // TRANS: Button text for action to save a new bookmark.
397 $this->out->submit('submit', _m('BUTTON', 'Register'));
403 * Should be unique on the page. Sub-classes should overload this
404 * to show their own IDs.
406 * @return int ID of the form
411 return 'form_email_registration';
415 * Action of the form.
417 * URL to post to. Should be overloaded by subclasses to give
418 * somewhere to post to.
420 * @return string URL to post to
425 return common_local_url('register');
430 return 'form_email_registration';