diff --git a/debian/control b/debian/control index c355d9c4..7ebef571 100644 --- a/debian/control +++ b/debian/control @@ -7,7 +7,7 @@ Maintainer: Benjamin Renard Package: ldapsaisie Architecture: all Depends: apache2 | httpd, php-ldap | php5-ldap, php-fpm | libapache2-mod-php5 | libapache2-mod-php | php5-cli | php-cli, smarty | smarty3, php-net-ldap2, php-console-table -Recommends: php-mbstring, php-phpseclib, php-unidecode, php-zxcvbn, php-ftp, php-mail, php-mail-mime +Recommends: php-mbstring, php-phpseclib, php-unidecode, php-zxcvbn, php-ftp, php-mail, php-mail-mime, php-html2text Description: web based interface for managing LDAP servers content LdapSaisie is a Web application developed to manage LDAP directory. It has been written in PHP / JavaScript and is published under the diff --git a/doc/src/conf/LSaddon/LSaddon_mail.md b/doc/src/conf/LSaddon/LSaddon_mail.md index 4bfc2e6b..a83fde19 100644 --- a/doc/src/conf/LSaddon/LSaddon_mail.md +++ b/doc/src/conf/LSaddon/LSaddon_mail.md @@ -1,8 +1,35 @@ # LSaddon_mail -Cet [LSaddon](index.md#configuration-des-lsaddons) est utilisé pour gérer l'envoie de mail. Il +Cet [LSaddon](index.md#configuration-des-lsaddons) est utilisé pour gérer l'envoi de courriels. Il utilise pour cela les librairies [PEAR](http://pear.php.net/) *Mail* et *Mail_Mime* qui doivent être -installés. Cet [LSaddon](index.md#configuration-des-lsaddons) doit être configuré en éditant son +installés. + +Cet [LSaddon](index.md#configuration-des-lsaddons) offre aussi la possibilité d'envoyer des +courriels dont le contenu est construit à partir de modèles. Ces modèles sont enregistrés dans des +fichiers textes stockés (voir `$GLOBALS['MAIL_TEMPLATES_DIRECTORIES']`). Pour chaque modèle, vous +devez fournir trois fichiers portant le même nom mais avec des extensions différentes : + +- `template.subject` : le sujet du courriel. Note : seule la première ligne du fichier est utilisé + (et passée dans la fonction `trim()`) +- `template.html` : le contenu HTML du courriel +- `template.txt`: le contenu texte du courriel + +Ces trois fichiers sont utilisés en tant que modèle [Smarty](http://www.smarty.net/) et seront +construit en utilisant les variables fournies dans le contexte d'envoi des courriels. À noter que le +moteur Smarty utilisé pour la génération du contenu de ces courriels n'est pas le même que celui +utilisé par LdapSaisie pour l'affichage des pages. + +Par ailleurs, cet [LSaddon](index.md#configuration-des-lsaddons) fourni une vue de gestion des +modèles de courriels existants (voir `$GLOBALS['MAIL_TEMPLATES_EDITOR_VIEW_ACCESS']` pour la +configuration des accès). + +!!! warning + + Cette vue n'est pas conçues pour être mise entre toutes les mains. La sécurisation de modèles de + courriels étant très complexe, il est fortement recommandé de n'ouvrir l'accès à cette vue + qu'aux utilisateurs avertis et de confiances. + +Cet [LSaddon](index.md#configuration-des-lsaddons) doit être configuré en éditant son fichier de configuration `config.LSaddons.mail.php`. ```php @@ -67,10 +94,43 @@ $GLOBALS['MAIL_HEARDERS = array(); // Catch all sent emails $GLOBALS['MAIL_CATCH_ALL'] = array(); + +/** + * Email templates + * + * This addon offer ability to send email by using templates. Email templates are stored in + * full-text files in configured directories (see $GLOBALS['MAIL_TEMPLATES_DIRECTORIES']). For each + * template, you have to provide three files with the same name but with different extensions: + * - template.subject: the email subject. Note: only the first line is used (and stripped) + * - template.html: the HTML content of the email + * - template.txt: the text content of the email + * All these files will be used as Smarty templates and will be computed using variables provided + * in the sending context. Note that the Smarty object used to compute the template is not the same + * as the one used by LdapSaisie to display pages. + * + * Futhermore, this addon offer a view to list and edit existing template (see + * $GLOBALS['MAIL_TEMPLATES_EDITOR_VIEW_ACCESS'] to configured access). + */ + +// List of directory paths where as stored mail templates +// Notes: +// - provided path could be absolute or relative. Relative path are relative to the root base +// sources LdapSaisie directory (commonly /usr/share/ldapsaisie or the src directory if you +// installed it from sources). On Debian installation, you can specify 'local/email_templates' to +// refer to /etc/ldapsaisie/local/email_templates directory/ +// - Multiple directories could be specified, sorted so that the first ones take priority over +// the last one. +// - To allow users to edit them using the editor view, these directories must be +// writable by PHP process (commonly runed as www-data). +$GLOBALS['MAIL_TEMPLATES_DIRECTORIES'] = array('local/email_templates'); + +// List of granted LSprofiles to access mail templates editor view +// WARNING: Sanitizing mail templates is hell... EXPOSE THIS VIEW ONLY TO TRUSTED USERS! +$GLOBALS['MAIL_TEMPLATES_EDITOR_VIEW_ACCESS'] = array('admin'); ``` -Cet [LSaddon](index.md#configuration-des-lsaddons) offre la possibilité d'utilisé la fonction PHP -`sendMail()` : +Cet [LSaddon](index.md#configuration-des-lsaddons) offre avant tout la possibilité d'envoyer des +courriels en utilisant la fonction PHP `sendMail()` : ``` bool sendMail( @@ -84,3 +144,16 @@ bool sendMail( $html ); ``` + +Pour l'envoi de courriels en utilisant un modèle, il faut utiliser la fonction PHP +`sendMailFromTemplate()` : + +``` +bool sendMailFromTemplate( + $tplname, + $to, + $variables, + $headers, + $attachments +); +``` diff --git a/src/conf/LSaddons/config.LSaddons.mail.php b/src/conf/LSaddons/config.LSaddons.mail.php index fd9c09c1..3413713e 100644 --- a/src/conf/LSaddons/config.LSaddons.mail.php +++ b/src/conf/LSaddons/config.LSaddons.mail.php @@ -32,6 +32,9 @@ define('PEAR_MAIL','/usr/share/php/Mail.php'); // Pear :: Mail_mime define('PEAR_MAIL_MIME','/usr/share/php/Mail/mime.php'); +// Html2Text +define('HTML2TEXT','/usr/share/php/Html2Text.php'); + /* * Méthode d'envoie : * - mail : envoie avec la méthode PHP mail() @@ -82,3 +85,36 @@ $GLOBALS['MAIL_HEARDERS'] = array(); // Catch all sent emails $GLOBALS['MAIL_CATCH_ALL'] = array(); + +/** + * Email templates + * + * This addon offer ability to send email by using templates. Email templates are stored in + * full-text files in configured directories (see $GLOBALS['MAIL_TEMPLATES_DIRECTORIES']). For each + * template, you have to provide three files with the same name but with different extensions: + * - template.subject: the email subject. Note: only the first line is used (and stripped) + * - template.html: the HTML content of the email + * - template.txt: the text content of the email + * All these files will be used as Smarty templates and will be computed using variables provided + * in the sending context. Note that the Smarty object used to compute the template is not the same + * as the one used by LdapSaisie to display pages. + * + * Futhermore, this addon offer a view to list and edit existing template (see + * $GLOBALS['MAIL_TEMPLATES_EDITOR_VIEW_ACCESS'] to configured access). + */ + +// List of directory paths where as stored mail templates +// Notes: +// - provided path could be absolute or relative. Relative path are relative to the root base +// sources LdapSaisie directory (commonly /usr/share/ldapsaisie or the src directory if you +// installed it from sources). On Debian installation, you can specify 'local/email_templates' to +// refer to /etc/ldapsaisie/local/email_templates directory/ +// - Multiple directories could be specified, sorted so that the first ones take priority over +// the last one. +// - To allow users to edit them using the editor view, these directories must be +// writable by PHP process (commonly runed as www-data). +$GLOBALS['MAIL_TEMPLATES_DIRECTORIES'] = array('local/email_templates'); + +// List of granted LSprofiles to access mail templates editor view +// WARNING: Sanitizing mail templates is hell... EXPOSE THIS VIEW ONLY TO TRUSTED USERS! +$GLOBALS['MAIL_TEMPLATES_EDITOR_VIEW_ACCESS'] = array('admin'); diff --git a/src/css/default/base.css b/src/css/default/base.css index 545d2cb6..6a7c88be 100644 --- a/src/css/default/base.css +++ b/src/css/default/base.css @@ -532,6 +532,10 @@ div.LStabs_content h2 { margin-right: 1vw; } + table.LStable { + margin-left: 0; + } + /* * LStabs */ diff --git a/src/css/default/email_templates.css b/src/css/default/email_templates.css new file mode 100644 index 00000000..8ad40264 --- /dev/null +++ b/src/css/default/email_templates.css @@ -0,0 +1,47 @@ +div.LStabs_content form { + display: block; + width: calc(100% - 0.5em); +} + +div.LStabs_content form input[type=text] { + width: 100%; + outline: none; +} + +div.LStabs_content form textarea { + width: 100%; + min-height: 60vh; +} + +div.LStabs_content form div.form-footer { + text-align: center; + width: 100%; + margin-top: 1em; +} + +th.mail_subject { + width: 10em; + white-space: nowrap; + overflow: hidden; +} + +th.mail_content, td.mail_content { + width: 25vw; + white-space: nowrap; + overflow: hidden; +} + +@media (max-width: 1024px) { + th.mail_subject { + widht: auto; + } + th.mail_content, td.mail_content { + max-width: 12em; + } +} + +@media (max-width: 500px) { + th.mail_content, td.mail_content { + display: none; + } +} diff --git a/src/email_templates/welcome.html b/src/email_templates/welcome.html deleted file mode 100644 index feddab87..00000000 --- a/src/email_templates/welcome.html +++ /dev/null @@ -1,7 +0,0 @@ -{literal}{/literal} - -

