Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[5.2] Feat: Introduce Mail Template Layout #3227

Closed
jgerman-bot opened this issue Jul 27, 2024 · 0 comments · Fixed by #3228
Closed

[5.2] Feat: Introduce Mail Template Layout #3227

jgerman-bot opened this issue Jul 27, 2024 · 0 comments · Fixed by #3228

Comments

@jgerman-bot
Copy link

New language relevant PR in upstream repo: joomla/joomla-cms#43829 Here are the upstream changes:

Click to expand the diff!
diff --git a/administrator/components/com_mails/config.xml b/administrator/components/com_mails/config.xml
index 5b3b64a30a8b7..1f72f4994b347 100644
--- a/administrator/components/com_mails/config.xml
+++ b/administrator/components/com_mails/config.xml
@@ -4,7 +4,8 @@
 	<inlinehelp button="show"/>
 	<fieldset
 		name="mails_options"
-		label="COM_MAILS_CONFIG_MAIL_OPTIONS" >
+		label="COM_MAILS_CONFIG_MAIL_OPTIONS"
+		addfieldprefix="Joomla\Component\Mails\Administrator\Field">
 
 		<field
 			name="mail_style"
@@ -49,6 +50,37 @@
 			exclude="administrator|api|cache|cli|components|includes|language|layouts|libraries|modules|plugins|templates|tmp"
 		/>
 
+		<field
+			name="disable_htmllayout"
+			type="radio"
+			label="COM_CONFIG_FIELD_MAILTEMPLATE_LAYOUT_OFF_LABEL"
+			layout="joomla.form.field.radio.switcher"
+			default="1"
+			showon="mail_style:html[OR]mail_style:both"
+			>
+			<option value="0">JDISABLED</option>
+			<option value="1">JENABLED</option>
+		</field>
+
+		<field
+			name="mail_htmllayout"
+			type="mailtemplateLayout"
+			label="COM_CONFIG_FIELD_MAILTEMPLATE_LAYOUT_LABEL"
+			class="form-select"
+			showon="mail_style:html[OR]mail_style:both[AND]disable_htmllayout:1"
+		/>
+
+		<field
+			name="mail_logofile"
+			type="media"
+			label="COM_CONFIG_FIELD_MAILTEMPLATE_LOGOFILE_LABEL"
+			schemes="http,https,ftp,ftps,data,file"
+			validate="url"
+			relative="true"
+			default=""
+			showon="mail_style:html[OR]mail_style:both[AND]disable_htmllayout:1"
+		/>
+
 	</fieldset>
 
 	<fieldset
diff --git a/administrator/components/com_mails/forms/template.xml b/administrator/components/com_mails/forms/template.xml
index c7930835b435a..b327c536a656b 100644
--- a/administrator/components/com_mails/forms/template.xml
+++ b/administrator/components/com_mails/forms/template.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<form>
+<form addfieldprefix="Joomla\Component\Mails\Administrator\Field">
 	<fieldset>
 		<field
 			name="template_id"
@@ -205,6 +205,42 @@
 				default=""
 				filter="string"
 			/>
+
+			<field
+				name="disable_htmllayout"
+				type="list"
+				label="COM_MAILS_FIELD_HTML_LAYOUT_OFF_LABEL"
+				validate="options"
+				class="form-select-color-state"
+				useglobal="true"
+				>
+				<option value="0">JDISABLED</option>
+				<option value="1">JENABLED</option>
+			</field>
+
+			<field
+				name="htmllayout"
+				type="mailtemplateLayout"
+				label="COM_MAILS_FIELD_HTML_LAYOUT_LABEL"
+				class="form-select"
+				useglobal="true"
+				default=""
+				showon="disable_htmllayout!:0"
+			/>
+
+			<field
+				name="disable_logofile"
+				type="radio"
+				label="COM_MAILS_FIELD_HTML_LAYOUT_LOGO_OFF_LABEL"
+				layout="joomla.form.field.radio.switcher"
+				default="1"
+				showon="disable_htmllayout!:0[AND]htmllayout!:mailtemplate"
+				validate="options"
+				>
+				<option value="0">JDISABLED</option>
+				<option value="1">JENABLED</option>
+			</field>
+
 		</fieldset>
 	</fields>
 </form>
