diff --git a/src/mibew/install/dbinfo.php b/src/mibew/install/dbinfo.php index 430b0f3b..59d8cb0f 100644 --- a/src/mibew/install/dbinfo.php +++ b/src/mibew/install/dbinfo.php @@ -125,6 +125,20 @@ $dbtables = array( "translation" => "text", ), + // Contains localized mail templates + "${mysqlprefix}mailtemplate" => array( + // Artificial primary key + "templateid" => "int NOT NULL auto_increment PRIMARY KEY", + // Locale code a template belongs to + "locale" => "varchar(5) NOT NULL", + // Machine name of a template + "name" => "varchar(256) NOT NULL", + // Mail subject + "subject" => "varchar(1024) NOT NULL", + // Mail body + "body" => "text", + ), + // Store chat thread messages "${mysqlprefix}chatmessage" => array( // Message ID. diff --git a/src/mibew/install/index.php b/src/mibew/install/index.php index 27e66a55..f683749d 100644 --- a/src/mibew/install/index.php +++ b/src/mibew/install/index.php @@ -329,6 +329,52 @@ function add_canned_messages($link){ } } +function add_mail_templates($link){ + global $mysqlprefix; + $localesresult = mysql_query("select distinct locale from ${mysqlprefix}mailtemplate", $link); + $existlocales = array(); + for ($i = 0; $i < mysql_num_rows($localesresult); $i++) { + $existlocales[] = mysql_result($localesresult, $i, 'locale'); + } + $result = array(); + foreach (get_available_locales() as $locale) { + if (! in_array($locale, $existlocales)) { + $result[] = array( + 'locale' => $locale, + 'name' => 'user_history', + 'subject' => getlocal('mail.user.history.subject', null, $locale), + 'body' => getlocal('mail.user.history.body', null, $locale) + ); + $result[] = array( + 'locale' => $locale, + 'name' => 'password_recovery', + 'subject' => getlocal('restore.mailsubj', null, $locale), + 'body' => getlocal('restore.mailtext', null, $locale) + ); + $result[] = array( + 'locale' => $locale, + 'name' => 'leave_message', + 'subject' => getlocal('leavemail.subject', null, $locale), + 'body' => getlocal('leavemail.body', null, $locale) + ); + } + } + if (count($result) > 0) { + $updatequery = "insert into ${mysqlprefix}mailtemplate (name,locale,subject,body) values "; + for ($i = 0; $i < count($result); $i++) { + if ($i > 0) { + $updatequery .= ", "; + } + $updatequery .= "('" . mysql_real_escape_string($result[$i]['name'], $link) . "', " + . "'" . mysql_real_escape_string($result[$i]['locale'], $link) . "', " + . "'" . mysql_real_escape_string($result[$i]['subject'], $link) . "', " + . "'" . mysql_real_escape_string($result[$i]['body'], $link) . "')"; + } + mysql_query($updatequery, $link); + } +} + + function check_status() { global $page, $mysqlprefix; @@ -364,6 +410,7 @@ function check_status() } add_canned_messages($link); + add_mail_templates($link); check_sound(); diff --git a/src/mibew/libs/classes/Mibew/Controller/Chat/MailController.php b/src/mibew/libs/classes/Mibew/Controller/Chat/MailController.php index 958dd257..bc760680 100644 --- a/src/mibew/libs/classes/Mibew/Controller/Chat/MailController.php +++ b/src/mibew/libs/classes/Mibew/Controller/Chat/MailController.php @@ -110,20 +110,24 @@ class MailController extends AbstractController $history .= message_to_text($msg); } - $subject = getlocal('mail.user.history.subject', null, CURRENT_LOCALE, true); - $body = getlocal( - 'mail.user.history.body', + // Load mail templates and substitute placeholders there. + $mail_template = mail_template_load('user_history', CURRENT_LOCALE); + if (!$mail_template) { + throw new \RuntimeException('Cannot load "user_history" mail template'); + } + + $body = str_replace( + array('{0}', '{1}', '{2}', '{3}'), array( $thread->userName, $history, Settings::get('title'), - Settings::get('hosturl') + Settings::get('hosturl'), ), - CURRENT_LOCALE, - true + $mail_template['body'] ); - mibew_mail($email, MIBEW_MAILBOX, $subject, $body); + mibew_mail($email, MIBEW_MAILBOX, $mail_template['subject'], $body); $page = setup_logo($group); $page['email'] = $email; diff --git a/src/mibew/libs/classes/Mibew/Controller/MailTemplateController.php b/src/mibew/libs/classes/Mibew/Controller/MailTemplateController.php new file mode 100644 index 00000000..c6707766 --- /dev/null +++ b/src/mibew/libs/classes/Mibew/Controller/MailTemplateController.php @@ -0,0 +1,218 @@ +getOperator(); + $page = array(); + + // Build list of available locales + $all_locales = get_available_locales(); + $locale_names = get_locale_names(); + $locales_with_label = array(); + foreach ($all_locales as $id) { + $locales_with_label[] = array( + 'id' => $id, + 'name' => (isset($locale_names[$id]) ? $locale_names[$id] : $id) + ); + } + $page['locales'] = $locales_with_label; + + // Get selected locale. + $lang = $this->extractLocale($request); + + $page['stored'] = $request->query->has('stored'); + $page['formaction'] = $this->generateUrl('mail_templates'); + $page['mailTemplates'] = $this->getMailTemplatesList($lang); + $page['formlang'] = $lang; + $page['title'] = getlocal('mail_template.title'); + $page['menuid'] = 'mail_templates'; + + $page = array_merge($page, prepare_menu($operator)); + + return $this->render('mail_templates', $page); + } + + /** + * Builds a page with form for mail template settings. + * + * @param Request $request Incoming request. + * @return string Rendered page content. + */ + public function showEditFormAction(Request $request) + { + set_csrf_token(); + + $operator = $this->getOperator(); + $lang = $this->extractLocale($request); + $template_name = $request->attributes->get('name'); + + $page = array( + // Use errors list stored in the request. We need to do so to have + // an ability to pass the request from the "submitEditForm" action. + 'errors' => $request->attributes->get('errors', array()), + ); + + $template = $this->loadMailTemplate($template_name, $lang); + + // Use values from the request or the default ones if they are not + // available. + $page['formsubject'] = $request->request->get('subject', $template['subject']); + $page['formbody'] = $request->request->get('body', $template['body']); + + $page['formname'] = $template_name; + $page['formlang'] = $lang; + $page['formaction'] = $this->generateUrl( + 'mail_template_edit', + array('name' => $template_name) + ); + $page['title'] = getlocal('mail_template.title'); + $page['menuid'] = 'mail_templates'; + + $page = array_merge($page, prepare_menu($operator)); + + return $this->render('mail_template_edit', $page); + } + + /** + * Processes submitting of the form which is generated in + * {@link \Mibew\Controller\MailTemplateController::showFormAction()} + * method. + * + * @param Request $request Incoming request. + * @return string Rendered page content. + */ + public function submitEditFormAction(Request $request) + { + csrf_check_token($request); + + $name = $request->attributes->get('name'); + $lang = $this->extractLocale($request); + $errors = array(); + + $subject = $request->request->get('subject'); + if (!$subject) { + $errors[] = no_field('form.field.mail_template_subject'); + } + + $body = $request->request->get('body'); + if (!$body) { + $errors[] = no_field('form.field.mail_template_body'); + } + + if (count($errors) != 0) { + // On or more errors took place. We cannot continue the saving + // process. Just attach errors to the request and rerender the edit + // form. + $request->attributes->set('errors', $errors); + + return $this->showEditFormAction($request); + } + + // Save the template and redirect the operator to the page with mail + // templates list. + mail_template_save($name, $lang, $subject, $body); + $redirect_to = $this->generateUrl( + 'mail_templates', + array( + 'lang' => $lang, + 'stored' => true, + ) + ); + + return $this->redirect($redirect_to); + } + + /** + * Builds list of mail templates. + * + * @param string $locale Locale code which will be used for templates + * loading. + * @return array List of mail templates available in the system. + */ + protected function getMailTemplatesList($locale) + { + return array( + $this->loadMailTemplate('user_history', $locale), + $this->loadMailTemplate('password_recovery', $locale), + $this->loadMailTemplate('leave_message', $locale), + ); + } + + /** + * Loads mail template. + * + * It's just a wrapper for {@link mail_template_load()} function which + * throws an exception if a template cannot be loaded. + * + * @param string $name Machine name of the template + * @param string $locale Locale code which should be used for template + * loading. + * @return array Mail template. + * @throws \RuntimeException If the template cannot be loaded. + */ + protected function loadMailTemplate($name, $locale) + { + $template = mail_template_load($name, $locale); + + if (!$template) { + throw new \RuntimeException(sprintf('Cannot load "%s" mail template', $name)); + } + + return $template; + } + + /** + * Extracts locale code from the request. + * + * @param Request $request + * @return string Locale code for the selected locale. + */ + protected function extractLocale(Request $request) + { + $lang = $request->isMethod('POST') + ? $request->request->get('lang') + : $request->query->get('lang'); + + $all_locales = get_available_locales(); + $correct_locale = !empty($lang) + && preg_match("/^[\w-]{2,5}$/", $lang) + && in_array($lang, $all_locales); + if (!$correct_locale) { + $lang = in_array(CURRENT_LOCALE, $all_locales) + ? CURRENT_LOCALE + : $all_locales[0]; + } + + return $lang; + } +} diff --git a/src/mibew/libs/classes/Mibew/Controller/PasswordRecoveryController.php b/src/mibew/libs/classes/Mibew/Controller/PasswordRecoveryController.php index 84f5bcde..5a9b70fc 100644 --- a/src/mibew/libs/classes/Mibew/Controller/PasswordRecoveryController.php +++ b/src/mibew/libs/classes/Mibew/Controller/PasswordRecoveryController.php @@ -90,15 +90,20 @@ class PasswordRecoveryController extends AbstractController ), UrlGeneratorInterface::ABSOLUTE_URL ); - mibew_mail( - $email, - $email, - getlocal('restore.mailsubj'), - getlocal( - 'restore.mailtext', - array(get_operator_name($to_restore), $href) - ) + + // Load mail templates and substitute placeholders there. + $mail_template = mail_template_load('password_recovery', CURRENT_LOCALE); + if (!$mail_template) { + throw new \RuntimeException('Cannot load "password_recovery" mail template'); + } + + $body = str_replace( + array('{0}', '{1}'), + array(get_operator_name($to_restore), $href), + $mail_template['body'] ); + + mibew_mail($email, $email, $mail_template['subject'], $body); $page['isdone'] = true; return $this->render('password_recovery', $page); diff --git a/src/mibew/libs/classes/Mibew/RequestProcessor/ThreadProcessor.php b/src/mibew/libs/classes/Mibew/RequestProcessor/ThreadProcessor.php index 995ddfaf..8eb443a3 100644 --- a/src/mibew/libs/classes/Mibew/RequestProcessor/ThreadProcessor.php +++ b/src/mibew/libs/classes/Mibew/RequestProcessor/ThreadProcessor.php @@ -644,20 +644,24 @@ class ThreadProcessor extends ClientSideProcessor // Send email if ($inbox_mail) { // Prepare message to send by email - $subject = getlocal( - "leavemail.subject", - array($args['name']), - $message_locale + $mail_template = mail_template_load('leave_message', $message_locale); + if (!$mail_template) { + throw new \RuntimeException('Cannot load "leave_message" mail template'); + } + $subject = str_replace( + '{0}', + $args['name'], + $mail_template['subject'] ); - $body = getlocal( - "leavemail.body", + $body = str_replace( + array('{0}', '{1}', '{2}', '{3}'), array( $args['name'], $email, $message, - $info ? $info . "\n" : "" + ($info ? $info . "\n" : "") ), - $message_locale + $mail_template['body'] ); // Send diff --git a/src/mibew/libs/notify.php b/src/mibew/libs/notify.php index 1eb9fa95..94e1d08e 100644 --- a/src/mibew/libs/notify.php +++ b/src/mibew/libs/notify.php @@ -15,6 +15,8 @@ * limitations under the License. */ +use Mibew\Database; + function mibew_mail($to_addr, $reply_to, $subject, $body) { $headers = "From: " . MIBEW_MAILBOX . "\r\n" @@ -38,3 +40,96 @@ function mibew_mail($to_addr, $reply_to, $subject, $body) @ini_set('sendmail_from', $old_from); } } + +/** + * Loads an email template. + * + * @param string $name Machine name of the template. + * @param string $locale Locale code for the mail template. + * @return array|boolean Associative array with localized mail template data. If + * the template with specified locale is not found a template for default + * locale will be loaded. If the last one does not found too boolean FALSE + * will be returned. + * Mail template array contains the following keys: + * - templateid: int, internale id of the template. It should not be used + * directly. + * - name: string, machine name of the template. + * - locale: string, locale code the templates belongs to. + * - title: string, localized human-readable mail template title. + * - subject: string, localized value which will be used as subject field in + * an email. + * - body: string, localized value which will be used as body in an email. + */ +function mail_template_load($name, $locale) +{ + static $templates = array(); + + if (!isset($templates[$locale][$name])) { + // Try to load the template from the database. + $template = Database::getInstance()->query( + "SELECT * FROM {mailtemplate} WHERE name = :name AND locale = :locale", + array( + ':name' => $name, + ':locale' => $locale, + ), + array( + 'return_rows' => Database::RETURN_ONE_ROW, + ) + ); + + if (!$template) { + // There is no template in the database. + if ($locale == DEFAULT_LOCALE) { + // The template is still not found. + $template = false; + } else { + // Try to load the template for the default locale. + $template = $this->loadMailTemplate($name, DEFAULT_LOCALE); + } + } + + $templates[$locale][$name] = $template; + } + + return $templates[$locale][$name]; +} + +/** + * Saves a mail template to the database. + * + * @param string $name Machine name of the template to save. + * @param string $locale Locale code the template belongs to. + * @param string $subject Localized string that is used as email subject. + * @param string $body Localized string that is used as email body. + */ +function mail_template_save($name, $locale, $subject, $body) +{ + $db = Database::getInstance(); + $template = mail_template_load($name, $locale); + + if ($template && $template['locale'] == $locale) { + // Update existing mail template + $db->query( + ("UPDATE {mailtemplate} " + . "SET subject = :subject, body = :body " + . "WHERE templateid = :id"), + array( + ':id' => $template['templateid'], + ':subject' => $subject, + ':body' => $body, + ) + ); + } else { + // Insert a new mail template + $db->query( + ("INSERT INTO {mailtemplate} (name, locale, subject, body) " + . "VALUES (:name, :locale, :subject, :body)"), + array( + ':name' => $name, + ':locale' => $locale, + ':subject' => $subject, + ':body' => $body, + ) + ); + } +} diff --git a/src/mibew/libs/routing.yml b/src/mibew/libs/routing.yml index 80511ce5..6dc0f3ee 100644 --- a/src/mibew/libs/routing.yml +++ b/src/mibew/libs/routing.yml @@ -343,6 +343,34 @@ logout: _controller: Mibew\Controller\LogoutController::logoutAction _access_check: Mibew\AccessControl\Check\LoggedInCheck +## Mail templates +mail_template_edit: + path: /operator/mail-template/{name}/edit + defaults: + _controller: Mibew\Controller\MailTemplateController::showEditFormAction + _access_check: Mibew\AccessControl\Check\PermissionsCheck + _access_permissions: [CAN_ADMINISTRATE] + methods: [GET] + requirements: + name: "[A-Za-z0-9_]+" + +mail_template_edit_save: + path: /operator/mail-template/{name}/edit + defaults: + _controller: Mibew\Controller\MailTemplateController::submitEditFormAction + _access_check: Mibew\AccessControl\Check\PermissionsCheck + _access_permissions: [CAN_ADMINISTRATE] + methods: [POST] + requirements: + name: "[A-Za-z0-9_]+" + +mail_templates: + path: /operator/mail-template + defaults: + _controller: Mibew\Controller\MailTemplateController::indexAction + _access_check: Mibew\AccessControl\Check\PermissionsCheck + _access_permissions: [CAN_ADMINISTRATE] + ## Operators operator_add: path: /operator/operator/add diff --git a/src/mibew/locales/en/translation.po b/src/mibew/locales/en/translation.po index 3a64a6ab..fb745f4c 100644 --- a/src/mibew/locales/en/translation.po +++ b/src/mibew/locales/en/translation.po @@ -338,6 +338,10 @@ msgid "form.field.mail.description" msgstr "For notifications and password retrieval." msgid "form.field.mail" msgstr "E-mail" +msgid "form.field.mail_template_body" +msgstr "Mail body" +msgid "form.field.mail_template_subject" +msgstr "Mail subject" msgid "form.field.message" msgstr "Message" msgid "form.field.name" @@ -460,6 +464,24 @@ msgid "localedirection" msgstr "ltr" msgid "localeid" msgstr "English (en)" +msgid "mail_template.actions" +msgstr "Modify" +msgid "mail_template.actions.edit" +msgstr "edit" +msgid "mail_template.body" +msgstr "Mail body" +msgid "mail_template.intro" +msgstr "On this page you can edit mail templates which are used in the system." +msgid "mail_template.locale" +msgstr "For language:" +msgid "mail_template.name" +msgstr "Machine name" +msgid "mail_template.saved" +msgstr "Changes saved" +msgid "mail_template.subject" +msgstr "Mail subject" +msgid "mail_template.title" +msgstr "Mail templates" msgid "mail.user.history.body" msgstr "Hello {0}!\n\nYour chat history: \n\n{1}\n--- \nRegards,\n{2} and Mibew\n{3}" msgid "mail.user.history.subject" @@ -488,6 +510,8 @@ msgid "menu.locale.content" msgstr "Change locale." msgid "menu.locale" msgstr "Language" +msgid "menu.mail_templates" +msgstr "Mail templates" msgid "menu.main" msgstr "Main" msgid "menu.operator" diff --git a/src/mibew/styles/pages/default/templates_src/server_side/_menu.handlebars b/src/mibew/styles/pages/default/templates_src/server_side/_menu.handlebars index 3bdc4b7e..53a36147 100644 --- a/src/mibew/styles/pages/default/templates_src/server_side/_menu.handlebars +++ b/src/mibew/styles/pages/default/templates_src/server_side/_menu.handlebars @@ -34,6 +34,7 @@ {{l10n "leftMenu.client_settings"}} {{l10n "menu.styles"}} {{l10n "menu.translate"}} + {{l10n "menu.mail_templates"}} {{l10n "menu.updates"}} {{/if}} {{#if currentopid}} diff --git a/src/mibew/styles/pages/default/templates_src/server_side/mail_template_edit.handlebars b/src/mibew/styles/pages/default/templates_src/server_side/mail_template_edit.handlebars new file mode 100644 index 00000000..2dc7c451 --- /dev/null +++ b/src/mibew/styles/pages/default/templates_src/server_side/mail_template_edit.handlebars @@ -0,0 +1,60 @@ +{{#extends "_layout"}} + {{#override "menu"}}{{> _menu}}{{/override}} + + {{#override "content"}} + {{l10n "mail_template.intro"}} + +
+
+ + {{> _errors}} + +
+ {{csrfTokenInput}} + + +
+
+
+
+
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+ +
+
+
+ +
+
+ +
+ {{l10n "common.asterisk_explanation"}} +
+ +
+ {{/override}} +{{/extends}} \ No newline at end of file diff --git a/src/mibew/styles/pages/default/templates_src/server_side/mail_templates.handlebars b/src/mibew/styles/pages/default/templates_src/server_side/mail_templates.handlebars new file mode 100644 index 00000000..57d7ab1e --- /dev/null +++ b/src/mibew/styles/pages/default/templates_src/server_side/mail_templates.handlebars @@ -0,0 +1,78 @@ +{{#extends "_layout"}} + {{#override "menu"}}{{> _menu}}{{/override}} + + {{#override "content"}} + {{l10n "mail_template.intro"}} + +
+
+ + {{#if stored}} +
{{l10n "mail_template.saved"}}
+ {{/if}} + + {{> _errors}} + +
+
+ {{> _tabs}} + +
+
+
+
+ +
+ +
+ {{l10n "mail_template.locale"}}
+ +
+ +
+ +
+ +
+
+
+ +
+
+
+ + + + + + + + + + + + + {{#each mailTemplates}} + + + + + + + {{/each}} + +
{{l10n "mail_template.name"}}{{l10n "mail_template.subject"}}{{l10n "mail_template.body"}}{{l10n "mail_template.actions"}}
+ {{name}} + + {{subject}} + + {{body}} + + {{l10n "mail_template.actions.edit"}} +
+ {{/override}} +{{/extends}} \ No newline at end of file