Welcome {$name}!

- -

This email is just an example of an email template.

- -

Regards,

diff --git a/src/email_templates/welcome.subject b/src/email_templates/welcome.subject deleted file mode 100644 index 44121bd3..00000000 --- a/src/email_templates/welcome.subject +++ /dev/null @@ -1 +0,0 @@ -Welcome {$name}! diff --git a/src/includes/addons/LSaddons.mail.php b/src/includes/addons/LSaddons.mail.php index 3ac60d26..72a13efc 100644 --- a/src/includes/addons/LSaddons.mail.php +++ b/src/includes/addons/LSaddons.mail.php @@ -24,19 +24,33 @@ // Support LSerror :: defineError('MAIL_SUPPORT_01', - ___("MAIL Support : Pear::MAIL is missing.") + ___("MAIL Support: Pear::MAIL is missing.") ); LSerror :: defineError('MAIL_SUPPORT_02', - ___("MAIL Support : Pear::MAIL_MIME is missing.") + ___("MAIL Support: Pear::MAIL_MIME is missing.") +); +LSerror :: defineError('MAIL_SUPPORT_03', + ___("MAIL Support: Html2Text\Html2Text is missing.") ); // Other errors LSerror :: defineError('MAIL_00', - ___("MAIL Error : %{msg}") + ___("MAIL Error: %{msg}") ); - LSerror :: defineError('MAIL_01', - ___("MAIL : Error sending your email") + ___("MAIL: Error sending your email") +); +LSerror :: defineError('MAIL_02', +___("MAIL: Unknown template %{name}.") +); +LSerror :: defineError('MAIL_03', +___("MAIL: Template %{name} is incomplete.") +); +LSerror :: defineError('MAIL_04', + ___("MAIL: No writable place to save your changes on this template.") +); +LSerror :: defineError('MAIL_05', + ___("MAIL: An error occured saving your changes on this template.") ); /** @@ -64,32 +78,89 @@ function LSaddon_mail_support() { } } - if ($retval) - LScli :: add_command( - 'test_send_mail', - 'cli_test_send_mail', - 'Send a test email', - "[-s subject] [-b body] [-H] [recipient1] [...]", - array ( - " -s/--subject The test email subject (optional)", - " -b/--body The test email body (optional)", - " -H/--HTML Enable HTML email body mode (optional)", - " --header Email header using format:", - " header_name=header_value", - " Multiple headers could be specified by using this", - " optional argument multiple time.", - " -a|--attachment Email attachment using format:", - " /path/to/attachment.file[:filename]", - " The filename is optional (default: using source filename).", - " Multiple attachments could be specified by using this", - " optional argument multiple time.", - " --bcc Add Blind Carbon Copy (BCC) recipient(s)", - " --cc Add Carbon Copy (CC) recipient(s)", - " recipients The test email recipient(s) (required).", + if (!$retval) + return false; + + $GLOBALS['MAIL_LOGGER'] = LSlog :: get_logger('LSaddon_mail'); + + LScli :: add_command( + 'test_send_mail', + 'cli_test_send_mail', + 'Send a test email', + "[-s subject] [-b body] [-H] [recipient1] [...]", + array ( + " -s/--subject The test email subject (optional)", + " -b/--body The test email body (optional)", + " -H/--HTML Enable HTML email body mode (optional)", + " --header Email header using format:", + " header_name=header_value", + " Multiple headers could be specified by using this", + " optional argument multiple time.", + " -a|--attachment Email attachment using format:", + " /path/to/attachment.file[:filename]", + " The filename is optional (default: using source filename).", + " Multiple attachments could be specified by using this", + " optional argument multiple time.", + " --bcc Add Blind Carbon Copy (BCC) recipient(s)", + " --cc Add Carbon Copy (CC) recipient(s)", + " recipients The test email recipient(s) (required).", + ), + false, // This command does not need LDAP connection + 'cli_test_send_mail_autocompleter' + ); + + // Handle mail templates stuff + LScli :: add_command( + 'test_send_mail_template', + 'cli_test_send_mail_template', + 'Test to send an email template', + '[template] [-V var1=value] [recipient1] [recipient2]', + array( + ' - Positional arguments :', + ' - email template name', + ' - email recipient(s)', + '', + ' - Optional arguments :', + ' -V|--variable Template variable using format:', + ' variable_name=variable_value', + ' Multiple variables could be specified by using this', + ' optional argument multiple time.', + ' -H|--header Email header using format:', + ' header_name=header_value', + ' Multiple headers could be specified by using this', + ' optional argument multiple time.', + ' -a|--attachment Email attachment using format:', + ' /path/to/attachment.file[:filename]', + ' The filename is optional (default: using source filename).', + ' Multiple attachments could be specified by using this', + ' optional argument multiple time.', + ' --bcc Add Blind Carbon Copy (BCC) recipient(s)', + ' --cc Add Carbon Copy (CC) recipient(s)', ), false, // This command does not need LDAP connection - 'cli_test_send_mail_autocompleter' - ); + 'cli_test_send_mail_from_template_autocompleter' + ); + if ( + isset($GLOBALS['MAIL_TEMPLATES_EDITOR_VIEW_ACCESS']) + && is_array($GLOBALS['MAIL_TEMPLATES_EDITOR_VIEW_ACCESS']) + && $GLOBALS['MAIL_TEMPLATES_EDITOR_VIEW_ACCESS'] + && list_mail_templates() + ) { + if (!class_exists('\Html2Text\Html2Text')) { + if(!LSsession::includeFile(HTML2TEXT, true)) { + LSerror :: addErrorCode('MAIL_SUPPORT_03'); + $retval = false; + } + } + if ($retval) + LSsession :: registerLSaddonView( + 'mail', 'templates', + _('Email templates'), + 'email_templates_view', + $GLOBALS['MAIL_TEMPLATES_EDITOR_VIEW_ACCESS'] + ); + } + return $retval; } @@ -215,6 +286,295 @@ function sendMail($to, $subject, $msg, $headers=null, $attachments=null, return true; } +/** + * List email templates directories + * @return array + */ +function list_mail_templates_directories() { + if (isset($GLOBALS['MAIL_TEMPLATES_DIRECTORIES_CACHE'])) + return $GLOBALS['MAIL_TEMPLATES_DIRECTORIES_CACHE']; + if ( + !isset($GLOBALS['MAIL_TEMPLATES_DIRECTORIES']) + || !is_array($GLOBALS['MAIL_TEMPLATES_DIRECTORIES']) + ) + return []; + $GLOBALS['MAIL_TEMPLATES_DIRECTORIES_CACHE'] = []; + foreach($GLOBALS['MAIL_TEMPLATES_DIRECTORIES'] as $directory) { + $path = new SplFileInfo( + substr($directory, 0,1) == '/'? + $directory: + LS_ROOT_DIR."/".$directory + ); + if ($path && $path->isDir()) + $GLOBALS['MAIL_TEMPLATES_DIRECTORIES_CACHE'][] = $path; + else + $GLOBALS['MAIL_LOGGER'] -> warning( + "list_mail_templates_directories(): directory {$directory} does not exists." + ); + } + $GLOBALS['MAIL_LOGGER'] -> debug( + "list_mail_templates_directories(): directories = ". + implode(', ', $GLOBALS['MAIL_TEMPLATES_DIRECTORIES_CACHE']) + ); + return $GLOBALS['MAIL_TEMPLATES_DIRECTORIES_CACHE']; +} + +/** + * List exiting email templates + * @return array> + * [ + * '[name]' => [ + * 'subject' => '/path/to/name.subject' or null, + * 'html' => '/path/to/name.html' or null, + * 'txt' => '/path/to/name.txt' or null, + * ], + * [...] + * ] + */ +function list_mail_templates() { + if (isset($GLOBALS['MAIL_TEMPLATES']) && $GLOBALS['MAIL_TEMPLATES']) + return $GLOBALS['MAIL_TEMPLATES']; + $GLOBALS['MAIL_TEMPLATES'] = []; + $expected_extensions = ['subject', 'html', 'txt']; + foreach(list_mail_templates_directories() as $directory) { + foreach (new DirectoryIterator($directory) as $fileInfo) { + if( + $fileInfo->isDot() + || !$fileInfo->isFile() + || !$fileInfo->isReadable() + || !in_array($fileInfo->getExtension(), $expected_extensions) + ) + continue; + $name = $fileInfo->getBasename(".".$fileInfo->getExtension()); + if (!array_key_exists($name, $GLOBALS['MAIL_TEMPLATES'])) { + $GLOBALS['MAIL_TEMPLATES'][$name] = []; + foreach($expected_extensions as $ext) $GLOBALS['MAIL_TEMPLATES'][$name][$ext] = null; + } + if (!$GLOBALS['MAIL_TEMPLATES'][$name][$fileInfo->getExtension()]) + $GLOBALS['MAIL_TEMPLATES'][$name][$fileInfo->getExtension()] = $fileInfo->getRealPath(); + } + } + return $GLOBALS['MAIL_TEMPLATES']; +} + +/** + * Search for writable path to save change of a template file + * @param string $template The template name + * @param string $extension The template file extension (subject, html or txt) + * @return string|false The path of writable template file if found, false otherwise + */ +function get_mail_template_saved_path($template, $extension) { + $templates = list_mail_templates(); + if (!array_key_exists($template, $templates)) + return false; + $found = false; + $first_writable = false; + foreach(list_mail_templates_directories() as $directory) { + $file_path = new SplFileInfo("$directory/$template.$extension"); + if ($file_path->isFile()) { + // File exist, check is writable + if ($file_path->isWritable()) + return $file_path->getRealPath(); + // If we don't find previously a writable file, trigger an error + if (!$first_writable) { + $GLOBALS['MAIL_LOGGER'] -> error( + "get_mail_template_saved_path($template, $extension): file '{$file_path->getRealPath()}' ". + "is not writable, can't saved this template file." + ); + return false; + } + continue; + } + else if (!$first_writable && $directory->isWritable()) { + $first_writable = strval($file_path); + } + } + // No existing writable file found + if ($first_writable) return $first_writable; + $GLOBALS['MAIL_LOGGER'] -> error( + "get_mail_template_saved_path($template, $extension): ". + "no writable path found, can't saved this template file." + ); + return false; +} + +/** + * Send email from template + * @param string $tplname The email template name + * @param string $to The email recipient + * @param array $variables Variables to use to compute the template + * @param array|null $headers Email headers + * @param array|null $attachments Email attachments as an array with + * filepath as key and filename as value + * @return boolean True if the email was sent, false otherwise + */ +function sendMailFromTemplate( + $tplname, $to, $variables=null, $headers=null, $attachments=null +) { + $templates = list_mail_templates(); + if (!array_key_exists($tplname, $templates)) { + LSerror :: addErrorCode('MAIL_02', $tplname); + return False; + } + + $tpl = $templates[$tplname]; + if (!$tpl['subject'] || !($tpl['txt'] || $tpl['html'])) { + LSerror :: addErrorCode('MAIL_03', $tplname); + return False; + } + + $smarty = new Smarty(); + $smarty -> setCompileDir(LS_TMP_DIR_PATH); + + if (is_array($variables)) + array_map([$smarty, "assign"], array_keys($variables), array_values($variables)); + + try { + $subject = $smarty -> fetch("file:{$tpl['subject']}"); + // Multiple line from subject cause problem, trim it and only the first line + $subject = explode("\n", trim($subject))[0]; + + $GLOBALS['MAIL_LOGGER'] -> debug( + "sendMailFromTemplate($tplname, ".implode("|", $to)."): ". + "subject compute from '{$tpl['subject']}'." + ); + if ($tpl['html']) { + $message = $smarty -> fetch("file:{$tpl['html']}"); + $html = true; + $GLOBALS['MAIL_LOGGER'] -> debug( + "sendMailFromTemplate($tplname, ".implode("|", $to)."): ". + "HTML content compute from '{$tpl['html']}'." + ); + } + else { + $message = $smarty -> fetch("file:{$tpl['txt']}"); + $html = false; + $GLOBALS['MAIL_LOGGER'] -> debug( + "sendMailFromTemplate($tplname, ".implode("|", $to)."): ". + "text content compute from '{$tpl['txt']}'." + ); + } + } + catch (Exception $e) { + $GLOBALS['MAIL_LOGGER'] -> exception( + $e, getFData( + _("An exception occured forging message from email template '%{template}'"), + $tplname + ), + false + ); + return false; + } + + return sendMail($to, $subject, $message, $headers, $attachments, "\n", "utf8", $html); +} + +/** + * Email templates management view + * @return void + */ +function email_templates_view() { + $template = isset($_REQUEST['name'])?$_REQUEST['name']:null; + $templates = []; + foreach(list_mail_templates() as $name => $tpl) { + if ($template && $template != $name) + continue; + $templates[$name] = [ + 'name' => $name, + 'subject' => $tpl['subject']?file_get_contents($tpl['subject']):null, + 'html' => $tpl['html']?file_get_contents($tpl['html']):null, + 'txt' => $tpl['txt']?file_get_contents($tpl['txt']):null, + ]; + if ($template) continue; + if ($templates[$name]['html']) { + $Html2Text = new \Html2Text\Html2Text($templates[$name]['html']); + $templates[$name]['html'] = substr($Html2Text->getText(), 0, 70)."..."; + } + if ($templates[$name]['txt']) { + $templates[$name]['txt'] = substr($templates[$name]['txt'], 0, 70)."..."; + } + } + if ($template) { + if (!array_key_exists($template, $templates)) { + LSurl::redirect("addon/mail/templates"); + } + LStemplate :: assign('pagetitle', getFData(_('Email template: %{name}'), $template)); + $tab = isset($_REQUEST['tab'])?$_REQUEST['tab']:'subject'; + $path = false; + switch ($tab) { + case 'subject': + $path = get_mail_template_saved_path($template, $tab); + if (array_key_exists('subject', $_POST)) { + if (!$path) + LSerror :: addErrorCode('MAIL_04'); + elseif (file_put_contents($path, $_POST['subject']) !== false) { + LSsession :: addInfo(_("Your changes have been saved.")); + LSurl::redirect("addon/mail/templates?name=".urlencode($template)."&tab=$tab"); + } + else { + LSerror :: addErrorCode('MAIL_05'); + $tpl['subject'] = $_POST['subject']; + } + } + break; + + case 'html': + $path = get_mail_template_saved_path($template, $tab); + if (array_key_exists('html', $_POST)) { + if (!$path) + LSerror :: addErrorCode('MAIL_04'); + elseif (file_put_contents($path, $_POST['html']) !== false) { + LSsession :: addInfo(_("Your changes have been saved.")); + LSurl::redirect("addon/mail/templates?name=".urlencode($template)."&tab=$tab"); + } + else { + LSerror :: addErrorCode('MAIL_05'); + $tpl['html'] = $_POST['html']; + } + } + LStemplate :: addLibJSscript('tinymce/js/tinymce/tinymce.min.js'); + LStemplate :: addJSscript('email_templates.js'); + break; + + case 'txt': + $path = get_mail_template_saved_path($template, $tab); + if (array_key_exists('txt', $_POST)) { + if (!$path) + LSerror :: addErrorCode('MAIL_04'); + elseif (file_put_contents($path, $_POST['txt']) !== false) { + LSsession :: addInfo(_("Your changes have been saved.")); + LSurl::redirect("addon/mail/templates?name=".urlencode($template)."&tab=$tab"); + } + else { + LSerror :: addErrorCode('MAIL_05'); + $tpl['txt'] = $_POST['txt']; + } + } + break; + + default: + LSurl::redirect("addon/mail/templates?name=".urlencode($template)); + } + LStemplate :: assign('template', $templates[$template]); + LStemplate :: assign('tab', $tab); + LStemplate :: assign('writable', boolval($path)); + $LSview_actions = array(); + $LSview_actions['return'] = array ( + 'label' => _('Go back'), + 'url' => 'addon/mail/templates', + 'action' => 'view' + ); + LStemplate :: assign('LSview_actions', $LSview_actions); + LSsession :: setTemplate('email_template.tpl'); + } + else { + LStemplate :: assign('pagetitle', _('Email templates')); + LStemplate :: assign('templates', $templates); + LSsession :: setTemplate('email_templates.tpl'); + } + LStemplate :: addCssFile('email_templates.css'); +} + if (php_sapi_name() != 'cli') return true; @@ -356,3 +716,132 @@ function cli_test_send_mail_autocompleter($comp_words, $comp_word_num, $comp_wor ); return LScli :: autocomplete_opts($opts, $comp_word); } + +/** + * CLI test_send_mail_template command + * + * @param array $command_args Command arguments : + * - Positional arguments : + * - template name + * - recipient + * - Optional arguments : + * - -V|--variable: template variable (format: variable=value) + * - -H|--header: header (format: header=value) + * - -a|--attachent: (format: /path/to/file.ext:filename or just /path/to/file.ext) + * - -bcc: BCC recipient(s) + * - -cc: CC recipient(s) + * + * @return boolean True on success, false otherwise + **/ +function cli_test_send_mail_template($command_args) { + $template = null; + $recipients = array(); + $variables = array(); + $headers = array(); + $attachments = array(); + for ($i=0; $i < count($command_args); $i++) { + LScli :: unquote_word($command_args[$i]); + if (in_array($command_args[$i], array('-V', '--variable'))) { + $i++; + LScli :: unquote_word($command_args[$i]); + $parts = explode('=', $command_args[$i]); + if (count($parts) != 2) + LScli :: usage('Invalid variable string ('.$command_args[$i].').'); + if (array_key_exists($parts[0], $variables)) + LScli :: usage('Variable "'.$parts[0].'" already specified.'); + $variables[$parts[0]] = $parts[1]; + } + elseif (in_array($command_args[$i], array('-H', '--header'))) { + $i++; + LScli :: unquote_word($command_args[$i]); + $parts = explode('=', $command_args[$i]); + if (count($parts) != 2) + LScli :: usage('Invalid header string ('.$command_args[$i].').'); + if (array_key_exists($parts[0], $headers)) + LScli :: usage('Header "'.$parts[0].'" already specified.'); + $headers[$parts[0]] = $parts[1]; + } + elseif (in_array($command_args[$i], array('-a', '--attachent'))) { + $i++; + LScli :: unquote_word($command_args[$i]); + $parts = explode(':', $command_args[$i]); + $path = $parts[0]; + if (!is_file($path)) + LScli :: usage('Invalid attachment "'.$command_args[$i].'": file not found.'); + $attachments[$path] = count($parts) >= 2?$parts[1]:basename($path); + } + elseif ($command_args[$i] == '--bcc') { + $i++; + LScli :: unquote_word($command_args[$i]); + if (!checkEmail($command_args[$i])) + LScli :: usage('Invalid BCC recipient "'.$command_args[$i].'".'); + $headers['BCC'] = isset($headers['BCC'])?ensureIsArray($headers['BCC']):[]; + $headers['BCC'][] = $command_args[$i]; + } + elseif ($command_args[$i] == '--cc') { + $i++; + LScli :: unquote_word($command_args[$i]); + if (!checkEmail($command_args[$i])) + LScli :: usage('Invalid CC recipient "'.$command_args[$i].'".'); + $headers['CC'] = isset($headers['CC'])?ensureIsArray($headers['CC']):[]; + $headers['CC'][] = $command_args[$i]; + } + else if (is_null($template)) { + $template = $command_args[$i]; + } + else if (checkEmail($command_args[$i])) { + $recipients[] = $command_args[$i]; + } + else + LScli :: usage('Invalid recipient "'.$command_args[$i].'".'); + } + + if (is_null($template) || empty($recipients)) + LScli :: usage('You must provide email template name and at least one recipient.'); + + return sendMailFromTemplate( + $template, + $recipients, + $variables, + $headers, + $attachments + ); +} + +/** + * Args autocompleter for CLI test_send_mail_from_template command + * + * @param array $comp_words List of already typed words of the command + * @param int $comp_word_num The command word number to autocomplete + * @param string $comp_word The command word to autocomplete + * @param array $opts List of global available options + * + * @return array List of available options for the word to autocomplete + **/ +function cli_test_send_mail_from_template_autocompleter( + $comp_words, $comp_word_num, $comp_word, $opts +) { + if (isset($comp_words[$comp_word_num-1])) + switch ($comp_words[$comp_word_num-1]) { + case '-v': + case '--variable': + case '-H': + case '--header': + case '-a': + case '--attachment': + case '--bcc': + case '--cc': + return array(); + break; + } + $opts = array_merge( + $opts, + array ( + '-v', '--variable', + '-H', '--header', + '-a', '--attachment', + '--bcc', '--cc', + ) + ); + return LScli :: autocomplete_opts($opts, $comp_word); +} diff --git a/src/includes/class/class.LSmail.php b/src/includes/class/class.LSmail.php index dfe64dd9..ada70af3 100644 --- a/src/includes/class/class.LSmail.php +++ b/src/includes/class/class.LSmail.php @@ -22,19 +22,6 @@ class LSmail extends LSlog_staticLoggerClass { - /** - * Array of templates directories where file have to be search - * @var array - */ - private static $template_directories = array('local', './'); - - /** - * Cache of loaded templates - * @see list_templates() - * @var array> - */ - private static $_templates = null; - /* * Méthode chargeant les dépendances d'affichage * @@ -94,287 +81,4 @@ class LSmail extends LSlog_staticLoggerClass { } } - /** - * List exiting email templates - * @return array> - * [ - * '[name]' => [ - * 'subject' => '/path/to/name.subject' or null, - * 'html' => '/path/to/name.html' or null, - * 'txt' => '/path/to/name.txt' or null, - * ], - * [...] - * ] - */ - public static function list_templates() { - if (self :: $_templates) - return self :: $_templates; - self :: $_templates = []; - $expected_extensions = ['subject', 'html', 'txt']; - foreach(self :: $template_directories as $dir) { - $dir_path = realpath(LS_ROOT_DIR."/".$dir."/email_templates"); - if ($dir_path === false) - // Directory not found or not accessible - continue; - foreach (new DirectoryIterator($dir_path) as $fileInfo) { - if( - $fileInfo->isDot() - || !$fileInfo->isFile() - || !$fileInfo->isReadable() - || !in_array($fileInfo->getExtension(), $expected_extensions) - ) - continue; - $name = $fileInfo->getBasename(".".$fileInfo->getExtension()); - if (!array_key_exists($name, self :: $_templates)) { - self :: $_templates[$name] = []; - foreach($expected_extensions as $ext) self :: $_templates[$name][$ext] = null; - } - if (!self :: $_templates[$name][$fileInfo->getExtension()]) - self :: $_templates[$name][$fileInfo->getExtension()] = $fileInfo->getRealPath(); - } - } - return self :: $_templates; - } - - /** - * Send email from template - * @param string $tplname The email template name - * @param string $to The email recipient - * @param array $variables Variables to use to compute the template - * @param array|null $headers Email headers - * @param array|null $attachments Email attachments as an array with - * filepath as key and filename as value - * @return boolean True if the email was sent, false otherwise - */ - public static function send_mail_from_template( - $tplname, $to, $variables=null, $headers=null, $attachments=null - ) { - $templates = self :: list_templates(); - if (!array_key_exists($tplname, $templates)) { - LSerror :: addErrorCode('LSmail_01', $tplname); - return False; - } - - $tpl = $templates[$tplname]; - if (!$tpl['subject'] || !($tpl['txt'] || $tpl['html'])) { - LSerror :: addErrorCode('LSmail_02', $tplname); - return False; - } - - $smarty = new Smarty(); - - if (is_array($variables)) - array_map([$smarty, "assign"], array_keys($variables), array_values($variables)); - - try { - $subject = $smarty -> fetch("file:{$tpl['subject']}"); - // Multiple line from subject cause problem, trim it and only the first line - $subject = explode("\n", trim($subject))[0]; - - self :: log_debug( - "send_mail_from_template($tplname, ".implode("|", $to)."): ". - "subject compute from '{$tpl['subject']}'." - ); - if ($tpl['html']) { - $body = $smarty -> fetch("file:{$tpl['html']}"); - $html = true; - self :: log_debug( - "send_mail_from_template($tplname, ".implode("|", $to)."): ". - "HTML body compute from '{$tpl['html']}'." - ); - } - else { - $body = $smarty -> fetch("file:{$tpl['txt']}"); - $html = false; - self :: log_debug( - "send_mail_from_template($tplname, ".implode("|", $to)."): ". - "text body compute from '{$tpl['txt']}'." - ); - } - } - catch (Exception $e) { - self :: log_exception( - $e, getFData( - _("LSmail - An exception occured forging message from email template '%{template}'"), - $tplname - ), - false - ); - return false; - } - - return sendMail($to, $subject, $body, $headers, $attachments, "\n", "utf8", $html); - } - - /** - * CLI test_send_mail_template command - * - * @param array $command_args Command arguments : - * - Positional arguments : - * - template name - * - recipient - * - Optional arguments : - * - -V|--variable: template variable (format: variable=value) - * - -H|--header: header (format: header=value) - * - -a|--attachent: (format: /path/to/file.ext:filename or just /path/to/file.ext) - * - -bcc: BCC recipient(s) - * - -cc: CC recipient(s) - * - * @return boolean True on success, false otherwise - **/ - public static function cli_test_send_mail_template($command_args) { - $template = null; - $recipients = array(); - $variables = array(); - $headers = array(); - $attachments = array(); - for ($i=0; $i < count($command_args); $i++) { - LScli :: unquote_word($command_args[$i]); - if (in_array($command_args[$i], array('-V', '--variable'))) { - $i++; - LScli :: unquote_word($command_args[$i]); - $parts = explode('=', $command_args[$i]); - if (count($parts) != 2) - LScli :: usage('Invalid variable string ('.$command_args[$i].').'); - if (array_key_exists($parts[0], $variables)) - LScli :: usage('Variable "'.$parts[0].'" already specified.'); - $variables[$parts[0]] = $parts[1]; - } - elseif (in_array($command_args[$i], array('-H', '--header'))) { - $i++; - LScli :: unquote_word($command_args[$i]); - $parts = explode('=', $command_args[$i]); - if (count($parts) != 2) - LScli :: usage('Invalid header string ('.$command_args[$i].').'); - if (array_key_exists($parts[0], $headers)) - LScli :: usage('Header "'.$parts[0].'" already specified.'); - $headers[$parts[0]] = $parts[1]; - } - elseif (in_array($command_args[$i], array('-a', '--attachent'))) { - $i++; - LScli :: unquote_word($command_args[$i]); - $parts = explode(':', $command_args[$i]); - $path = $parts[0]; - if (!is_file($path)) - LScli :: usage('Invalid attachment "'.$command_args[$i].'": file not found.'); - $attachments[$path] = count($parts) >= 2?$parts[1]:basename($path); - } - elseif ($command_args[$i] == '--bcc') { - $i++; - LScli :: unquote_word($command_args[$i]); - if (!checkEmail($command_args[$i])) - LScli :: usage('Invalid BCC recipient "'.$command_args[$i].'".'); - $headers['BCC'] = isset($headers['BCC'])?ensureIsArray($headers['BCC']):[]; - $headers['BCC'][] = $command_args[$i]; - } - elseif ($command_args[$i] == '--cc') { - $i++; - LScli :: unquote_word($command_args[$i]); - if (!checkEmail($command_args[$i])) - LScli :: usage('Invalid CC recipient "'.$command_args[$i].'".'); - $headers['CC'] = isset($headers['CC'])?ensureIsArray($headers['CC']):[]; - $headers['CC'][] = $command_args[$i]; - } - else if (is_null($template)) { - $template = $command_args[$i]; - } - else if (checkEmail($command_args[$i])) { - $recipients[] = $command_args[$i]; - } - else - LScli :: usage('Invalid recipient "'.$command_args[$i].'".'); - } - - if (is_null($template) || empty($recipients)) - LScli :: usage('You must provide email template name and at least one recipient.'); - - return self :: send_mail_from_template( - $template, - $recipients, - $variables, - $headers, - $attachments - ); - } - - /** - * Args autocompleter for CLI test_send_mail_from_template command - * - * @param array $comp_words List of already typed words of the command - * @param int $comp_word_num The command word number to autocomplete - * @param string $comp_word The command word to autocomplete - * @param array $opts List of global available options - * - * @return array List of available options for the word to autocomplete - **/ - public static function cli_test_send_mail_from_template_autocompleter( - $comp_words, $comp_word_num, $comp_word, $opts - ) { - if (isset($comp_words[$comp_word_num-1])) - switch ($comp_words[$comp_word_num-1]) { - case '-v': - case '--variable': - case '-H': - case '--header': - case '-a': - case '--attachment': - case '--bcc': - case '--cc': - return array(); - break; - } - $opts = array_merge( - $opts, - array ( - '-v', '--variable', - '-H', '--header', - '-a', '--attachment', - '--bcc', '--cc', - ) - ); - return LScli :: autocomplete_opts($opts, $comp_word); - } - } - -/** - * Error Codes - */ -LSerror :: defineError('LSmail_01', -___("LSmail: Unknown template %{name}.") -); -LSerror :: defineError('LSmail_02', -___("LSmail: Template %{name} is incomplete.") -); - -// LScli -LScli :: add_command( - 'test_send_mail_template', - array('LSmail', 'cli_test_send_mail_template'), - 'Test to send an email template', - '[template] [-V var1=value] [recipient1] [recipient2]', - array( - ' - Positional arguments :', - ' - email template name', - ' - email recipient(s)', - '', - ' - Optional arguments :', - ' -V|--variable Template variable using format:', - ' variable_name=variable_value', - ' Multiple variables could be specified by using this', - ' optional argument multiple time.', - ' -H|--header Email header using format:', - ' header_name=header_value', - ' Multiple headers could be specified by using this', - ' optional argument multiple time.', - ' -a|--attachment Email attachment using format:', - ' /path/to/attachment.file[:filename]', - ' The filename is optional (default: using source filename).', - ' Multiple attachments could be specified by using this', - ' optional argument multiple time.', - ' --bcc Add Blind Carbon Copy (BCC) recipient(s)', - ' --cc Add Carbon Copy (CC) recipient(s)', - ), - false, // This command does not need LDAP connection - array('LSmail', 'cli_test_send_mail_from_template_autocompleter') -); diff --git a/src/includes/js/email_templates.js b/src/includes/js/email_templates.js new file mode 100644 index 00000000..1abf00ed --- /dev/null +++ b/src/includes/js/email_templates.js @@ -0,0 +1,20 @@ +window.addEvent(window.ie ? 'load' : 'domready', function() { + var textarea = document.getElement('textarea[name=html]'); + if (!textarea) return; + var options = {}; + if (textarea.disabled) { + options.readonly = 1; + options.setup = function(editor) { + editor.on('skinLoaded', function(e) { + editor.notificationManager.open({ + text: textarea.get('title'), + type: 'warning' + }); + }); + }; + } + + options.target = textarea; + options.language = varLSdefault.getCurrentLang(); + tinymce.init(options); +}); diff --git a/src/lang/fr_FR.UTF8/LC_MESSAGES/ldapsaisie.mo b/src/lang/fr_FR.UTF8/LC_MESSAGES/ldapsaisie.mo index 3a77dfee..79ab9d4e 100644 Binary files a/src/lang/fr_FR.UTF8/LC_MESSAGES/ldapsaisie.mo and b/src/lang/fr_FR.UTF8/LC_MESSAGES/ldapsaisie.mo differ diff --git a/src/lang/fr_FR.UTF8/LC_MESSAGES/ldapsaisie.po b/src/lang/fr_FR.UTF8/LC_MESSAGES/ldapsaisie.po index 13e888b4..686decfe 100644 --- a/src/lang/fr_FR.UTF8/LC_MESSAGES/ldapsaisie.po +++ b/src/lang/fr_FR.UTF8/LC_MESSAGES/ldapsaisie.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: LdapSaisie\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2023-12-05 12:48+0100\n" +"PO-Revision-Date: 2024-02-22 15:25+0100\n" "Last-Translator: Benjamin Renard \n" "Language-Team: LdapSaisie \n" @@ -82,7 +82,7 @@ msgstr "Expiré (depuis %s)" #, php-format msgid "Last password changed: %s, duration of validity: %s days" msgstr "" -"Dernière modification du mot de passe : %s, durée de validité : %s jours." +"Dernière modification du mot de passe : %s, durée de validité : %s jours" #: includes/addons/LSaddons.ppolicy.php:197 #, php-format @@ -230,27 +230,55 @@ msgstr "" "ligne CSV." #: includes/addons/LSaddons.mail.php:27 -msgid "MAIL Support : Pear::MAIL is missing." +msgid "MAIL Support: Pear::MAIL is missing." msgstr "Support MAIL : Pear::MAIL est manquant." #: includes/addons/LSaddons.mail.php:30 -msgid "MAIL Support : Pear::MAIL_MIME is missing." +msgid "MAIL Support: Pear::MAIL_MIME is missing." msgstr "Support MAIL : Pear::MAIL_MIME est manquant." -#: includes/addons/LSaddons.mail.php:35 -msgid "MAIL Error : %{msg}" +#: includes/addons/LSaddons.mail.php:33 +msgid "MAIL Support: Html2Text\\Html2Text is missing." +msgstr "Support MAIL : Html2Text\\Html2Text est manquant." + +#: includes/addons/LSaddons.mail.php:38 +msgid "MAIL Error: %{msg}" msgstr "Erreur MAIL : %{msg}" -#: includes/addons/LSaddons.mail.php:39 -msgid "MAIL : Error sending your email" -msgstr "MAIL : Erreur durant l'envoie de votre mail" +#: includes/addons/LSaddons.mail.php:41 +msgid "MAIL: Error sending your email" +msgstr "MAIL : Erreur durant l'envoi de votre mail" -#: includes/addons/LSaddons.mail.php:125 +#: includes/addons/LSaddons.mail.php:44 +msgid "MAIL: Unknown template %{name}." +msgstr "MAIL : Template %{name} inconnu." + +#: includes/addons/LSaddons.mail.php:47 +msgid "MAIL: Template %{name} is incomplete." +msgstr "MAIL : Le template %{name} est incomplet." + +#: includes/addons/LSaddons.mail.php:50 +msgid "MAIL: No writable place to save your changes on this template." +msgstr "" +"MAIL : Aucun emplacement accessible en écriture pour enregistrer vos " +"modifications sur ce modèle." + +#: includes/addons/LSaddons.mail.php:53 +msgid "MAIL: An error occured saving your changes on this template." +msgstr "" +"MAIL : Une erreur est survenue en enregistrant vos modifications sur ce " +"modèle." + +#: includes/addons/LSaddons.mail.php:158 includes/addons/LSaddons.mail.php:568 +msgid "Email templates" +msgstr "Modèles de courriels" + +#: includes/addons/LSaddons.mail.php:206 #, php-format msgid "

