emailsettings.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. <?php
  2. /**
  3. * StatusNet, the distributed open-source microblogging tool
  4. *
  5. * Settings for email
  6. *
  7. * PHP version 5
  8. *
  9. * LICENCE: This program is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU Affero General Public License as published by
  11. * the Free Software Foundation, either version 3 of the License, or
  12. * (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU Affero General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Affero General Public License
  20. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  21. *
  22. * @category Settings
  23. * @package StatusNet
  24. * @author Evan Prodromou <evan@status.net>
  25. * @author Zach Copley <zach@status.net>
  26. * @copyright 2008-2009 StatusNet, Inc.
  27. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  28. * @link http://status.net/
  29. */
  30. if (!defined('GNUSOCIAL')) { exit(1); }
  31. /**
  32. * Settings for email
  33. *
  34. * @category Settings
  35. * @package StatusNet
  36. * @author Evan Prodromou <evan@status.net>
  37. * @author Zach Copley <zach@status.net>
  38. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  39. * @link http://status.net/
  40. *
  41. * @see Widget
  42. */
  43. class EmailsettingsAction extends SettingsAction
  44. {
  45. /**
  46. * Title of the page
  47. *
  48. * @return string Title of the page
  49. */
  50. function title()
  51. {
  52. // TRANS: Title for e-mail settings.
  53. return _('Email settings');
  54. }
  55. /**
  56. * Instructions for use
  57. *
  58. * @return instructions for use
  59. */
  60. function getInstructions()
  61. {
  62. // XXX: For consistency of parameters in messages, this should be a
  63. // regular parameters, replaced with sprintf().
  64. // TRANS: E-mail settings page instructions.
  65. // TRANS: %%site.name%% is the name of the site.
  66. return _('Manage how you get email from %%site.name%%.');
  67. }
  68. function showScripts()
  69. {
  70. parent::showScripts();
  71. $this->script('emailsettings.js');
  72. $this->autofocus('email');
  73. }
  74. /**
  75. * Content area of the page
  76. *
  77. * Shows a form for adding and removing email addresses and setting
  78. * email preferences.
  79. *
  80. * @return void
  81. */
  82. function showContent()
  83. {
  84. $user = $this->scoped->getUser();
  85. $this->elementStart('form', array('method' => 'post',
  86. 'id' => 'form_settings_email',
  87. 'class' => 'form_settings',
  88. 'action' =>
  89. common_local_url('emailsettings')));
  90. $this->elementStart('fieldset');
  91. $this->elementStart('fieldset', array('id' => 'settings_email_address'));
  92. // TRANS: Form legend for e-mail settings form.
  93. $this->element('legend', null, _('Email address'));
  94. $this->hidden('token', common_session_token());
  95. if ($user->email) {
  96. $this->element('p', array('id' => 'form_confirmed'), $user->email);
  97. // TRANS: Form note in e-mail settings form.
  98. $this->element('p', array('class' => 'form_note'), _('Current confirmed email address.'));
  99. $this->hidden('email', $user->email);
  100. // TRANS: Button label to remove a confirmed e-mail address.
  101. $this->submit('remove', _m('BUTTON','Remove'));
  102. } else {
  103. try {
  104. $confirm = $this->getConfirmation();
  105. $this->element('p', array('id' => 'form_unconfirmed'), $confirm->address);
  106. $this->element('p', array('class' => 'form_note'),
  107. // TRANS: Form note in e-mail settings form.
  108. _('Awaiting confirmation on this address. '.
  109. 'Check your inbox (and spam box!) for a message '.
  110. 'with further instructions.'));
  111. $this->hidden('email', $confirm->address);
  112. // TRANS: Button label to cancel an e-mail address confirmation procedure.
  113. $this->submit('cancel', _m('BUTTON','Cancel'));
  114. } catch (NoResultException $e) {
  115. $this->elementStart('ul', 'form_data');
  116. $this->elementStart('li');
  117. // TRANS: Field label for e-mail address input in e-mail settings form.
  118. $this->input('email', _('Email address'),
  119. $this->trimmed('email') ?: null,
  120. // TRANS: Instructions for e-mail address input form. Do not translate
  121. // TRANS: "example.org". It is one of the domain names reserved for
  122. // TRANS: use in examples by http://www.rfc-editor.org/rfc/rfc2606.txt.
  123. // TRANS: Any other domain may be owned by a legitimate person or
  124. // TRANS: organization.
  125. _('Email address, like "UserName@example.org"'));
  126. $this->elementEnd('li');
  127. $this->elementEnd('ul');
  128. // TRANS: Button label for adding an e-mail address in e-mail settings form.
  129. $this->submit('add', _m('BUTTON','Add'));
  130. }
  131. }
  132. $this->elementEnd('fieldset');
  133. if (common_config('emailpost', 'enabled') && $user->email) {
  134. $this->elementStart('fieldset', array('id' => 'settings_email_incoming'));
  135. // TRANS: Form legend for incoming e-mail settings form.
  136. $this->element('legend', null, _('Incoming email'));
  137. $this->elementStart('ul', 'form_data');
  138. $this->elementStart('li');
  139. $this->checkbox('emailpost',
  140. // TRANS: Checkbox label in e-mail preferences form.
  141. _('I want to post notices by email.'),
  142. $user->emailpost);
  143. $this->elementEnd('li');
  144. $this->elementEnd('ul');
  145. // Our stylesheets make the form_data list items all floats, which
  146. // creates lots of problems with trying to wrap divs around things.
  147. // This should force a break before the next section, which needs
  148. // to be separate so we can disable the things in it when the
  149. // checkbox is off.
  150. $this->elementStart('div', array('style' => 'clear: both'));
  151. $this->elementEnd('div');
  152. $this->elementStart('div', array('id' => 'emailincoming'));
  153. if ($user->incomingemail) {
  154. $this->elementStart('p');
  155. $this->element('span', 'address', $user->incomingemail);
  156. // @todo XXX: Looks a little awkward in the UI.
  157. // Something like "xxxx@identi.ca Send email ..". Needs improvement.
  158. $this->element('span', 'input_instructions',
  159. // TRANS: Form instructions for incoming e-mail form in e-mail settings.
  160. _('Send email to this address to post new notices.'));
  161. $this->elementEnd('p');
  162. // TRANS: Button label for removing a set sender e-mail address to post notices from.
  163. $this->submit('removeincoming', _m('BUTTON','Remove'));
  164. }
  165. $this->elementStart('p');
  166. if ($user->incomingemail) {
  167. // TRANS: Instructions for incoming e-mail address input form, when an address has already been assigned.
  168. $msg = _('Make a new email address for posting to; '.
  169. 'cancels the old one.');
  170. } else {
  171. // TRANS: Instructions for incoming e-mail address input form.
  172. $msg = _('To send notices via email, we need to create a unique email address for you on this server:');
  173. }
  174. $this->element('span', 'input_instructions', $msg);
  175. $this->elementEnd('p');
  176. // TRANS: Button label for adding an e-mail address to send notices from.
  177. $this->submit('newincoming', _m('BUTTON','New'));
  178. $this->elementEnd('div'); // div#emailincoming
  179. $this->elementEnd('fieldset');
  180. }
  181. $this->elementStart('fieldset', array('id' => 'settings_email_preferences'));
  182. // TRANS: Form legend for e-mail preferences form.
  183. $this->element('legend', null, _('Email preferences'));
  184. $this->elementStart('ul', 'form_data');
  185. if (Event::handle('StartEmailFormData', array($this, $this->scoped))) {
  186. $this->elementStart('li');
  187. $this->checkbox('emailnotifysub',
  188. // TRANS: Checkbox label in e-mail preferences form.
  189. _('Send me notices of new subscriptions through email.'),
  190. $user->emailnotifysub);
  191. $this->elementEnd('li');
  192. $this->elementStart('li');
  193. $this->checkbox('emailnotifymsg',
  194. // TRANS: Checkbox label in e-mail preferences form.
  195. _('Send me email when someone sends me a private message.'),
  196. $user->emailnotifymsg);
  197. $this->elementEnd('li');
  198. $this->elementStart('li');
  199. $this->checkbox('emailnotifyattn',
  200. // TRANS: Checkbox label in e-mail preferences form.
  201. _('Send me email when someone sends me an "@-reply".'),
  202. $user->emailnotifyattn);
  203. $this->elementEnd('li');
  204. $this->elementStart('li');
  205. $this->checkbox('emailnotifynudge',
  206. // TRANS: Checkbox label in e-mail preferences form.
  207. _('Allow friends to nudge me and send me an email.'),
  208. $user->emailnotifynudge);
  209. $this->elementEnd('li');
  210. Event::handle('EndEmailFormData', array($this, $this->scoped));
  211. }
  212. $this->elementEnd('ul');
  213. // TRANS: Button label to save e-mail preferences.
  214. $this->submit('save', _m('BUTTON','Save'));
  215. $this->elementEnd('fieldset');
  216. $this->elementEnd('fieldset');
  217. $this->elementEnd('form');
  218. }
  219. /**
  220. * Gets any existing email address confirmations we're waiting for
  221. *
  222. * @return Confirm_address Email address confirmation for user, or null
  223. */
  224. function getConfirmation()
  225. {
  226. $confirm = new Confirm_address();
  227. $confirm->user_id = $this->scoped->getID();
  228. $confirm->address_type = 'email';
  229. if ($confirm->find(true)) {
  230. return $confirm;
  231. }
  232. throw new NoResultException($confirm);
  233. }
  234. protected function doPost()
  235. {
  236. if ($this->arg('save')) {
  237. return $this->savePreferences();
  238. } else if ($this->arg('add')) {
  239. return $this->addAddress();
  240. } else if ($this->arg('cancel')) {
  241. return $this->cancelConfirmation();
  242. } else if ($this->arg('remove')) {
  243. return $this->removeAddress();
  244. } else if ($this->arg('removeincoming')) {
  245. return $this->removeIncoming();
  246. } else if ($this->arg('newincoming')) {
  247. return $this->newIncoming();
  248. }
  249. // TRANS: Message given submitting a form with an unknown action in e-mail settings.
  250. throw new ClientException(_('Unexpected form submission.'));
  251. }
  252. /**
  253. * Save email preferences
  254. *
  255. * @return void
  256. */
  257. function savePreferences()
  258. {
  259. if (Event::handle('StartEmailSaveForm', array($this, $this->scoped))) {
  260. $emailnotifysub = $this->booleanintstring('emailnotifysub');
  261. $emailnotifymsg = $this->booleanintstring('emailnotifymsg');
  262. $emailnotifynudge = $this->booleanintstring('emailnotifynudge');
  263. $emailnotifyattn = $this->booleanintstring('emailnotifyattn');
  264. $emailpost = $this->booleanintstring('emailpost');
  265. $user = $this->scoped->getUser();
  266. $user->query('BEGIN');
  267. $original = clone($user);
  268. $user->emailnotifysub = $emailnotifysub;
  269. $user->emailnotifymsg = $emailnotifymsg;
  270. $user->emailnotifynudge = $emailnotifynudge;
  271. $user->emailnotifyattn = $emailnotifyattn;
  272. $user->emailpost = $emailpost;
  273. $result = $user->update($original);
  274. if ($result === false) {
  275. common_log_db_error($user, 'UPDATE', __FILE__);
  276. $user->query('ROLLBACK');
  277. // TRANS: Server error thrown on database error updating e-mail preferences.
  278. throw new ServerException(_('Could not update user.'));
  279. }
  280. $user->query('COMMIT');
  281. Event::handle('EndEmailSaveForm', array($this, $this->scoped));
  282. }
  283. // TRANS: Confirmation message for successful e-mail preferences save.
  284. return _('Email preferences saved.');
  285. }
  286. /**
  287. * Add the address passed in by the user
  288. *
  289. * @return void
  290. */
  291. function addAddress()
  292. {
  293. $user = $this->scoped->getUser();
  294. $email = $this->trimmed('email');
  295. // Some validation
  296. if (empty($email)) {
  297. // TRANS: Message given saving e-mail address without having provided one.
  298. throw new ClientException(_('No email address.'));
  299. }
  300. $email = common_canonical_email($email);
  301. if (empty($email)) {
  302. // TRANS: Message given saving e-mail address that cannot be normalised.
  303. throw new ClientException(_('Cannot normalize that email address.'));
  304. }
  305. if (!Validate::email($email, common_config('email', 'check_domain'))) {
  306. // TRANS: Message given saving e-mail address that not valid.
  307. throw new ClientException(_('Not a valid email address.'));
  308. } else if ($user->email == $email) {
  309. // TRANS: Message given saving e-mail address that is already set.
  310. throw new ClientException(_('That is already your email address.'));
  311. } else if ($this->emailExists($email)) {
  312. // TRANS: Message given saving e-mail address that is already set for another user.
  313. throw new ClientException(_('That email address already belongs to another user.'));
  314. }
  315. if (Event::handle('StartAddEmailAddress', array($user, $email))) {
  316. $confirm = new Confirm_address();
  317. $confirm->address = $email;
  318. $confirm->address_type = 'email';
  319. $confirm->user_id = $user->getID();
  320. $confirm->code = common_confirmation_code(64);
  321. $result = $confirm->insert();
  322. if ($result === false) {
  323. common_log_db_error($confirm, 'INSERT', __FILE__);
  324. // TRANS: Server error thrown on database error adding e-mail confirmation code.
  325. throw new ServerException(_('Could not insert confirmation code.'));
  326. }
  327. common_debug('Sending confirmation address for user '.$user->getID().' to email '.$email);
  328. mail_confirm_address($user, $confirm->code, $user->getNickname(), $email);
  329. Event::handle('EndAddEmailAddress', array($user, $email));
  330. }
  331. // TRANS: Message given saving valid e-mail address that is to be confirmed.
  332. return _('A confirmation code was sent to the email address you added. '.
  333. 'Check your inbox (and spam box!) for the code and instructions '.
  334. 'on how to use it.');
  335. }
  336. /**
  337. * Handle a request to cancel email confirmation
  338. *
  339. * @return void
  340. */
  341. function cancelConfirmation()
  342. {
  343. $email = $this->trimmed('email');
  344. try {
  345. $confirm = $this->getConfirmation();
  346. if ($confirm->address !== $email) {
  347. // TRANS: Message given canceling e-mail address confirmation for the wrong e-mail address.
  348. throw new ClientException(_('That is the wrong email address.'));
  349. }
  350. } catch (NoResultException $e) {
  351. // TRANS: Message given canceling e-mail address confirmation that is not pending.
  352. throw new AlreadyFulfilledException(_('No pending confirmation to cancel.'));
  353. }
  354. $result = $confirm->delete();
  355. if ($result === false) {
  356. common_log_db_error($confirm, 'DELETE', __FILE__);
  357. // TRANS: Server error thrown on database error canceling e-mail address confirmation.
  358. throw new ServerException(_('Could not delete email confirmation.'));
  359. }
  360. // TRANS: Message given after successfully canceling e-mail address confirmation.
  361. return _('Email confirmation cancelled.');
  362. }
  363. /**
  364. * Handle a request to remove an address from the user's account
  365. *
  366. * @return void
  367. */
  368. function removeAddress()
  369. {
  370. $user = common_current_user();
  371. $email = $this->trimmed('email');
  372. // Maybe an old tab open...?
  373. if ($user->email !== $email) {
  374. // TRANS: Message given trying to remove an e-mail address that is not
  375. // TRANS: registered for the active user.
  376. throw new ClientException(_('That is not your email address.'));
  377. }
  378. $original = clone($user);
  379. $user->email = null;
  380. // Throws exception on failure. Also performs it within a transaction.
  381. $user->updateWithKeys($original);
  382. // TRANS: Message given after successfully removing a registered e-mail address.
  383. return _('The email address was removed.');
  384. }
  385. /**
  386. * Handle a request to remove an incoming email address
  387. *
  388. * @return void
  389. */
  390. function removeIncoming()
  391. {
  392. $user = common_current_user();
  393. if (empty($user->incomingemail)) {
  394. // TRANS: Form validation error displayed when trying to remove an incoming e-mail address while no address has been set.
  395. throw new AlreadyFulfilledException(_('No incoming email address.'));
  396. }
  397. $orig = clone($user);
  398. $user->incomingemail = null;
  399. $user->emailpost = 0;
  400. // Throws exception on failure. Also performs it within a transaction.
  401. $user->updateWithKeys($orig);
  402. // TRANS: Message given after successfully removing an incoming e-mail address.
  403. return _('Incoming email address removed.');
  404. }
  405. /**
  406. * Generate a new incoming email address
  407. *
  408. * @return void
  409. */
  410. function newIncoming()
  411. {
  412. $user = common_current_user();
  413. $orig = clone($user);
  414. $user->incomingemail = mail_new_incoming_address();
  415. $user->emailpost = 1;
  416. // Throws exception on failure. Also performs it within a transaction.
  417. $user->updateWithKeys($orig);
  418. // TRANS: Message given after successfully adding an incoming e-mail address.
  419. return _('New incoming email address added.');
  420. }
  421. /**
  422. * Does another user already have this email address?
  423. *
  424. * Email addresses are unique for users.
  425. *
  426. * @param string $email Address to check
  427. *
  428. * @return boolean Whether the email already exists.
  429. */
  430. function emailExists($email)
  431. {
  432. $user = common_current_user();
  433. $other = User::getKV('email', $email);
  434. if (!$other instanceof User) {
  435. return false;
  436. }
  437. return $other->id != $user->id;
  438. }
  439. }