diff --git a/administrator/components/com_mails/src/Field/MailtemplateLayoutField.php b/administrator/components/com_mails/src/Field/MailtemplateLayoutField.php
new file mode 100644
index 0000000000000..3ed4897764cb0
--- /dev/null
+++ b/administrator/components/com_mails/src/Field/MailtemplateLayoutField.php
@@ -0,0 +1,131 @@
+<?php
+
+/**
+ * @package     Joomla.Administrator
+ * @subpackage  com_mails
+ *
+ * @copyright   (C) 2024 Open Source Matters, Inc. <https://www.joomla.org>
+ * @license     GNU General Public License version 2 or later; see LICENSE.txt
+ */
+
+namespace Joomla\Component\Mails\Administrator\Field;
+
+use Joomla\CMS\Factory;
+use Joomla\CMS\Form\FormField;
+use Joomla\CMS\HTML\HTMLHelper;
+use Joomla\CMS\Language\Text;
+use Joomla\Filesystem\Folder;
+use Joomla\Filesystem\Path;
+
+// phpcs:disable PSR1.Files.SideEffects
+\defined('_JEXEC') or die;
+// phpcs:enable PSR1.Files.SideEffects
+
+/**
+ * Form Field to display a list of the layouts for a field from
+ * the extension or template overrides.
+ *
+ * @since  __DEPLOY_VERSION__
+ */
+class MailtemplateLayoutField extends FormField
+{
+    /**
+     * The form field type.
+     *
+     * @var    string
+     * @since  __DEPLOY_VERSION__
+     */
+    protected $type = 'MailtemplateLayout';
+
+    /**
+     * Method to get the field input for a field layout field.
+     *
+     * @return  string   The field input.
+     *
+     * @since   __DEPLOY_VERSION__
+     */
+    protected function getInput()
+    {
+        $lang = Factory::getApplication()->getLanguage();
+
+        // Get the database object and a new query object.
+        $db    = $this->getDatabase();
+        $query = $db->getQuery(true);
+
+        // Build the query.
+        $query->select('element, name')
+            ->from('#__extensions')
+            ->where($db->quoteName('client_id') . ' = 0')
+            ->where($db->quoteName('type') . ' = ' . $db->quote('template'))
+            ->where($db->quoteName('enabled') . ' = 1');
+
+        // Set the query and load the templates.
+        $db->setQuery($query);
+        $templates = $db->loadObjectList('element');
+
+        // Prepare the grouped list
+        $groups = [];
+
+        // Add "Use Default"
+        $groups[]['items'][] = HTMLHelper::_('select.option', 'mailtemplate', Text::_('JOPTION_USE_DEFAULT'));
+
+        // Add a Use Global option if useglobal="true" in XML file
+        if ((string) $this->element['useglobal'] === 'true') {
+            $groups[Text::_('JOPTION_FROM_STANDARD')]['items'][] = HTMLHelper::_('select.option', '', Text::_('JGLOBAL_USE_GLOBAL'));
+        }
+
+        // Loop on all templates
+        if ($templates) {
+            foreach ($templates as $template) {
+                $files          = [];
+                $template_paths = [
+                    Path::clean(JPATH_SITE . '/templates/' . $template->element . '/html/layouts/joomla/mail'),
+                    Path::clean(JPATH_SITE . '/templates/' . $template->element . '/html/layouts/com_mails/joomla/mail'),
+                ];
+
+                // Add the layout options from the template paths.
+                foreach ($template_paths as $template_path) {
+                    if (is_dir($template_path)) {
+                        $files = array_merge($files, Folder::files($template_path, '^[^_]*\.php$', false, true));
+                    }
+                }
+
+                if (\count($files)) {
+                    // Create the group for the template
+                    $groups[$template->name]          = [];
+                    $groups[$template->name]['id']    = $this->id . '_' . $template->element;
+                    $groups[$template->name]['text']  = Text::sprintf('JOPTION_FROM_TEMPLATE', $template->name);
+                    $groups[$template->name]['items'] = [];
+
+                    foreach ($files as $file) {
+                        // Add an option to the template group
+                        $value = basename($file, '.php');
+                        $text  = $lang->hasKey($key = strtoupper('TPL_' . $template->element . '_MAILTEMPLATE_LAYOUT_' . $value))
+                                    ? Text::_($key) : $value;
+                        $groups[$template->name]['items'][] = HTMLHelper::_('select.option', $template->element . ':' . $value, $text);
+                    }
+                }
+            }
+        }
+
+        // Compute attributes for the grouped list
+        $attr = $this->element['size'] ? ' size="' . (int) $this->element['size'] . '"' : '';
+        $attr .= $this->element['class'] ? ' class="' . (string) $this->element['class'] . '"' : '';
+
+        // Prepare HTML code
+        $html = [];
+
+        // Compute the current selected values
+        $selected = [$this->value];
+
+        // Add a grouped list
+        $html[] = HTMLHelper::_(
+            'select.groupedlist',
+            $groups,
+            $this->name,
+            ['id' => $this->id, 'group.id' => 'id', 'list.attr' => $attr, 'list.select' => $selected]
+        );
+
+        return implode($html);
+    }
+}
diff --git a/administrator/components/com_mails/src/Model/TemplateModel.php b/administrator/components/com_mails/src/Model/TemplateModel.php
index 512edd3829f53..c6849b31a4de3 100644
--- a/administrator/components/com_mails/src/Model/TemplateModel.php
+++ b/administrator/components/com_mails/src/Model/TemplateModel.php
@@ -86,6 +86,9 @@ public function getForm($data = [], $loadData = true)
 
         if ($params->get('mail_style', 'plaintext') == 'plaintext') {
             $form->removeField('htmlbody');
+            $form->removeField('disable_htmllayout', 'params');
+            $form->removeField('htmllayout', 'params');
+            $form->removeField('disable_logofile', 'params');
         }
 
         if ($params->get('mail_style', 'plaintext') == 'html') {
@@ -106,6 +109,9 @@ public function getForm($data = [], $loadData = true)
             $form->removeField('smtpauth', 'params');
             $form->removeField('smtpuser', 'params');
             $form->removeField('smtppass', 'params');
+            $form->removeField('disable_htmllayout', 'params');
+            $form->removeField('htmllayout', 'params');
+            $form->removeField('disable_logofile', 'params');
         }
 
         if (!$params->get('copy_mails')) {
diff --git a/administrator/language/en-GB/com_config.ini b/administrator/language/en-GB/com_config.ini
index 5e3b1d1c1fc07..d896ccdc803d1 100644
--- a/administrator/language/en-GB/com_config.ini
+++ b/administrator/language/en-GB/com_config.ini
@@ -131,6 +131,9 @@ COM_CONFIG_FIELD_MAIL_SMTP_PASSWORD_LABEL="SMTP Password"
 COM_CONFIG_FIELD_MAIL_SMTP_PORT_LABEL="SMTP Port"
 COM_CONFIG_FIELD_MAIL_SMTP_SECURE_LABEL="SMTP Security"
 COM_CONFIG_FIELD_MAIL_SMTP_USERNAME_LABEL="SMTP Username"
+COM_CONFIG_FIELD_MAILTEMPLATE_LAYOUT_LABEL="Layout"
+COM_CONFIG_FIELD_MAILTEMPLATE_LAYOUT_OFF_LABEL="Mail Template Layout"
+COM_CONFIG_FIELD_MAILTEMPLATE_LOGOFILE_LABEL="Logo File"
 COM_CONFIG_FIELD_MEMCACHE_COMPRESSION_LABEL="Memcache(d) Compression"
 COM_CONFIG_FIELD_MEMCACHE_HOST_LABEL="Memcache(d) Server Host"
 COM_CONFIG_FIELD_MEMCACHE_PERSISTENT_LABEL="Persistent Memcache(d)"
diff --git a/administrator/language/en-GB/com_mails.ini b/administrator/language/en-GB/com_mails.ini
index 07c50249a56a4..01388cfdffc7b 100644
--- a/administrator/language/en-GB/com_mails.ini
+++ b/administrator/language/en-GB/com_mails.ini
@@ -18,6 +18,9 @@ COM_MAILS_FIELD_BODY_LABEL="Body"
 COM_MAILS_FIELD_FILE_LABEL="File"
 COM_MAILS_FIELD_FILENAME_LABEL="File Name"
 COM_MAILS_FIELD_HTMLBODY_LABEL="HTML Body"
+COM_MAILS_FIELD_HTML_LAYOUT_LABEL="Layout"
+COM_MAILS_FIELD_HTML_LAYOUT_OFF_LABEL="Mail Template Layout"
+COM_MAILS_FIELD_HTML_LAYOUT_LOGO_OFF_LABEL="Logo File"
 COM_MAILS_FIELD_LANGUAGE_CODE_INVALID="Invalid Language Code"
 COM_MAILS_FIELD_MAIL_COPY_MAIL_LABEL="Send Copy To Email"
 COM_MAILS_FIELD_MAIL_FROM_EMAIL_LABEL="From Email"
diff --git a/layouts/joomla/mail/mailtemplate.php b/layouts/joomla/mail/mailtemplate.php
new file mode 100644
index 0000000000000..209b0ad26f570
--- /dev/null
+++ b/layouts/joomla/mail/mailtemplate.php
@@ -0,0 +1,113 @@
+<?php
+
+/**
+ * @package     Joomla.Site
+ * @subpackage  Layout
+ *
+ * @copyright   (C) 2024 Open Source Matters, Inc. <https://www.joomla.org>
+ * @license     GNU General Public License version 2 or later; see LICENSE.txt
+ */
+
+use Joomla\CMS\Uri\Uri;
+
+defined('_JEXEC') or die;
+
+// Check if we have all the data
+if (!array_key_exists('mail', $displayData)) {
+    return;
+}
+
+// Setting up for display
+$mailBody = $displayData['mail'];
+
+if (!$mailBody) {
+    return;
+}
+
+$extraData = [];
+
+if (array_key_exists('extra', $displayData)) {
+    $extraData = $displayData['extra'];
+}
+
+$siteUrl = Uri::root(false);
+
+?>
+<!DOCTYPE html>
+<html lang="<?php echo (isset($extraData['lang'])) ?  $extraData['lang'] : 'en' ?>" xmlns="http://www.w3.org/1999/xhtml" xmlns:o="urn:schemas-microsoft-com:office:office">
+    <head>
+        <meta charset="utf-8">
+        <meta name="viewport" content="width=device-width,initial-scale=1">
+        <meta name="x-apple-disable-message-reformatting">
+        <!--[if !mso]><!-->
+        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+        <!--<![endif]-->
+        <title></title>
+        <!--[if mso]>
+            <style>
+                table {border-collapse:collapse;border-spacing:0;border:none;margin:0;}
+                div, td {padding:0;}
+                div {margin:0 !important;}
+                </style>
+            <noscript>
+                <xml>
+                <o:OfficeDocumentSettings>
+                    <o:PixelsPerInch>96</o:PixelsPerInch>
+                </o:OfficeDocumentSettings>
+                </xml>
+            </noscript>
+            <![endif]-->
+        <style>
+            html {height: 100%;}
+            table, td, div, h1, p { font-family: Arial, sans-serif; }
+        </style>
+    </head>
+    <body style="margin:0;padding:0;word-spacing:normal;background-color:#00000008;height:100%;">
+        <div role="article" aria-roledescription="email" style="text-size-adjust:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;background-color:#00000008;height:100%;">
+            <table role="presentation" style="width:100%;border:none;border-spacing:0;height:100%;">
+                <tr>
+                    <td align="center" style="vertical-align:baseline; padding:30px 0">
+                        <!--[if mso]>
+                        <table role="presentation" align="center" style="width:630px;">
+                        <tr>
+                        <td>
+                        <![endif]-->
+                        <table role="presentation" style="width:94%;max-width:630px;border:none;border-spacing:0;text-align:left;font-family:Arial,sans-serif;font-size:16px;line-height:22px;color:#363636;">
+                            <tr>
+                                <td style="padding:40px 30px 0 30px;text-align:center;font-size:24px;font-weight:bold;background-color:#ffffff;">
+                                <?php if (isset($extraData['logo']) || isset($extraData['siteName'])) : ?>
+                                    <?php if (isset($extraData['logo'])) : ?>
+                                    <img src="cid:<?php echo htmlspecialchars($extraData['logo'], ENT_QUOTES);?>" alt="<?php echo (isset($extraData['siteName']) ? $extraData['siteName'] . ' ' : '');?>Logo" style="max-width:80%;height:auto;border:none;text-decoration:none;color:#ffffff;">
+                                    <?php else : ?>
+                                    <h1 style="margin-top:0;margin-bottom:0;font-size:26px;line-height:32px;font-weight:bold;letter-spacing:-0.02em;color:#112855;">
+                                        <?php echo $extraData['siteName']; ?>
+                                    </h1>
+                                    <?php endif; ?>
+                                    <div style="padding: 30px 0 0;"></div>
+                                    <div style="padding:.75px;background-color:#0000000f;"></div>
+                                </td>
+                            </tr>
+                            <tr>
+                                <td style="padding:30px;background-color:#ffffff;">
+                                <?php endif; ?>
+                                    <?php echo $mailBody; ?>
+                                </td>
+                            </tr>
+                            <tr>
+                                <td style="padding:30px;text-align:center;font-size:12px;background-color:#112855;color:#cccccc;">
+                                    <p style="margin:0;font-size:14px;line-height:20px;">&copy; <?php echo isset($extraData['siteName']) ? $extraData['siteName'] . ' ' : ''; ?><?php echo date("Y"); ?>
+                                    <br><a title="<?php echo $siteUrl;?>" href="<?php echo $siteUrl; ?>" style="color:#cccccc;text-decoration:underline;"><?php echo $siteUrl; ?></a>
+                                </td>
+                            </tr>
+                        </table>
+                        <!--[if mso]>
+                        </td>
+                        </tr>
+                        </table>
+                        <![endif]-->
+                    </td>
+                </tr>
+            </table>
+        </div>
+    </body>
+</html>
diff --git a/libraries/src/Mail/MailTemplate.php b/libraries/src/Mail/MailTemplate.php
index 710db97b95c50..411dc01881cb5 100644
--- a/libraries/src/Mail/MailTemplate.php
+++ b/libraries/src/Mail/MailTemplate.php
@@ -11,7 +11,9 @@
 
 use Joomla\CMS\Component\ComponentHelper;
 use Joomla\CMS\Factory;
+use Joomla\CMS\HTML\HTMLHelper;
 use Joomla\CMS\Language\Text;
+use Joomla\CMS\Layout\FileLayout;
 use Joomla\CMS\Mail\Exception\MailDisabledException;
 use Joomla\Database\ParameterType;
 use Joomla\Filesystem\File;
@@ -90,6 +92,14 @@ class MailTemplate
      */
     protected $replyto;
 
+    /**
+     * Layout mailtemplate options of the email
+     *
+     * @var    string[]
+     * @since  __DEPLOY_VERSION__
+     */
+    protected $layoutTemplateData = [];
+
     /**
      * Constructor for the mail templating class
      *
@@ -167,6 +177,20 @@ public function setReplyTo($mail, $name = '')
         $this->replyto = $reply;
     }
 
+    /**
+     * Add data to the html layout template
+     *
+     * @param   array  $data  Associative array of strings for the layout
+     *
+     * @return  void
+     *
+     * @since   __DEPLOY_VERSION__
+     */
+    public function addLayoutTemplateData($data)
+    {
+        $this->layoutTemplateData = array_merge($this->layoutTemplateData, $data);
+    }
+
     /**
      * Add data to replace in the template
      *
@@ -239,16 +263,23 @@ public function send()
             $replyToName = $params->get('replytoname', $replyToName);
         }
 
+        $useLayout   = $config->get('disable_htmllayout', '1');
+
+        if ((int) $config->get('alternative_mailconfig', 0) === 1) {
+            $useLayout   = $params->get('disable_htmllayout', $useLayout);
+        }
+
         $app->triggerEvent('onMailBeforeRendering', [$this->template_id, &$this]);
 
         $subject = $this->replaceTags(Text::_($mail->subject), $this->data);
         $this->mailer->setSubject($subject);
 
         $mailStyle = $config->get('mail_style', 'plaintext');
+
         // Use the plain-text replacement data, if specified.
         $plainData = $this->plain_data ?: $this->data;
         $plainBody = $this->replaceTags(Text::_($mail->body), $plainData);
-        $htmlBody  = $this->replaceTags(Text::_($mail->htmlbody), $this->data);
+        $htmlBody  = $useLayout ? Text::_($mail->htmlbody) : $this->replaceTags(Text::_($mail->htmlbody), $this->data);
 
         if ($mailStyle === 'plaintext' || $mailStyle === 'both') {
             // If the Plain template is empty try to convert the HTML template to a Plain text
@@ -274,6 +305,57 @@ public function send()
 
             $htmlBody = MailHelper::convertRelativeToAbsoluteUrls($htmlBody);
 
+            if ($useLayout) {
+                // Add additional data to the layout template
+                $this->addLayoutTemplateData([
+                    'siteName' => $app->get('sitename'),
+                    'lang'     => substr($this->language, 0, 2),
+                ]);
+
+                $layout = $config->get('mail_htmllayout', 'mailtemplate');
+                $logo   = (string) $config->get('mail_logofile', '');
+
+                // Check alternative mailconfig
+                if ((int) $config->get('alternative_mailconfig', 0) === 1) {
+                    $layout = $params->get('htmllayout', $layout);
+                    $logo   = $params->get('disable_logofile', 1) ? $logo : '' ;
+                }
+
+                // Add the logo to the mail as inline attachement
+                if ($logo) {
+                    $logo = Path::check(JPATH_ROOT . '/' . HTMLHelper::_('cleanImageURL', $logo)->url);
+                    if (is_file(urldecode($logo))) {
+                        # Attach the logo as inline attachement
+                        $this->mailer->addAttachment($logo, 'site-logo', 'base64', mime_content_type($logo), 'inline');
+
+                        // We need only the cid for attached logo file
+                        $this->addLayoutTemplateData(['logo' => 'site-logo']);
+                    }
+                }
+
+                // Check if layout is a template override
+                $layoutParts = explode(':', $layout);
+
+                if (\count($layoutParts) === 2) {
+                    $layout = $layoutParts[1];
+                }
+
+                // Wrap the default Joomla mail template around the HTML body
+                $layoutFile = new FileLayout('joomla.mail.' . $layout, null, ['client' => 'site']);
+
+                // Set the template layout path if needed
+                if (\count($layoutParts) === 2) {
+                    $layoutFile->addIncludePaths([
+                        JPATH_SITE . '/templates/' . $layoutParts[0] . '/html/layouts',
+                        JPATH_SITE . '/templates/' . $layoutParts[0] . '/html/layouts/com_mails',
+                    ]);
+                }
+
+                $htmlBody = $layoutFile->render(['mail' => $htmlBody, 'extra' => $this->layoutTemplateData], null);
+
+                $htmlBody = $this->replaceTags(Text::_($htmlBody), $this->data);
+            }
+
             $this->mailer->setBody($htmlBody);
         }
 
tecpromotion added a commit to tecpromotion/joomla that referenced this issue Jul 27, 2024
add translation
@tecpromotion tecpromotion linked a pull request Jul 27, 2024 that will close this issue
zero-24 pushed a commit that referenced this issue Jul 27, 2024
* add 6 new strings

* fix #3227

add translation
@zero-24 zero-24 closed this as completed Jul 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging a pull request may close this issue.

4 participants