Mail initialy intended for %s.

" msgstr "

Mail initialement adressé à %s.

" -#: includes/addons/LSaddons.mail.php:126 +#: includes/addons/LSaddons.mail.php:207 #, php-format msgid "" "\n" @@ -263,12 +291,12 @@ msgstr "" "\n" "Mail initialement adressé à %s." -#: includes/addons/LSaddons.mail.php:156 +#: includes/addons/LSaddons.mail.php:235 #, php-format msgid "

%s: %s

" msgstr "

%s: %s

" -#: includes/addons/LSaddons.mail.php:157 +#: includes/addons/LSaddons.mail.php:236 #, php-format msgid "" "\n" @@ -277,6 +305,28 @@ msgstr "" "\n" "%s: %s" +#: includes/addons/LSaddons.mail.php:458 +msgid "An exception occured forging message from email template '%{template}'" +msgstr "" +"Une exception est survenue en générant le message à partir du modèle de " +"courriel '%{template}'" + +#: includes/addons/LSaddons.mail.php:498 +msgid "Email template: %{name}" +msgstr "Modèle de courriel : %{name}" + +#: includes/addons/LSaddons.mail.php:508 includes/addons/LSaddons.mail.php:524 +#: includes/addons/LSaddons.mail.php:542 +msgid "Your changes have been saved." +msgstr "Vos modifications ont été enregistrées." + +#: includes/addons/LSaddons.mail.php:560 +#: includes/addons/LSaddons.showSupportInfo.php:78 +#: includes/addons/LSaddons.accesslog.php:248 +#: includes/addons/LSaddons.showTechInfo.php:117 +msgid "Go back" +msgstr "Retour" + #: includes/addons/LSaddons.phpldapadmin.php:27 msgid "PhpLdapAdmin Support : The constant %{const} is not defined." msgstr "Support PhpLdapAdmin : La constante %{const} n'est pas définie." @@ -293,12 +343,6 @@ msgstr "%{name} : Informations pour le support" msgid "Download" msgstr "Télécharger" -#: includes/addons/LSaddons.showSupportInfo.php:78 -#: includes/addons/LSaddons.accesslog.php:248 -#: includes/addons/LSaddons.showTechInfo.php:117 -msgid "Go back" -msgstr "Retour" - #: includes/addons/LSaddons.dyngroup.php:27 msgid "Dynamic groups support: The constant %{const} is not defined." msgstr "" @@ -409,8 +453,8 @@ msgstr "Comparer" #: includes/class/class.LSrelation.php:679 includes/class/class.LSform.php:346 #: includes/class/class.LSformElement_select_object.php:75 #: includes/class/class.LSformElement_select_object.php:91 -#: includes/class/class.LSsearchEntry.php:237 includes/routes.php:1068 -#: includes/routes.php:1212 +#: includes/class/class.LSsearchEntry.php:237 includes/routes.php:1075 +#: includes/routes.php:1219 msgid "Delete" msgstr "Supprimer" @@ -423,8 +467,8 @@ msgstr "Étendue" #: includes/class/class.LSrelation.php:736 #: includes/class/class.LSformElement_select_object.php:74 #: includes/class/class.LSformElement_supannLabeledValue.php:90 -#: includes/class/class.LSsearchEntry.php:221 includes/routes.php:1052 -#: includes/routes.php:1220 includes/routes.php:1305 includes/routes.php:1451 +#: includes/class/class.LSsearchEntry.php:221 includes/routes.php:1059 +#: includes/routes.php:1227 includes/routes.php:1313 includes/routes.php:1460 msgid "Modify" msgstr "Modifier" @@ -432,7 +476,7 @@ msgstr "Modifier" msgid "Modify RDN" msgstr "Modifier le RDN" -#: includes/addons/LSaddons.accesslog.php:35 includes/routes.php:517 +#: includes/addons/LSaddons.accesslog.php:35 includes/routes.php:521 #: templates/default/select.tpl:28 templates/default/global_search.tpl:6 msgid "Search" msgstr "Rechercher" @@ -460,7 +504,7 @@ msgstr "Voir les modifications d'LdapSaisie" #: includes/addons/LSaddons.accesslog.php:243 #: includes/class/class.LSsession.php:1875 includes/routes.php:157 -#: includes/routes.php:474 templates/default/select.tpl:29 +#: includes/routes.php:478 templates/default/select.tpl:29 msgid "Refresh" msgstr "Rafraîchir" @@ -916,7 +960,7 @@ msgstr "Confirmez-vous votre choix ?" #: includes/class/class.LSconfirmBox.php:37 #: includes/class/class.LSsmoothbox.php:39 includes/class/class.LSform.php:175 -#: includes/routes.php:638 includes/routes.php:1292 includes/routes.php:1438 +#: includes/routes.php:645 includes/routes.php:1300 includes/routes.php:1447 #: templates/default/recoverpassword.tpl:21 msgid "Validate" msgstr "Valider" @@ -2265,8 +2309,8 @@ msgstr "Attention" msgid "No object." msgstr "Aucun objet." -#: includes/class/class.LSrelation.php:747 includes/routes.php:456 -#: includes/routes.php:997 +#: includes/class/class.LSrelation.php:747 includes/routes.php:460 +#: includes/routes.php:1004 msgid "New" msgstr "Nouveau" @@ -2502,13 +2546,13 @@ msgstr "" "Une erreur est survenue en soumettant ce formulaire. Merci de ré-essayer ou " "de contacter le support." -#: includes/class/class.LSform.php:327 includes/routes.php:634 +#: includes/class/class.LSform.php:327 includes/routes.php:641 msgid "Do you really want to execute custom action %{title} on this search ?" msgstr "" "Êtes-vous vraiment sûre de vouloir exécuter l'action personnalisée %{title} " "sur cette recherche ?" -#: includes/class/class.LSform.php:333 includes/routes.php:1432 +#: includes/class/class.LSform.php:333 includes/routes.php:1441 msgid "" "Do you really want to execute custom action %{customAction} on " "%{objectname} ?" @@ -2716,8 +2760,8 @@ msgstr "Réinitialiser le choix." msgid "Display RSS stack." msgstr "Afficher la file RSS." -#: includes/class/class.LSattr_ldap_password.php:108 includes/routes.php:587 -#: includes/routes.php:1379 +#: includes/class/class.LSattr_ldap_password.php:108 includes/routes.php:594 +#: includes/routes.php:1388 msgid "undefined" msgstr "non-définie" @@ -3242,12 +3286,12 @@ msgstr "Erreur inconnue" msgid "Unknown error : %{error}" msgstr "Erreur inconnue : %{error}" -#: includes/class/class.LSsearchEntry.php:213 includes/routes.php:1204 -#: includes/routes.php:1297 includes/routes.php:1443 +#: includes/class/class.LSsearchEntry.php:213 includes/routes.php:1211 +#: includes/routes.php:1305 includes/routes.php:1452 msgid "View" msgstr "Voir" -#: includes/class/class.LSsearchEntry.php:229 includes/routes.php:1060 +#: includes/class/class.LSsearchEntry.php:229 includes/routes.php:1067 msgid "Copy" msgstr "Copier" @@ -3277,66 +3321,66 @@ msgstr "Accueil" msgid "You must provide pattern for global search." msgstr "Vous devez fournir un mot clé pour les recherches globales." -#: includes/routes.php:462 includes/routes.php:818 +#: includes/routes.php:466 includes/routes.php:825 msgid "Import" msgstr "Importer" -#: includes/routes.php:467 includes/routes.php:884 +#: includes/routes.php:471 includes/routes.php:891 msgid "Export" msgstr "Exporter" -#: includes/routes.php:479 +#: includes/routes.php:483 msgid "Reset" msgstr "Réinitialiser" -#: includes/routes.php:518 templates/default/select.tpl:31 +#: includes/routes.php:522 templates/default/select.tpl:31 msgid "Approximative search" msgstr "Recherche approximative" -#: includes/routes.php:519 templates/default/select.tpl:32 +#: includes/routes.php:523 templates/default/select.tpl:32 msgid "Recursive search" msgstr "Recherche récursive" -#: includes/routes.php:608 +#: includes/routes.php:615 msgid "" "The custom action %{title} have been successfully execute on this search." msgstr "" "L'action personnalisée %{title} a été correctement exécutée sur cette " "recherche." -#: includes/routes.php:956 +#: includes/routes.php:963 msgid "Data entry form" msgstr "Masque de saisie" -#: includes/routes.php:962 includes/routes.php:1736 +#: includes/routes.php:969 includes/routes.php:1746 msgid "Object has been added." msgstr "L'objet a été ajouté." -#: includes/routes.php:1099 +#: includes/routes.php:1106 msgid "My account" msgstr "Mon compte" -#: includes/routes.php:1162 includes/routes.php:1908 +#: includes/routes.php:1169 includes/routes.php:1918 msgid "The object has been partially modified." msgstr "L'objet a été partiellement modifié." -#: includes/routes.php:1165 includes/routes.php:1911 +#: includes/routes.php:1172 includes/routes.php:1921 msgid "The object has been modified successfully." msgstr "L'objet a bien été modifié." -#: includes/routes.php:1280 includes/routes.php:1951 +#: includes/routes.php:1288 includes/routes.php:1961 msgid "%{objectname} has been successfully deleted." msgstr "%{objectname} a bien été supprimé." -#: includes/routes.php:1289 +#: includes/routes.php:1297 msgid "Deleting : %{objectname}" msgstr "Suppression : %{objectname}" -#: includes/routes.php:1290 +#: includes/routes.php:1298 msgid "Do you really want to delete %{displayName} ?" msgstr "Voulez-vous vraiment supprimer %{displayName} ?" -#: includes/routes.php:1400 +#: includes/routes.php:1409 msgid "" "The custom action %{customAction} have been successfully execute on " "%{objectname}." @@ -3613,6 +3657,11 @@ msgstr "Format" msgid "Global search" msgstr "Recherche globale" +#: templates/default/email_templates.tpl:9 +#: templates/default/email_template.tpl:12 +msgid "HTML content" +msgstr "Contenu HTML" + #: templates/default/import.tpl:148 msgid "Hook: %(name)" msgstr "Déclencheur : %(name)" @@ -3654,7 +3703,7 @@ msgstr "Message" msgid "Messages" msgstr "Messages" -#: templates/default/viewSearch.tpl:113 +#: templates/default/viewSearch.tpl:128 msgid "Nb / page :" msgstr "Nb / page :" @@ -3662,6 +3711,10 @@ msgstr "Nb / page :" msgid "No access log found for this object." msgstr "Aucun log d'accès trouvé pour cet objet." +#: templates/default/email_templates.tpl:23 +msgid "No existing email template." +msgstr "Aucun modèle de courriel existant." + #: templates/default/LSform_view.tpl:34 msgid "No field." msgstr "Aucun champ." @@ -3674,6 +3727,26 @@ msgstr "Aucun objet importé" msgid "No value" msgstr "Aucune valeur" +#: templates/default/email_template.tpl:30 +msgid "" +"No writable path to save your changes on the HTML message of this template." +msgstr "" +"Aucun emplacement accessible en écriture pour enregistrer vos modifications " +"sur le contenu HTML de ce modèle." + +#: templates/default/email_template.tpl:25 +msgid "No writable path to save your changes on the subject of this template." +msgstr "" +"Aucun emplacement accessible en écriture pour enregistrer vos modifications " +"sur le sujet de ce modèle." + +#: templates/default/email_template.tpl:35 +msgid "" +"No writable path to save your changes on the text message of this template." +msgstr "" +"Aucun emplacement accessible en écriture pour enregistrer vos modifications " +"sur le contenu texte de ce modèle." + #: templates/default/import.tpl:45 msgid "Object %(idx)" msgstr "Objet %(idx)" @@ -3755,14 +3828,29 @@ msgstr "Résultat" msgid "Right inherited from all connected users profile" msgstr "Droit hérité du profile \"Tous les utilisateurs connectés\"" +#: templates/default/email_template.tpl:40 +msgid "Save" +msgstr "Enregistrer" + #: templates/default/base_connected.tpl:6 msgid "Show/hide menu" msgstr "Afficher/cacher le menu" -#: templates/default/LSmail.tpl:21 +#: templates/default/email_templates.tpl:8 +#: templates/default/email_template.tpl:9 +#: templates/default/email_template.tpl:22 templates/default/LSmail.tpl:21 msgid "Subject" msgstr "Sujet" +#: templates/default/email_templates.tpl:7 +msgid "Template name" +msgstr "Nom du modèle" + +#: templates/default/email_templates.tpl:10 +#: templates/default/email_template.tpl:15 +msgid "Text content" +msgstr "Contenu texte" + #: templates/default/LSaccessRightsMatrixView.tpl:58 msgid "Their relations with other objects" msgstr "Leurs relations avec les autres objets" @@ -3804,3 +3892,12 @@ msgstr "événement(s) trouvé(s) pour cet objet." #: templates/default/import.tpl:27 templates/default/import.tpl:33 msgid "no" msgstr "non" + +#~ msgid "HTML body" +#~ msgstr "Corps HTML" + +#~ msgid "HTML" +#~ msgstr "HTML" + +#~ msgid "Text" +#~ msgstr "Texte" diff --git a/src/lang/ldapsaisie.pot b/src/lang/ldapsaisie.pot index ed5748be..996df7ed 100644 --- a/src/lang/ldapsaisie.pot +++ b/src/lang/ldapsaisie.pot @@ -170,27 +170,51 @@ msgid "ExportSearchResultAsCSV Error : An error occured writing a CSV row." msgstr "" #: includes/addons/LSaddons.mail.php:27 -msgid "MAIL Support : Pear::MAIL is missing." +msgid "MAIL Support: Pear::MAIL is missing." msgstr "" #: includes/addons/LSaddons.mail.php:30 -msgid "MAIL Support : Pear::MAIL_MIME is missing." +msgid "MAIL Support: Pear::MAIL_MIME is missing." msgstr "" -#: includes/addons/LSaddons.mail.php:35 -msgid "MAIL Error : %{msg}" +#: includes/addons/LSaddons.mail.php:33 +msgid "MAIL Support: Html2Text\\Html2Text is missing." msgstr "" -#: includes/addons/LSaddons.mail.php:39 -msgid "MAIL : Error sending your email" +#: includes/addons/LSaddons.mail.php:38 +msgid "MAIL Error: %{msg}" msgstr "" -#: includes/addons/LSaddons.mail.php:125 +#: includes/addons/LSaddons.mail.php:41 +msgid "MAIL: Error sending your email" +msgstr "" + +#: includes/addons/LSaddons.mail.php:44 +msgid "MAIL: Unknown template %{name}." +msgstr "" + +#: includes/addons/LSaddons.mail.php:47 +msgid "MAIL: Template %{name} is incomplete." +msgstr "" + +#: includes/addons/LSaddons.mail.php:50 +msgid "MAIL: No writable place to save your changes on this template." +msgstr "" + +#: includes/addons/LSaddons.mail.php:53 +msgid "MAIL: An error occured saving your changes on this template." +msgstr "" + +#: includes/addons/LSaddons.mail.php:158 includes/addons/LSaddons.mail.php:568 +msgid "Email templates" +msgstr "" + +#: includes/addons/LSaddons.mail.php:206 #, php-format msgid "

