diff --git a/src/email_templates/welcome.html b/src/email_templates/welcome.html
new file mode 100644
index 00000000..feddab87
--- /dev/null
+++ b/src/email_templates/welcome.html
@@ -0,0 +1,7 @@
+{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
new file mode 100644
index 00000000..44121bd3
--- /dev/null
+++ b/src/email_templates/welcome.subject
@@ -0,0 +1 @@
+Welcome {$name}!
diff --git a/src/includes/class/class.LSmail.php b/src/includes/class/class.LSmail.php
index fc5cf9bc..dfe64dd9 100644
--- a/src/includes/class/class.LSmail.php
+++ b/src/includes/class/class.LSmail.php
@@ -20,7 +20,20 @@
******************************************************************************/
-class LSmail {
+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
@@ -81,4 +94,287 @@ class LSmail {
}
}
+ /**
+ * 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')
+);