clockworksms.lib.php 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671
  1. <?php
  2. /* For licensing terms, see /vendor/license.txt */
  3. /**
  4. * Class Clockworksms
  5. * This script handles incoming SMS information, process it and sends an SMS if everything is right
  6. *
  7. * @package chamilo.plugin.clockworksms.lib
  8. * @author Imanol Losada <imanol.losada@beeznest.com>
  9. *
  10. * Clockworksms-Chamilo connector class
  11. */
  12. class Clockworksms implements SmsPluginLibraryInterface
  13. {
  14. public $apiKey;
  15. public $api;
  16. public $plugin_enabled = false;
  17. /**
  18. * Constructor (generates a connection to the API)
  19. * @param string Clockworksms API key required to use the plugin
  20. * @return void
  21. */
  22. public function __construct($apiKey = null)
  23. {
  24. $plugin = ClockworksmsPlugin::create();
  25. $clockWorkSMSPlugin = $plugin->get('tool_enable');
  26. if (empty($apiKey)) {
  27. $clockWorkSMSApiKey = $plugin->get('api_key');
  28. } else {
  29. $clockWorkSMSApiKey = $apiKey;
  30. }
  31. $this->table = Database::get_main_table('user_field_values');
  32. if ($clockWorkSMSPlugin == true) {
  33. $this->apiKey = $clockWorkSMSApiKey;
  34. // Setting Clockworksms api
  35. if (!defined('CONFIG_SECURITY_API_KEY')) {
  36. define('CONFIG_SECURITY_API_KEY', $this->apiKey);
  37. }
  38. $trimmedApiKey = trim(CONFIG_SECURITY_API_KEY);
  39. if (!empty($trimmedApiKey)) {
  40. $this->api = new Clockwork(CONFIG_SECURITY_API_KEY);
  41. } else {
  42. $this->api = new Clockwork(' ');
  43. $recipient_name = api_get_person_name(
  44. api_get_setting('administratorName'),
  45. api_get_setting('administratorSurname'),
  46. null,
  47. PERSON_NAME_EMAIL_ADDRESS
  48. );
  49. $email_form = api_get_setting('emailAdministrator');
  50. $emailsubject = 'Clockworksms error';
  51. $emailbody = 'Key cannot be blank';
  52. $sender_name = $recipient_name;
  53. $email_admin = $email_form;
  54. api_mail_html(
  55. $recipient_name,
  56. $email_form,
  57. $emailsubject,
  58. $emailbody,
  59. $sender_name,
  60. $email_admin
  61. );
  62. }
  63. $this->plugin_enabled = true;
  64. }
  65. }
  66. /**
  67. * @inheritdoc
  68. */
  69. public function getMobilePhoneNumberById($userId)
  70. {
  71. $mobilePhoneNumberExtraField = new ExtraField('user');
  72. $mobilePhoneNumberExtraField = $mobilePhoneNumberExtraField->get_handler_field_info_by_field_variable('mobile_phone_number');
  73. $mobilePhoneNumberExtraFieldValue = new ExtraFieldValue('user');
  74. $mobilePhoneNumberExtraFieldValue = $mobilePhoneNumberExtraFieldValue->get_values_by_handler_and_field_id($userId, $mobilePhoneNumberExtraField['id']);
  75. return $mobilePhoneNumberExtraFieldValue['value'];
  76. }
  77. /**
  78. * send (sends an SMS to the user)
  79. * @param array Data needed to send the SMS. It is mandatory to include the
  80. * 'smsType' and 'userId' (or 'mobilePhoneNumber') fields at least.
  81. * More data may be neccesary depending on the message type
  82. * Example: $additional_parameters = array(
  83. * 'smsType' => EXAMPLE_SMS_TYPE,
  84. * 'userId' => $userId,
  85. * 'moreData' => $moreData
  86. * );
  87. * @return void
  88. */
  89. public function send($additionalParameters)
  90. {
  91. $trimmedKey = trim(CONFIG_SECURITY_API_KEY);
  92. if (!empty($trimmedKey)) {
  93. $phoneExists = array_key_exists("mobilePhoneNumber", $additionalParameters);
  94. $to = $phoneExists ? $additionalParameters['mobilePhoneNumber'] : $this->getMobilePhoneNumberById($additionalParameters['userId']);
  95. $message = array(
  96. "to" => $to,
  97. "message" => $this->getSms($additionalParameters)
  98. );
  99. if (!empty($message['message'])) {
  100. $result = $this->api->send($message);
  101. // Commented for future message logging / tracking purposes
  102. /*if ($result["success"]) {
  103. echo "Message sent - ID: " . $result["id"];
  104. } else {
  105. echo "Message failed - Error: " . $result["error_message"];
  106. }*/
  107. }
  108. }
  109. }
  110. /**
  111. * buildSms (builds an SMS from a template and data)
  112. * @param object ClockworksmsPlugin object
  113. * @param object Template object
  114. * @param string Template file name
  115. * @param string Text key from lang file
  116. * @param array Data to fill message variables (if any)
  117. * @return object Template object with message property updated
  118. */
  119. public function buildSms($plugin, $tpl, $templateName, $messageKey, $parameters = null)
  120. {
  121. $result = Database::select(
  122. 'selected_value',
  123. 'settings_current',
  124. array(
  125. 'where'=> array('variable = ?' => array('clockworksms_message'.$messageKey))
  126. )
  127. );
  128. //if (empty($result)) {
  129. if (0) {
  130. $tpl->assign('message', '');
  131. } else {
  132. $templatePath = 'clockworksms/sms_templates/';
  133. $content = $tpl->fetch($templatePath.$templateName);
  134. $message = $plugin->get_lang($messageKey);
  135. if ($parameters !== null) {
  136. $message = vsprintf($message, $parameters);
  137. }
  138. $tpl->assign('message', $message);
  139. }
  140. return $tpl->params['message'];
  141. }
  142. /**
  143. * getSms (returns an SMS message depending of its type)
  144. * @param array Data needed to send the SMS. It is mandatory to include the
  145. * 'smsType' and 'userId' (or 'mobilePhoneNumber') fields at least.
  146. * More data may be neccesary depending on the message type
  147. * Example: $additional_parameters = array(
  148. * 'smsType' => EXAMPLE_SMS_TYPE,
  149. * 'userId' => $userId,
  150. * 'moreData' => $moreData
  151. * );
  152. * @return string A ready to be sent SMS
  153. */
  154. public function getSms($additionalParameters)
  155. {
  156. $plugin = ClockworksmsPlugin::create();
  157. $tool_name = $plugin->get_lang('plugin_title');
  158. $tpl = new Template($tool_name);
  159. switch ($additionalParameters['smsType']) {
  160. case SmsPlugin::WELCOME_LOGIN_PASSWORD:
  161. $userInfo = api_get_user_info($additionalParameters['userId']);
  162. return $this->buildSms(
  163. $plugin,
  164. $tpl,
  165. 'welcome_login_password.tpl',
  166. 'WelcomeXLoginXPasswordX',
  167. array(
  168. api_get_setting('siteName'),
  169. $userInfo['username'],
  170. $additionalParameters['password']
  171. )
  172. );
  173. break;
  174. case SmsPlugin::NEW_FILE_SHARED_COURSE_BY:
  175. return $this->buildSms(
  176. $plugin,
  177. $tpl,
  178. 'new_file_shared_course_by.tpl',
  179. 'XNewFileSharedCourseXByX',
  180. array(
  181. api_get_setting('siteName'),
  182. $additionalParameters['courseTitle'],
  183. $additionalParameters['userUsername']
  184. )
  185. );
  186. break;
  187. case SmsPlugin::ACCOUNT_APPROVED_CONNECT:
  188. return $this->buildSms(
  189. $plugin,
  190. $tpl,
  191. 'account_approved_connect.tpl',
  192. 'XAccountApprovedConnectX',
  193. array(
  194. api_get_setting('siteName'),
  195. $tpl->params['_p']['web']
  196. )
  197. );
  198. break;
  199. case SmsPlugin::NEW_COURSE_BEEN_CREATED:
  200. return $this->buildSms(
  201. $plugin,
  202. $tpl,
  203. 'new_course_been_created.tpl',
  204. 'XNewCourseXBeenCreatedX',
  205. array(
  206. api_get_setting('siteName'),
  207. $additionalParameters['courseName'],
  208. $additionalParameters['creatorUsername']
  209. )
  210. );
  211. break;
  212. case SmsPlugin::NEW_USER_SUBSCRIBED_COURSE:
  213. return $this->buildSms(
  214. $plugin,
  215. $tpl,
  216. 'new_user_subscribed_course.tpl',
  217. 'XNewUserXSubscribedCourseX',
  218. array(
  219. api_get_setting('siteName'),
  220. $additionalParameters['userUsername'],
  221. $additionalParameters['courseCode']
  222. )
  223. );
  224. break;
  225. case SmsPlugin::NEW_COURSE_SUGGESTED_TEACHER:
  226. return $this->buildSms(
  227. $plugin,
  228. $tpl,
  229. 'new_course_suggested_teacher.tpl',
  230. 'XNewCourseSuggestedTeacherX',
  231. array(
  232. api_get_setting('siteName'),
  233. $additionalParameters['userUsername']
  234. )
  235. );
  236. break;
  237. case SmsPlugin::COURSE_OPENING_REQUEST_CODE_REGISTERED:
  238. return $this->buildSms(
  239. $plugin,
  240. $tpl,
  241. 'course_opening_request_code_registered.tpl',
  242. 'XCourseOpeningRequestCodeXRegistered',
  243. array(
  244. api_get_setting('siteName'),
  245. $additionalParameters['courseCode']
  246. )
  247. );
  248. break;
  249. case SmsPlugin::COURSE_OPENING_REQUEST_CODE_APPROVED:
  250. return $this->buildSms(
  251. $plugin,
  252. $tpl,
  253. 'course_opening_request_course_code_approved.tpl',
  254. 'XCourseOpeningRequestCourseCodeXApproved',
  255. array(
  256. api_get_setting('siteName'),
  257. $additionalParameters['courseCode']
  258. )
  259. );
  260. break;
  261. case SmsPlugin::COURSE_OPENING_REQUEST_CODE_REJECTED:
  262. return $this->buildSms(
  263. $plugin,
  264. $tpl,
  265. 'request_open_course_code_rejected.tpl',
  266. 'XRequestOpenCourseCodeXReject',
  267. array(
  268. api_get_setting('siteName'),
  269. $additionalParameters['courseCode']
  270. )
  271. );
  272. break;
  273. case SmsPlugin::COURSE_OPENING_REQUEST_CODE:
  274. return $this->buildSms(
  275. $plugin,
  276. $tpl,
  277. 'course_opening_request_course_code.tpl',
  278. 'XCourseOpeningRequestCourseCodeX',
  279. array(
  280. api_get_setting('siteName'),
  281. $additionalParameters['courseCode']
  282. )
  283. );
  284. break;
  285. case SmsPlugin::BEEN_SUBSCRIBED_COURSE:
  286. return $this->buildSms(
  287. $plugin,
  288. $tpl,
  289. 'been_subscribed_course.tpl',
  290. 'XBeenSubscribedCourseX',
  291. array(
  292. api_get_setting('siteName'),
  293. $additionalParameters['courseTitle']
  294. )
  295. );
  296. break;
  297. case SmsPlugin::ASSIGNMENT_BEEN_CREATED_COURSE:
  298. return $this->buildSms(
  299. $plugin,
  300. $tpl,
  301. 'assignment_been_created_course.tpl',
  302. 'XAssignmentBeenCreatedCourseX',
  303. array(
  304. api_get_setting('siteName'),
  305. $additionalParameters['courseTitle']
  306. )
  307. );
  308. break;
  309. // Message types to be implemented. Fill the array parameter with arguments.
  310. /*case SmsPlugin::ACCOUNT_CREATED_UPDATED_LOGIN_PASSWORD:
  311. return $this->buildSms(
  312. $plugin,
  313. $tpl,
  314. 'account_created_updated_login_password.tpl',
  315. 'XAccountCreatedUpdatedLoginXPasswordX',
  316. array(
  317. api_get_setting('siteName')
  318. )
  319. );
  320. break;*/
  321. /*case SmsPlugin::PASSWORD_UPDATED_LOGIN_PASSWORD:
  322. return $this->buildSms(
  323. $plugin,
  324. $tpl,
  325. 'password_updated_login_password.tpl',
  326. 'XPasswordUpdatedLoginXPasswordX',
  327. array(
  328. api_get_setting('siteName')
  329. )
  330. );
  331. break;*/
  332. /*case SmsPlugin::REQUESTED_PASSWORD_CHANGE:
  333. return $this->buildSms(
  334. $plugin,
  335. $tpl,
  336. 'requested_password_change.tpl',
  337. 'XPasswordUpdatedLoginXPasswordX',
  338. array(
  339. api_get_setting('siteName')
  340. )
  341. );
  342. break;*/
  343. /*case SmsPlugin::RECEIVED_NEW_PERSONAL_MESSAGES:
  344. return $this->buildSms(
  345. $plugin,
  346. $tpl,
  347. 'received_new_personal_messages.tpl',
  348. 'XReceivedNewPersonalMessages',
  349. array(
  350. api_get_setting('siteName')
  351. )
  352. );
  353. break;*/
  354. /*case SmsPlugin::NEW_USER_PENDING_APPROVAL:
  355. return $this->buildSms(
  356. $plugin,
  357. $tpl,
  358. 'new_user_pending_approval.tpl',
  359. 'XNewUserXPendingApproval',
  360. array(
  361. api_get_setting('siteName')
  362. )
  363. );
  364. break;*/
  365. /*case SmsPlugin::POSTED_FORUM_COURSE:
  366. return $this->buildSms(
  367. $plugin,
  368. $tpl,
  369. 'posted_forum_course.tpl',
  370. 'XXPostedForumXCourseX',
  371. array(
  372. api_get_setting('siteName')
  373. )
  374. );
  375. break;*/
  376. /*case SmsPlugin::CHECK_EMAIL_CONNECT_MORE_INFO:
  377. return $this->buildSms(
  378. $plugin,
  379. $tpl,
  380. 'check_email_connect_more_info.tpl',
  381. 'XXXCheckEmailConnectMoreInfo',
  382. array(
  383. api_get_setting('siteName')
  384. )
  385. );
  386. break;*/
  387. /*case SmsPlugin::STUDENT_ANSWERED_TEST:
  388. return $this->buildSms(
  389. $plugin,
  390. $tpl,
  391. 'student_answered_test.tpl',
  392. 'XXStudentXAnsweredTestX',
  393. array(
  394. api_get_setting('siteName')
  395. )
  396. );
  397. break;*/
  398. /*case SmsPlugin::STUDENT_ANSWERED_TEST_OPEN_QUESTION:
  399. return $this->buildSms(
  400. $plugin,
  401. $tpl,
  402. 'student_answered_test_open_question.tpl',
  403. 'XXStudentXAnsweredTestXOpenQuestion',
  404. array(
  405. api_get_setting('siteName')
  406. )
  407. );
  408. break;*/
  409. /*case SmsPlugin::STUDENT_ANSWERED_TEST_VOICE_QUESTION:
  410. return $this->buildSms(
  411. $plugin,
  412. $tpl,
  413. 'student_answered_test_voice_question.tpl',
  414. 'XXStudentXAnsweredTestXVoiceQuestion',
  415. array(
  416. api_get_setting('siteName')
  417. )
  418. );
  419. break;*/
  420. /*case SmsPlugin::ANSWER_OPEN_QUESTION_TEST_REVIEWED:
  421. return $this->buildSms(
  422. $plugin,
  423. $tpl,
  424. 'answer_open_question_test_reviewed.tpl',
  425. 'XXAnswerOpenQuestionTestXReviewed',
  426. array(
  427. api_get_setting('siteName')
  428. )
  429. );
  430. break;*/
  431. /*case SmsPlugin::NEW_THREAD_STARTED_FORUM:
  432. return $this->buildSms(
  433. $plugin,
  434. $tpl,
  435. 'new_thread_started_forum.tpl',
  436. 'XXNewThreadXStartedForumX',
  437. array(
  438. api_get_setting('siteName')
  439. )
  440. );
  441. break;*/
  442. /*case SmsPlugin::NEW_ANSWER_POSTED_FORUM:
  443. return $this->buildSms(
  444. $plugin,
  445. $tpl,
  446. 'new_answer_posted_forum.tpl',
  447. 'XXNewAnswerPostedXForumX',
  448. array(
  449. api_get_setting('siteName')
  450. )
  451. );
  452. break;*/
  453. /*case SmsPlugin::NEW_SYSTEM_ANNOUNCEMENT_ADDED:
  454. return $this->buildSms(
  455. $plugin,
  456. $tpl,
  457. 'new_system_announcement_added.tpl',
  458. 'XXNewSystemAnnouncementAdded',
  459. array(
  460. api_get_setting('siteName')
  461. )
  462. );
  463. break;*/
  464. /*case SmsPlugin::TEST_NEW_SYSTEM_ANNOUNCEMENT_ADDED:
  465. return $this->buildSms(
  466. $plugin,
  467. $tpl,
  468. 'test_new_system_announcement_added.tpl',
  469. 'XTestXNewSystemAnnouncementAdded',
  470. array(
  471. api_get_setting('siteName')
  472. )
  473. );
  474. break;*/
  475. /*case SmsPlugin::SYSTEM_ANNOUNCEMENT_UPDATE:
  476. return $this->buildSms(
  477. $plugin,
  478. $tpl,
  479. 'system_announcement_update.tpl',
  480. 'XXSystemAnnouncementUpdate',
  481. array(
  482. api_get_setting('siteName')
  483. )
  484. );
  485. break;*/
  486. /*case SmsPlugin::TEST_SYSTEM_ANNOUNCEMENT_UPDATE:
  487. return $this->buildSms(
  488. $plugin,
  489. $tpl,
  490. 'test_system_announcement_update.tpl',
  491. 'XXSystemAnnouncementUpdate',
  492. array(
  493. api_get_setting('siteName')
  494. )
  495. );
  496. break;*/
  497. /*case SmsPlugin::USER_UPLOADED_ASSIGNMENT_COURSE_STUDENT_SUBMITS_PAPER:
  498. return $this->buildSms(
  499. $plugin,
  500. $tpl,
  501. 'user_uploaded_assignment_course_student_submits_paper.tpl',
  502. 'XUserXUploadedAssignmentXCourseXStudentSubmitsPaper',
  503. array(
  504. api_get_setting('siteName')
  505. )
  506. );
  507. break;*/
  508. /*case SmsPlugin::USER_UPLOADED_ASSIGNMENT_CHECK_STUDENT_SUBMITS_PAPER:
  509. return $this->buildSms(
  510. $plugin,
  511. $tpl,
  512. 'user_uploaded_assignment_check_student_submits_paper.tpl',
  513. 'XUserXUploadedAssignmentXCheckXStudentSubmitsPaper',
  514. array(
  515. api_get_setting('siteName')
  516. )
  517. );
  518. break;*/
  519. /*case SmsPlugin::USER_UPLOADED_ASSIGNMENT_COURSE:
  520. return $this->buildSms(
  521. $plugin,
  522. $tpl,
  523. 'user_uploaded_assignment_course.tpl',
  524. 'XUserXUploadedAssignmentXCourseX',
  525. array(
  526. api_get_setting('siteName')
  527. )
  528. );
  529. break;*/
  530. /*case SmsPlugin::USER_UPLOADED_ASSIGNMENT_CHECK:
  531. return $this->buildSms(
  532. $plugin,
  533. $tpl,
  534. 'user_uploaded_assignment_check.tpl',
  535. 'XUserXUploadedAssignmentXCheckX',
  536. array(
  537. api_get_setting('siteName')
  538. )
  539. );
  540. break;*/
  541. /*case SmsPlugin::SUBSCRIBED_SESSION:
  542. return $this->buildSms(
  543. $plugin,
  544. $tpl,
  545. 'subscribed_session.tpl',
  546. 'XSubscribedSessionX',
  547. array(
  548. api_get_setting('siteName')
  549. )
  550. );
  551. break;*/
  552. /*case SmsPlugin::SUBSCRIBED_SESSION_CSV:
  553. return $this->buildSms(
  554. $plugin,
  555. $tpl,
  556. 'subscribed_session_csv.tpl',
  557. 'XSubscribedSessionXCSV',
  558. array(
  559. api_get_setting('siteName')
  560. )
  561. );
  562. break;*/
  563. /*case SmsPlugin::USER_SUGGESTED_BE_FRIENDS:
  564. return $this->buildSms(
  565. $plugin,
  566. $tpl,
  567. 'user_suggested_be_friends.tpl',
  568. 'XUserXSuggestedBeFriends',
  569. array(
  570. api_get_setting('siteName')
  571. )
  572. );
  573. break;*/
  574. /*case SmsPlugin::USER_ANSWERED_INBOX_MESSAGE:
  575. return $this->buildSms(
  576. $plugin,
  577. $tpl,
  578. 'user_answered_inbox_message.tpl',
  579. 'XUserXAnsweredInboxMessage',
  580. array(
  581. api_get_setting('siteName')
  582. )
  583. );
  584. break;*/
  585. /*case SmsPlugin::BEEN_INVITED_JOIN_GROUP:
  586. return $this->buildSms(
  587. $plugin,
  588. $tpl,
  589. 'been_invited_join_group.tpl',
  590. 'XBeenInvitedJoinGroupX',
  591. array(
  592. api_get_setting('siteName')
  593. )
  594. );
  595. break;*/
  596. /*case SmsPlugin::MESSAGES_SENT_EDITED_GROUP_EDITED:
  597. return $this->buildSms(
  598. $plugin,
  599. $tpl,
  600. 'messages_sent_edited_group_edited.tpl',
  601. 'XMessagesSentEditedGroupXEdited',
  602. array(
  603. api_get_setting('siteName')
  604. )
  605. );
  606. break;*/
  607. /*case SmsPlugin::MESSAGES_SENT_EDITED_GROUP_ADDED:
  608. return $this->buildSms(
  609. $plugin,
  610. $tpl,
  611. 'messages_sent_edited_group_added.tpl',
  612. 'XMessagesSentEditedGroupXAdded',
  613. array(
  614. api_get_setting('siteName')
  615. )
  616. );
  617. break;*/
  618. /*case SmsPlugin::BEEN_INVITED_COMPLETE_SURVEY_COURSE:
  619. return $this->buildSms(
  620. $plugin,
  621. $tpl,
  622. 'been_invited_complete_survey_course.tpl',
  623. 'XBeenInvitedCompleteSurveyXCourseX',
  624. array(
  625. api_get_setting('siteName')
  626. )
  627. );
  628. break;*/
  629. /*case SmsPlugin::REMINDER_ASSIGNMENT_COURSE_DUE:
  630. return $this->buildSms(
  631. $plugin,
  632. $tpl,
  633. 'reminder_assignment_course_due.tpl',
  634. 'XReminderAssignmentXCourseXDue',
  635. array(
  636. api_get_setting('siteName')
  637. )
  638. );
  639. break;*/
  640. /*case SmsPlugin::USER_DETAILS_MODIFIED:
  641. return $this->buildSms(
  642. $plugin,
  643. $tpl,
  644. 'user_details_modified.tpl',
  645. 'XUserDetailsModified',
  646. array(
  647. api_get_setting('siteName')
  648. )
  649. );
  650. break;*/
  651. default:
  652. return '';
  653. }
  654. }
  655. }