Mail initialy intended for %s.

" msgstr "" -#: includes/addons/LSaddons.mail.php:126 +#: includes/addons/LSaddons.mail.php:207 #, php-format msgid "" "\n" @@ -199,18 +223,38 @@ msgid "" "Mail initialy intended for %s." msgstr "" -#: includes/addons/LSaddons.mail.php:156 +#: includes/addons/LSaddons.mail.php:235 #, php-format msgid "

%s: %s

" msgstr "" -#: includes/addons/LSaddons.mail.php:157 +#: includes/addons/LSaddons.mail.php:236 #, php-format msgid "" "\n" "%s: %s" msgstr "" +#: includes/addons/LSaddons.mail.php:458 +msgid "An exception occured forging message from email template '%{template}'" +msgstr "" + +#: includes/addons/LSaddons.mail.php:498 +msgid "Email template: %{name}" +msgstr "" + +#: includes/addons/LSaddons.mail.php:508 includes/addons/LSaddons.mail.php:524 +#: includes/addons/LSaddons.mail.php:542 +msgid "Your changes have been saved." +msgstr "" + +#: includes/addons/LSaddons.mail.php:560 +#: includes/addons/LSaddons.showSupportInfo.php:78 +#: includes/addons/LSaddons.accesslog.php:248 +#: includes/addons/LSaddons.showTechInfo.php:117 +msgid "Go back" +msgstr "" + #: includes/addons/LSaddons.phpldapadmin.php:27 msgid "PhpLdapAdmin Support : The constant %{const} is not defined." msgstr "" @@ -227,12 +271,6 @@ msgstr "" msgid "Download" msgstr "" -#: includes/addons/LSaddons.showSupportInfo.php:78 -#: includes/addons/LSaddons.accesslog.php:248 -#: includes/addons/LSaddons.showTechInfo.php:117 -msgid "Go back" -msgstr "" - #: includes/addons/LSaddons.dyngroup.php:27 msgid "Dynamic groups support: The constant %{const} is not defined." msgstr "" @@ -328,8 +366,8 @@ msgstr "" #: includes/class/class.LSrelation.php:679 includes/class/class.LSform.php:346 #: includes/class/class.LSformElement_select_object.php:75 #: includes/class/class.LSformElement_select_object.php:91 -#: includes/class/class.LSsearchEntry.php:237 includes/routes.php:1068 -#: includes/routes.php:1212 +#: includes/class/class.LSsearchEntry.php:237 includes/routes.php:1075 +#: includes/routes.php:1219 msgid "Delete" msgstr "" @@ -342,8 +380,8 @@ msgstr "" #: includes/class/class.LSrelation.php:736 #: includes/class/class.LSformElement_select_object.php:74 #: includes/class/class.LSformElement_supannLabeledValue.php:90 -#: includes/class/class.LSsearchEntry.php:221 includes/routes.php:1052 -#: includes/routes.php:1220 includes/routes.php:1305 includes/routes.php:1451 +#: includes/class/class.LSsearchEntry.php:221 includes/routes.php:1059 +#: includes/routes.php:1227 includes/routes.php:1313 includes/routes.php:1460 msgid "Modify" msgstr "" @@ -351,7 +389,7 @@ msgstr "" msgid "Modify RDN" msgstr "" -#: includes/addons/LSaddons.accesslog.php:35 includes/routes.php:517 +#: includes/addons/LSaddons.accesslog.php:35 includes/routes.php:521 #: templates/default/select.tpl:28 templates/default/global_search.tpl:6 msgid "Search" msgstr "" @@ -379,7 +417,7 @@ msgstr "" #: includes/addons/LSaddons.accesslog.php:243 #: includes/class/class.LSsession.php:1875 includes/routes.php:157 -#: includes/routes.php:474 templates/default/select.tpl:29 +#: includes/routes.php:478 templates/default/select.tpl:29 msgid "Refresh" msgstr "" @@ -779,7 +817,7 @@ msgstr "" #: includes/class/class.LSconfirmBox.php:37 #: includes/class/class.LSsmoothbox.php:39 includes/class/class.LSform.php:175 -#: includes/routes.php:638 includes/routes.php:1292 includes/routes.php:1438 +#: includes/routes.php:645 includes/routes.php:1300 includes/routes.php:1447 #: templates/default/recoverpassword.tpl:21 msgid "Validate" msgstr "" @@ -1915,8 +1953,8 @@ msgstr "" msgid "No object." msgstr "" -#: includes/class/class.LSrelation.php:747 includes/routes.php:456 -#: includes/routes.php:997 +#: includes/class/class.LSrelation.php:747 includes/routes.php:460 +#: includes/routes.php:1004 msgid "New" msgstr "" @@ -2113,11 +2151,11 @@ msgid "" "support." msgstr "" -#: includes/class/class.LSform.php:327 includes/routes.php:634 +#: includes/class/class.LSform.php:327 includes/routes.php:641 msgid "Do you really want to execute custom action %{title} on this search ?" msgstr "" -#: includes/class/class.LSform.php:333 includes/routes.php:1432 +#: includes/class/class.LSform.php:333 includes/routes.php:1441 msgid "" "Do you really want to execute custom action %{customAction} on " "%{objectname} ?" @@ -2307,8 +2345,8 @@ msgstr "" msgid "Display RSS stack." msgstr "" -#: includes/class/class.LSattr_ldap_password.php:108 includes/routes.php:587 -#: includes/routes.php:1379 +#: includes/class/class.LSattr_ldap_password.php:108 includes/routes.php:594 +#: includes/routes.php:1388 msgid "undefined" msgstr "" @@ -2755,12 +2793,12 @@ msgstr "" msgid "Unknown error : %{error}" msgstr "" -#: includes/class/class.LSsearchEntry.php:213 includes/routes.php:1204 -#: includes/routes.php:1297 includes/routes.php:1443 +#: includes/class/class.LSsearchEntry.php:213 includes/routes.php:1211 +#: includes/routes.php:1305 includes/routes.php:1452 msgid "View" msgstr "" -#: includes/class/class.LSsearchEntry.php:229 includes/routes.php:1060 +#: includes/class/class.LSsearchEntry.php:229 includes/routes.php:1067 msgid "Copy" msgstr "" @@ -2786,64 +2824,64 @@ msgstr "" msgid "You must provide pattern for global search." msgstr "" -#: includes/routes.php:462 includes/routes.php:818 +#: includes/routes.php:466 includes/routes.php:825 msgid "Import" msgstr "" -#: includes/routes.php:467 includes/routes.php:884 +#: includes/routes.php:471 includes/routes.php:891 msgid "Export" msgstr "" -#: includes/routes.php:479 +#: includes/routes.php:483 msgid "Reset" msgstr "" -#: includes/routes.php:518 templates/default/select.tpl:31 +#: includes/routes.php:522 templates/default/select.tpl:31 msgid "Approximative search" msgstr "" -#: includes/routes.php:519 templates/default/select.tpl:32 +#: includes/routes.php:523 templates/default/select.tpl:32 msgid "Recursive search" msgstr "" -#: includes/routes.php:608 +#: includes/routes.php:615 msgid "" "The custom action %{title} have been successfully execute on this search." msgstr "" -#: includes/routes.php:956 +#: includes/routes.php:963 msgid "Data entry form" msgstr "" -#: includes/routes.php:962 includes/routes.php:1736 +#: includes/routes.php:969 includes/routes.php:1746 msgid "Object has been added." msgstr "" -#: includes/routes.php:1099 +#: includes/routes.php:1106 msgid "My account" msgstr "" -#: includes/routes.php:1162 includes/routes.php:1908 +#: includes/routes.php:1169 includes/routes.php:1918 msgid "The object has been partially modified." msgstr "" -#: includes/routes.php:1165 includes/routes.php:1911 +#: includes/routes.php:1172 includes/routes.php:1921 msgid "The object has been modified successfully." msgstr "" -#: includes/routes.php:1280 includes/routes.php:1951 +#: includes/routes.php:1288 includes/routes.php:1961 msgid "%{objectname} has been successfully deleted." msgstr "" -#: includes/routes.php:1289 +#: includes/routes.php:1297 msgid "Deleting : %{objectname}" msgstr "" -#: includes/routes.php:1290 +#: includes/routes.php:1298 msgid "Do you really want to delete %{displayName} ?" msgstr "" -#: includes/routes.php:1400 +#: includes/routes.php:1409 msgid "" "The custom action %{customAction} have been successfully execute on " "%{objectname}." @@ -3104,6 +3142,11 @@ msgstr "" msgid "Global search" msgstr "" +#: templates/default/email_templates.tpl:9 +#: templates/default/email_template.tpl:12 +msgid "HTML content" +msgstr "" + #: templates/default/import.tpl:148 msgid "Hook: %(name)" msgstr "" @@ -3145,7 +3188,7 @@ msgstr "" msgid "Messages" msgstr "" -#: templates/default/viewSearch.tpl:113 +#: templates/default/viewSearch.tpl:128 msgid "Nb / page :" msgstr "" @@ -3153,6 +3196,10 @@ msgstr "" msgid "No access log found for this object." msgstr "" +#: templates/default/email_templates.tpl:23 +msgid "No existing email template." +msgstr "" + #: templates/default/LSform_view.tpl:34 msgid "No field." msgstr "" @@ -3165,6 +3212,20 @@ msgstr "" msgid "No value" msgstr "" +#: templates/default/email_template.tpl:30 +msgid "" +"No writable path to save your changes on the HTML message of this template." +msgstr "" + +#: templates/default/email_template.tpl:25 +msgid "No writable path to save your changes on the subject of this template." +msgstr "" + +#: templates/default/email_template.tpl:35 +msgid "" +"No writable path to save your changes on the text message of this template." +msgstr "" + #: templates/default/import.tpl:45 msgid "Object %(idx)" msgstr "" @@ -3246,14 +3307,29 @@ msgstr "" msgid "Right inherited from all connected users profile" msgstr "" +#: templates/default/email_template.tpl:40 +msgid "Save" +msgstr "" + #: templates/default/base_connected.tpl:6 msgid "Show/hide menu" msgstr "" -#: templates/default/LSmail.tpl:21 +#: templates/default/email_templates.tpl:8 +#: templates/default/email_template.tpl:9 +#: templates/default/email_template.tpl:22 templates/default/LSmail.tpl:21 msgid "Subject" msgstr "" +#: templates/default/email_templates.tpl:7 +msgid "Template name" +msgstr "" + +#: templates/default/email_templates.tpl:10 +#: templates/default/email_template.tpl:15 +msgid "Text content" +msgstr "" + #: templates/default/LSaccessRightsMatrixView.tpl:58 msgid "Their relations with other objects" msgstr "" diff --git a/src/templates/default/email_template.tpl b/src/templates/default/email_template.tpl new file mode 100644 index 00000000..c29f9370 --- /dev/null +++ b/src/templates/default/email_template.tpl @@ -0,0 +1,44 @@ +{extends file='ls:base_connected.tpl'} +{block name="content"} +

{$pagetitle}

+ +{include file='ls:LSview_actions.tpl'} + + + +
+
+ {if $tab == "subject"} + + + {elseif $tab == "html"} + + {else} + + {/if} + +
+
+{/block} diff --git a/src/templates/default/email_templates.tpl b/src/templates/default/email_templates.tpl new file mode 100644 index 00000000..86a40ef1 --- /dev/null +++ b/src/templates/default/email_templates.tpl @@ -0,0 +1,28 @@ +{extends file='ls:base_connected.tpl'} +{block name="content"} +

{$pagetitle}

+ + + + + + + + + + + {foreach $templates as $name => $tpl} + + + + + + + {foreachelse} + + + + {/foreach} + +
{tr msg="Template name"}{tr msg="Subject"}{tr msg="HTML content"}{tr msg="Text content"}
{$name}{$tpl.subject|escape:"htmlall"}{$tpl.html|escape:"htmlall"}{$tpl.txt|escape:"htmlall"}
{tr msg="No existing email template."}
+{/block}