smssettings.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. <?php
  2. // This file is part of GNU social - https://www.gnu.org/software/social
  3. //
  4. // GNU social is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Affero General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // GNU social is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Affero General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Affero General Public License
  15. // along with GNU social. If not, see <http://www.gnu.org/licenses/>.
  16. /**
  17. * Settings for SMS.
  18. *
  19. * @category Setting
  20. * @package GNUsocial
  21. * @author Evan Prodromou <evan@status.net>
  22. * @copyright 2008-2009 StatusNet, Inc.
  23. * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
  24. */
  25. defined('GNUSOCIAL') || die();
  26. /**
  27. * Settings for SMS
  28. *
  29. * @copyright 2008-2009 StatusNet, Inc.
  30. * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
  31. *
  32. * @see SettingsAction
  33. */
  34. class SmssettingsAction extends SettingsAction
  35. {
  36. protected function doPreparation()
  37. {
  38. if (!common_config('sms', 'enabled')) {
  39. // TRANS: Message given in the SMS settings if SMS is not enabled on the site.
  40. throw new ServerException(_('SMS is not available.'));
  41. }
  42. }
  43. /**
  44. * Title of the page
  45. *
  46. * @return string Title of the page
  47. */
  48. public function title()
  49. {
  50. // TRANS: Title for SMS settings.
  51. return _('SMS settings');
  52. }
  53. /**
  54. * Instructions for use
  55. *
  56. * @return instructions for use
  57. */
  58. public function getInstructions()
  59. {
  60. // XXX: For consistency of parameters in messages, this should be a
  61. // regular parameters, replaced with sprintf().
  62. // TRANS: SMS settings page instructions.
  63. // TRANS: %%site.name%% is the name of the site.
  64. return _('You can receive SMS messages through email from %%site.name%%.');
  65. }
  66. public function showScripts()
  67. {
  68. parent::showScripts();
  69. $this->autofocus('sms');
  70. }
  71. /**
  72. * Content area of the page
  73. *
  74. * Shows a form for adding and removing SMS phone numbers and setting
  75. * SMS preferences.
  76. *
  77. * @return void
  78. */
  79. public function showContent()
  80. {
  81. $user = $this->scoped->getUser();
  82. $this->elementStart('form', array('method' => 'post',
  83. 'id' => 'form_settings_sms',
  84. 'class' => 'form_settings',
  85. 'action' =>
  86. common_local_url('smssettings')));
  87. $this->elementStart('fieldset', array('id' => 'settings_sms_address'));
  88. // TRANS: Form legend for SMS settings form.
  89. $this->element('legend', null, _('SMS address'));
  90. $this->hidden('token', common_session_token());
  91. if ($user->sms) {
  92. $carrier = $user->getCarrier();
  93. $this->element(
  94. 'p',
  95. 'form_confirmed',
  96. $user->sms . ' (' . $carrier->name . ')'
  97. );
  98. $this->element(
  99. 'p',
  100. 'form_guide',
  101. // TRANS: Form guide in SMS settings form.
  102. _('Current confirmed SMS-enabled phone number.')
  103. );
  104. $this->hidden('sms', $user->sms);
  105. $this->hidden('carrier', $user->carrier);
  106. // TRANS: Button label to remove a confirmed SMS address.
  107. $this->submit('remove', _m('BUTTON', 'Remove'));
  108. } else {
  109. try {
  110. $confirm = $this->getConfirmation();
  111. $carrier = Sms_carrier::getKV($confirm->address_extra);
  112. $this->element(
  113. 'p',
  114. 'form_unconfirmed',
  115. $confirm->address . ' (' . $carrier->name . ')'
  116. );
  117. $this->element(
  118. 'p',
  119. 'form_guide',
  120. // TRANS: Form guide in IM settings form.
  121. _('Awaiting confirmation on this phone number.')
  122. );
  123. $this->hidden('sms', $confirm->address);
  124. $this->hidden('carrier', $confirm->address_extra);
  125. // TRANS: Button label to cancel a SMS address confirmation procedure.
  126. $this->submit('cancel', _m('BUTTON', 'Cancel'));
  127. $this->elementStart('ul', 'form_data');
  128. $this->elementStart('li');
  129. // TRANS: Field label for SMS address input in SMS settings form.
  130. $this->input(
  131. 'code',
  132. _('Confirmation code'),
  133. null,
  134. // TRANS: Form field instructions in SMS settings form.
  135. _('Enter the code you received on your phone.')
  136. );
  137. $this->elementEnd('li');
  138. $this->elementEnd('ul');
  139. // TRANS: Button label to confirm SMS confirmation code in SMS settings.
  140. $this->submit('confirm', _m('BUTTON', 'Confirm'));
  141. } catch (NoResultException $e) {
  142. $this->elementStart('ul', 'form_data');
  143. $this->elementStart('li');
  144. // TRANS: Field label for SMS phone number input in SMS settings form.
  145. $this->input(
  146. 'sms',
  147. _('SMS phone number'),
  148. ($this->arg('sms') ?: null),
  149. // TRANS: SMS phone number input field instructions in SMS settings form.
  150. _('Phone number, no punctuation or spaces, ' .
  151. 'with area code.')
  152. );
  153. $this->elementEnd('li');
  154. $this->elementEnd('ul');
  155. $this->carrierSelect();
  156. // TRANS: Button label for adding a SMS phone number in SMS settings form.
  157. $this->submit('add', _m('BUTTON', 'Add'));
  158. }
  159. }
  160. $this->elementEnd('fieldset');
  161. if ($user->sms) {
  162. $this->elementStart('fieldset', ['id' => 'settings_sms_incoming_email']);
  163. // XXX: Confused! This is about SMS. Should this message be updated?
  164. // TRANS: Form legend for incoming SMS settings form.
  165. $this->element('legend', null, _('Incoming email'));
  166. if ($user->incomingemail) {
  167. $this->element('p', 'form_unconfirmed', $user->incomingemail);
  168. $this->element(
  169. 'p',
  170. 'form_note',
  171. // XXX: Confused! This is about SMS. Should this message be updated?
  172. // TRANS: Form instructions for incoming SMS e-mail address form in SMS settings.
  173. _('Send email to this address to post new notices.')
  174. );
  175. // TRANS: Button label for removing a set sender SMS e-mail address to post notices from.
  176. $this->submit('removeincoming', _m('BUTTON', 'Remove'));
  177. }
  178. $this->element(
  179. 'p',
  180. 'form_guide',
  181. // XXX: Confused! This is about SMS. Should this message be updated?
  182. // TRANS: Instructions for incoming SMS e-mail address input form.
  183. _('Make a new email address for posting to; ' .
  184. 'cancels the old one.')
  185. );
  186. // TRANS: Button label for adding an SMS e-mail address to send notices from.
  187. $this->submit('newincoming', _m('BUTTON', 'New'));
  188. $this->elementEnd('fieldset');
  189. }
  190. $this->elementStart('fieldset', array('id' => 'settings_sms_preferences'));
  191. // TRANS: Form legend for SMS preferences form.
  192. $this->element('legend', null, _('SMS preferences'));
  193. $this->elementStart('ul', 'form_data');
  194. $this->elementStart('li');
  195. $this->checkbox(
  196. 'smsnotify',
  197. // TRANS: Checkbox label in SMS preferences form.
  198. _('Send me notices through SMS; ' .
  199. 'I understand I may incur ' .
  200. 'exorbitant charges from my carrier.'),
  201. $user->smsnotify
  202. );
  203. $this->elementEnd('li');
  204. $this->elementEnd('ul');
  205. // TRANS: Button label to save SMS preferences.
  206. $this->submit('save', _m('BUTTON', 'Save'));
  207. $this->elementEnd('fieldset');
  208. $this->elementEnd('form');
  209. }
  210. /**
  211. * Get a pending confirmation, if any, for this user
  212. *
  213. * @return void
  214. *
  215. * @todo very similar to EmailsettingsAction::getConfirmation(); refactor?
  216. */
  217. public function getConfirmation()
  218. {
  219. $confirm = new Confirm_address();
  220. $confirm->user_id = $this->scoped->getID();
  221. $confirm->address_type = 'sms';
  222. if ($confirm->find(true)) {
  223. return $confirm;
  224. }
  225. throw new NoResultException($confirm);
  226. }
  227. protected function doPost()
  228. {
  229. if ($this->arg('save')) {
  230. return $this->savePreferences();
  231. } elseif ($this->arg('add')) {
  232. return $this->addAddress();
  233. } elseif ($this->arg('cancel')) {
  234. return $this->cancelConfirmation();
  235. } elseif ($this->arg('remove')) {
  236. return $this->removeAddress();
  237. } elseif ($this->arg('removeincoming')) {
  238. return $this->removeIncoming();
  239. } elseif ($this->arg('newincoming')) {
  240. return $this->newIncoming();
  241. } elseif ($this->arg('confirm')) {
  242. return $this->confirmCode();
  243. }
  244. // TRANS: Message given submitting a form with an unknown action in SMS settings.
  245. throw new ClientException(_('Unexpected form submission.'));
  246. }
  247. /**
  248. * Handle a request to save preferences
  249. *
  250. * Sets the user's SMS preferences in the DB.
  251. *
  252. * @return void
  253. */
  254. public function savePreferences()
  255. {
  256. $user = $this->scoped->getUser();
  257. $user->query('BEGIN');
  258. $original = clone($user);
  259. $user->smsnotify = $this->boolean('smsnotify');
  260. $result = $user->update($original);
  261. if ($result === false) {
  262. common_log_db_error($user, 'UPDATE', __FILE__);
  263. // TRANS: Server error thrown on database error updating SMS preferences.
  264. throw new ServerException(_('Could not update user.'));
  265. }
  266. $user->query('COMMIT');
  267. // TRANS: Confirmation message for successful SMS preferences save.
  268. return _('SMS preferences saved.');
  269. }
  270. /**
  271. * Add a new SMS number for confirmation
  272. *
  273. * When the user requests a new SMS number, sends a confirmation
  274. * message.
  275. *
  276. * @return void
  277. */
  278. public function addAddress()
  279. {
  280. $user = common_current_user();
  281. $sms = $this->trimmed('sms');
  282. $carrier_id = $this->trimmed('carrier');
  283. // Some validation
  284. if (empty($sms)) {
  285. // TRANS: Message given saving SMS phone number without having provided one.
  286. throw new ClientException(_('No phone number.'));
  287. }
  288. if (empty($carrier_id)) {
  289. // TRANS: Message given saving SMS phone number without having selected a carrier.
  290. throw new ClientException(_('No carrier selected.'));
  291. }
  292. $sms = common_canonical_sms($sms);
  293. if ($user->sms === $sms) {
  294. // TRANS: Message given saving SMS phone number that is already set.
  295. throw new AlreadyFulfilledException(_('That is already your phone number.'));
  296. } elseif ($this->smsExists($sms)) {
  297. // TRANS: Message given saving SMS phone number that is already set for another user.
  298. throw new ClientException(_('That phone number already belongs to another user.'));
  299. }
  300. $confirm = new Confirm_address();
  301. $confirm->address = $sms;
  302. $confirm->address_extra = $carrier_id;
  303. $confirm->address_type = 'sms';
  304. $confirm->user_id = $this->scoped->getID();
  305. $confirm->code = common_confirmation_code(40);
  306. $result = $confirm->insert();
  307. if ($result === false) {
  308. common_log_db_error($confirm, 'INSERT', __FILE__);
  309. // TRANS: Server error thrown on database error adding SMS confirmation code.
  310. $this->serverError(_('Could not insert confirmation code.'));
  311. }
  312. $carrier = Sms_carrier::getKV($carrier_id);
  313. mail_confirm_sms(
  314. $confirm->code,
  315. $user->nickname,
  316. $carrier->toEmailAddress($sms)
  317. );
  318. // TRANS: Message given saving valid SMS phone number that is to be confirmed.
  319. return _('A confirmation code was sent to the phone number you added. '.
  320. 'Check your phone for the code and instructions '.
  321. 'on how to use it.');
  322. }
  323. /**
  324. * Cancel a pending confirmation
  325. *
  326. * Cancels the confirmation.
  327. *
  328. * @return void
  329. */
  330. public function cancelConfirmation()
  331. {
  332. $sms = $this->trimmed('sms');
  333. $carrier = $this->trimmed('carrier');
  334. try {
  335. $confirm = $this->getConfirmation();
  336. if ($confirm->address != $sms) {
  337. // TRANS: Message given canceling SMS phone number confirmation for the wrong phone number.
  338. throw new ClientException(_('That is the wrong confirmation number.'));
  339. }
  340. } catch (NoResultException $e) {
  341. // TRANS: Message given canceling SMS phone number confirmation that is not pending.
  342. throw new AlreadyFulfilledException(_('No pending confirmation to cancel.'));
  343. }
  344. $confirm->delete();
  345. // TRANS: Message given after successfully canceling SMS phone number confirmation.
  346. return _('SMS confirmation cancelled.');
  347. }
  348. /**
  349. * Remove a phone number from the user's account
  350. *
  351. * @return void
  352. */
  353. public function removeAddress()
  354. {
  355. $user = $this->scoped->getUser();
  356. $sms = $this->arg('sms');
  357. $carrier = $this->arg('carrier');
  358. // Maybe an old tab open...?
  359. if ($user->sms != $sms) {
  360. // TRANS: Message given trying to remove an SMS phone number that is not
  361. // TRANS: registered for the active user.
  362. throw new ClientException(_('That is not your phone number.'));
  363. }
  364. $original = clone($user);
  365. $user->sms = DB_DataObject_Cast::sql('NULL');
  366. $user->carrier = DB_DataObject_Cast::sql('NULL');
  367. $user->smsemail = DB_DataObject_Cast::sql('NULL');
  368. // Throws exception on failure. Also performs it within a transaction.
  369. $user->updateWithKeys($original);
  370. // TRANS: Message given after successfully removing a registered SMS phone number.
  371. return _('The SMS phone number was removed.');
  372. }
  373. /**
  374. * Does this sms number exist in our database?
  375. *
  376. * Also checks if it belongs to someone else
  377. *
  378. * @param string $sms phone number to check
  379. *
  380. * @return boolean does the number exist
  381. */
  382. public function smsExists($sms)
  383. {
  384. $other = User::getKV('sms', $sms);
  385. if (!$other instanceof User) {
  386. return false;
  387. }
  388. return !$this->scoped->sameAs($other->getProfile());
  389. }
  390. /**
  391. * Show a drop-down box with all the SMS carriers in the DB
  392. *
  393. * @return void
  394. */
  395. public function carrierSelect()
  396. {
  397. $carrier = new Sms_carrier();
  398. $cnt = $carrier->find();
  399. $this->elementStart('ul', 'form_data');
  400. $this->elementStart('li');
  401. // TRANS: Label for mobile carrier dropdown menu in SMS settings.
  402. $this->element('label', array('for' => 'carrier'), _('Mobile carrier'));
  403. $this->elementStart('select', array('name' => 'carrier',
  404. 'id' => 'carrier'));
  405. $this->element(
  406. 'option',
  407. ['value' => 0],
  408. // TRANS: Default option for mobile carrier dropdown menu in SMS settings.
  409. _('Select a carrier')
  410. );
  411. while ($carrier->fetch()) {
  412. $this->element(
  413. 'option',
  414. ['value' => $carrier->id],
  415. $carrier->name
  416. );
  417. }
  418. $this->elementEnd('select');
  419. $this->element(
  420. 'p',
  421. 'form_guide',
  422. // TRANS: Form instructions for mobile carrier dropdown menu in SMS settings.
  423. // TRANS: %s is an administrative contact's e-mail address.
  424. sprintf(
  425. _('Mobile carrier for your phone. ' .
  426. 'If you know a carrier that accepts ' .
  427. 'SMS over email but isn\'t listed here, ' .
  428. 'send email to let us know at %s.'),
  429. common_config('site', 'email')
  430. )
  431. );
  432. $this->elementEnd('li');
  433. $this->elementEnd('ul');
  434. }
  435. /**
  436. * Confirm an SMS confirmation code
  437. *
  438. * Redirects to the confirmaddress page for this code
  439. *
  440. * @return void
  441. */
  442. public function confirmCode()
  443. {
  444. $code = $this->trimmed('code');
  445. if (empty($code)) {
  446. // TRANS: Message given saving SMS phone number confirmation code without having provided one.
  447. throw new ClientException(_('No code entered.'));
  448. }
  449. common_redirect(common_local_url('confirmaddress', array('code' => $code)), 303);
  450. }
  451. /**
  452. * Handle a request to remove an incoming email address
  453. *
  454. * @return void
  455. */
  456. public function removeIncoming()
  457. {
  458. $user = common_current_user();
  459. if (!$user->incomingemail) {
  460. // TRANS: Form validation error displayed when trying to remove an incoming e-mail address while no address has been set.
  461. throw new ClientException(_('No incoming email address.'));
  462. }
  463. $orig = clone($user);
  464. $user->incomingemail = DB_DataObject_Cast::sql('NULL');
  465. // Throws exception on failure. Also performs it within a transaction.
  466. $user->updateWithKeys($orig);
  467. // TRANS: Confirmation text after updating SMS settings.
  468. return _('Incoming email address removed.');
  469. }
  470. /**
  471. * Generate a new incoming email address
  472. *
  473. * @return void
  474. *
  475. * @see Emailsettings::newIncoming
  476. */
  477. public function newIncoming()
  478. {
  479. $user = $this->scoped->getUser();
  480. $orig = clone($user);
  481. $user->incomingemail = mail_new_incoming_address();
  482. // Throws exception on failure. Also performs it within a transaction.
  483. $user->updateWithKeys($orig);
  484. // TRANS: Confirmation text after updating SMS settings.
  485. return _('New incoming email address added.');
  486. }
  487. }