Skip to content

Email Template Viewer

Flow ID: AD-53 | Module(s): settings | Complexity: Medium Last Updated: 2026-04-06

Business Context

The email template viewer provides administrators with a browser for previewing all system email templates. Located at /settings/email_views.htm, it renders each hard-coded template with synthetic Greek-market test data so admins can visually verify email layouts, translations, and content before they reach customers.

The viewer also exposes a subject line editor at /settings/email_views/editEmailSubjects.htm that allows per-language customization of email subject lines via the EMAIL_SUBJECTS registry group. This is the primary way admins change email subjects without touching code.

The viewer hard-codes 21 named template views (ecommercen/settings/controllers/AdvEmailViewer.php:120-142). The real runtime dispatcher Adv_mailer can render a slightly different set — the two lists are out of sync and the underlying template directories have diverged (see Two Mail Directories and Known Issues).


API Reference

REST Endpoints

No REST API. Email template viewing is an admin-only tool.

Legacy Admin Routes

RouteControllerMethodHTTPDescription
settings/email_viewsAdvEmailViewerindex()GETBrowse and preview all email templates
settings/email_views/editEmailSubjectsAdvEmailViewereditEmailSubjects()GET/POSTEdit email subject lines per language

Routes are HMVC auto-routed through the settings module — no entries in application/config/*routes*.php. The HMVC entry point is a thin subclass: application/modules/settings/controllers/Email_views.php (class Email_views extends AdvEmailViewer {}).


Code Flow

Template Preview (index)

AdvEmailViewer::index()                 (ecommercen/settings/controllers/AdvEmailViewer.php:24-155)
  |
  +--> initTestEmailViews()
  |    +--> Loads eshop/order_model, eshop/stores_model, encryption library
  |
  +--> Build $data test fixture (inline, no external fixtures):
  |    - Customer scalars (name, email, phone, address)
  |    - orderData array with 25+ keys incl. getCurrencyData(1)
  |    - store_data, transporter_name, products, loyalty_reward
  |    - customerData, token, encryption, giftCard,
  |      couponRules, giftCardCoupon
  |    - externalLang = new ExternalLang($this->language_abbr)
  |
  +--> $data['emailViews'] = new Template(
  |        new Config(APPPATH . '/config/emailViews.json')
  |    )
  |    (only for views' own component resolution — viewer does NOT
  |     use layouts/components to choose which templates to render)
  |
  +--> Hard-coded $email_views list (21 entries, L120-142):
  |    ask_us_email, contact_email, notify_admin, notify_admin_low_stock,
  |    notify_admin_new_product, order_created, order_on_store, order_update,
  |    product_review_accept, product_review_reject, reset_password,
  |    review_for_facebook, review_for_skroutz, review_for_google,
  |    waiting_list_success, remaining_points, birthday_wishes,
  |    gift_card, gift_card_inform_customer, blog_comment_accept,
  |    blog_comment_reject
  |
  +--> view_content = "{$classView}/email_views"
  +--> $this->template_view_admin(...)

The admin view (application/views/admin/settings/email_views.php) iterates $email_views and renders each via:

php
$this->load->view($this->client_views . '/mail/' . $email_view, $data, true)

Because application/config/app.php:17-19 still sets the deprecated $config['client_views'] = 'default', the viewer loads from application/views/default/mail/, not from the directory Adv_mailer actually uses at runtime (application/views/main/mail/). See Two Mail Directories.

Subject Editor (editEmailSubjects)

AdvEmailViewer::editEmailSubjects()     (ecommercen/settings/controllers/AdvEmailViewer.php:157-187)
  |
  +--> Seed empty defaults for contact form keys:
  |    $contactFormEmptyData = array_map(
  |        fn ($item) => '',
  |        array_flip((new FormsConfig())->getContactFormKeys())
  |    )
  |
  +--> On POST ($this->input->post('submit')):
  |    +--> For each admin language ($this->adminLanguages):
  |    |    +--> For each posted field whose name contains $langAbbr:
  |    |         +--> $postKey = rtrim($postKey, "_$langAbbr")   # BUG — see Known Issues
  |    |         +--> $this->registry->setValue('EMAIL_SUBJECTS', $postKey, $value, $langAbbr)
  |    +--> Flash success (t('eshop.admin.success.entryedit')), redirect
  |
  +--> On GET:
  |    +--> For each admin language:
  |    |    +--> $this->registry->getGroupAsArray('EMAIL_SUBJECTS', $langAbbr)
  |    |    +--> Merge with $contactFormEmptyData as defaults
  |    +--> Render admin/settings/email_subjects.php with
  |         inputs named "{key}_{langAbbr}"

Domain Layer

No modern domain layer. The email viewer is a pure legacy admin tool.


Templates Directory

Canonical templates live in application/views/main/mail/ (23 files). These are what Adv_mailer actually renders at email-send time.

FilePurpose
ask_us_email.phpLegacy "ask us" form — orphan, only referenced by the viewer's hard-coded list
birthday_wishes.phpBirthday greetings + optional loyalty points
blog_comment_accept.phpBlog comment moderation — approved notification
blog_comment_reject.phpBlog comment moderation — rejected notification
contact_email.phpDefault contact-form email
contact_email_generated.phpGeneric contact-form email via generated form config (fullSampleForm)
email_products_summary.phpShared partial included by order_created.php and notify_admin.php via componentView('emailProductsSummary')
gift_card.phpGift card delivery to recipient
gift_card_inform_customer.phpGift card purchase confirmation to buyer
notify_admin.phpAdmin order notification
notify_admin_low_stock.phpAdmin low-stock alert
notify_admin_new_product.phpAdmin new-product notification (method inform_new_product flagged @todo refactor ... BROKEN ATM at application/models/Adv_mailer.php:241)
order_created.phpCustomer order confirmation
order_on_store.php"Your order arrived at the store"
order_update.phpOrder status update (shipping / paid / invoiced)
product_review_accept.phpReview moderation — approved notification
product_review_reject.phpReview moderation — rejected notification
remaining_points.phpLoyalty "you have remaining points" reminder
reset_password.phpPassword reset with encrypted token
review_for_facebook.phpPost-purchase review prompt for Facebook
review_for_skroutz.phpPost-purchase review prompt for Skroutz
review_for_google.phpPost-purchase review prompt for Google
waiting_list_success.phpBack-in-stock notification

Two Mail Directories

There are two mail view directories, and they have drifted apart:

DirectoryFile CountReader
application/views/main/mail/23Adv_mailer (runtime sends) — driven by emailViews.json::templateFolder = "main/mail"
application/views/default/mail/17AdvEmailViewer (admin preview) — driven by deprecated $config['client_views'] = 'default' at application/config/app.php:17-19

Files missing from default/mail/

Present in main/mail/ but not in default/mail/ — the viewer tries to include these and CI view loading will fail for admins:

  • birthday_wishes
  • blog_comment_accept
  • blog_comment_reject
  • contact_email_generated (also missing from the viewer's $email_views list — see below)
  • gift_card_inform_customer
  • remaining_points

Orphan template

ask_us_email.php exists in both directories but is never dispatched at runtime. It has no layout entry in application/config/emailViews.json, no caller in Adv_mailer, and no EMAIL_SUBJECTS.ask_us_email registry key. It survives only because AdvEmailViewer::$email_views (hard-coded at ecommercen/settings/controllers/AdvEmailViewer.php:121) lists it for preview.

Broken reference: apologize_shipping_delay.php

  • Referenced in application/config/emailViews.json:50-52 (layout orderInformDelay → apologize_shipping_delay)
  • Consumed by Adv_mailer::sendCustomerInformDelay() at application/models/Adv_mailer.php:400
  • Called from application/models/Adv_order_model.php:2736 and the AdvSendEmailForDeliveryDelay job
  • The file does not exist in either main/mail/ or default/mail/. Calls fail at runtime when CI tries to load the view.
  • Not listed in AdvEmailViewer::$email_views, so the viewer never tries to render it — the bug is only visible at dispatch time.

Templates missing from the viewer's list

The viewer's hard-coded $email_views array is missing two layouts that emailViews.json knows about:

  • contact_email_generated (layout contactEmailGenerated)
  • apologize_shipping_delay (layout orderInformDelay) — and the file does not exist anyway

The partial email_products_summary is also absent from the list, correctly — it is a component included by other templates, not a standalone preview target.


Subject Registry Keys

Subject strings live in the registry table under the EMAIL_SUBJECTS group, keyed per admin language. Defaults are seeded from application/config/db_default_values.php:42-157 by database/seeders/InitialSeed.php::emailSubjects() at database/seeders/InitialSeed.php:8569-8580.

Registry KeyDefault (EN)Consumer in application/models/Adv_mailer.php
contactFormContact FormAdv_forms::contactForm() at ecommercen/forms/controllers/Adv_forms.php:62
fullSampleFormTest Contact FormAdv_forms::contactForm('fullSampleForm')
PASSWORD_RESETPassword Resetrecover_password_mail() L73
ORDER_COMPLETEOrder Completeorder_complete() L169
ORDER_UPDATEOrder Updateorder_has_been_updated() L208
WAITING_LISTAvailability UpdatesendEmailForWaitingList() L236
ORDER_ON_STOREYour order is at the storeorder_is_on_store() L316
USER_REVIEW_ACCEPTThe product review has been approvedsendUserReviewStatusUpdateEmail() L351
USER_REVIEW_REJECTThe product review has been rejectedsendUserReviewStatusUpdateEmail() L351
REVIEW_FOR_FACEBOOKTell us your opinion!sendCustomerPromptReview() L365
REVIEW_FOR_SKROUTZTell us your opinion!sendCustomerPromptReview() L368
REVIEW_FOR_GOOGLETell us your opinion!sendCustomerPromptReview() L371
CUSTOMER_INFORM_DELAYDelivery DelaysendCustomerInformDelay() L405
REMAINING_POINTSYou have remaining points!sentRemainingPoints() L453
BIRTHDAY_WISHESHappy Birthday!sentBirthdayWishes() L486
GIFT_CARDYou have received a gift card!sendGiftCardToEmailRenderer() L532, L542
GIFT_CARD_INFORM_CUSTOMERGift Card PurchasesendGiftCardToEmailRenderer() L531, L541
BLOG_COMMENT_ACCEPTBlog comment approvalsendBlogCommentStatusUpdateEmail() L562
BLOG_COMMENT_REJECTBlog comment rejectionsendBlogCommentStatusUpdateEmail() L562

The same group is also read in application/views/admin/contact_emails/list.php:89.

Templates with no matching registry key

These templates rely on a different subject source:

  • notify_admin, notify_admin_low_stock, notify_admin_new_productAdv_mailer::sendAdmin() falls back to getRegistryValue('TITLE', 'SITE', ...) at application/models/Adv_mailer.php:630, or uses $data['subject'] (set from the site.emails.inform_new_product.title lang line at L254).
  • ask_us_email — orphan, no subject.

Email Templates Engine

Template / Config

  • src/Template/Template.php (61 lines) turns layout keys into view paths via application/config/emailViews.json.
  • layoutView('orderCreated') returns "main/mail/order_created".
  • componentView('emailProductsSummary') returns "main/mail/email_products_summary".
  • Optional debug dumps to layout.txt / component.txt when APP_TEMPLATE_DEBUG=true.

emailViews.json structure

application/config/emailViews.json (77 lines) contains:

  • template — empty
  • templateFolder"main/mail" (read by Adv_mailer at runtime)
  • layouts — 22 named layouts mapping layoutName → { view: template_name }
  • components — one entry: emailProductsSummary → email_products_summary

Layout keys: contactEmail, contactEmailGenerated, resetPassword, orderCreated, orderOnStore, orderUpdate, productReviewAccept, productReviewReject, reviewForFacebook, reviewForSkroutz, reviewForGoogle, waitingListSuccess, notifyAdmin, notifyAdminLowStock, notifyAdminNewProduct, orderInformDelay, remainingPoints, birthdayWishes, giftCard, giftCardInformCustomer, blogCommentAccept, blogCommentReject.

Important: the viewer instantiates a Template and hands it to views via $data['emailViews'] only so templates can resolve their own components. The viewer chooses which templates to render via its hard-coded $email_views array and CI's $this->load->view($client_views . '/mail/' . $email_view, ...) — it never consults layouts.

Adv_mailer pipeline

application/models/Adv_mailer.php (715 lines, extends Adv_base_model) is the central legacy mailer. Its constructor at L22 instantiates a shared Advisable\Template\Template and Registry.

Public methods, one per email type:

  • recover_password_mail (L50)
  • order_complete (L85), which also triggers notify_admin at L175
  • order_has_been_updated (L182)
  • sendEmailForWaitingList (L222)
  • inform_new_product (L241, flagged BROKEN in a @todo comment)
  • order_with_low_stock
  • order_is_on_store (L295)
  • sendContactFormEmail (L321)
  • sendUserReviewStatusUpdateEmail (L336)
  • sendCustomerPromptReview (L355)
  • sendCustomerInformDelay (L394)
  • sendExpiringCertificate (L417, uses direct ExternalLang body — not template-based)
  • sentRemainingPoints (L453)
  • sentBirthdayWishes (L486)
  • sendGiftCardToInformCustomer / sendGiftCardToEmail (L490-ish)
  • sendBlogCommentStatusUpdateEmail (L548)

Private helpers:

  • send() L580 — rendering pipeline
  • sendAdmin() L621 — admin variant with subject fallback
  • doSend() L655 — builds a Mailer via new Mailer(), sets SMTP, sends
  • addEmailViewsToRender()
  • addEmailToCustomerHistory() L608 — logs the rendered email to eshop/customer_message_history_model::addRecord(customerId, emailType, emailSubject, emailMessage)

send() at L597 instantiates new ExternalLang($customerData->lang, $this->languageAbbr) so customer locale is preferred over admin fallback, then calls $this->load->view($pageToLoad, ..., true) to render the template to a string. Invalid mail addresses are short-circuited via $this->config->item('order.invalid.email') at L590.


PHPMailer Transport

For the full transport pipeline (validation, SMTP timeout, state reset, error handling, and doSend() internals), see SY-24 Email Dispatch — Code Flow. The summary below covers only what is relevant to configuring SMTP credentials for the email viewer context.

Mailer wrapper

ecommercen/core/Mailer.php wraps PHPMailer\PHPMailer:

  • setFrom(...)
  • addSendToAddresses(...) / addSendCCAddresses(...) / addSendBCCAddresses(...)
  • addAttachments(...)
  • setSmtp($host, $port, $user, $pass, $autoSslTls)
  • $subject, $message
  • send()

Default charset is utf-8, encoding base64.

SMTP credentials

Credentials are fetched via getDefaultSmtpCredentials($purpose) in ecommercen/helpers/registry_helper.php:30-80. Only two purposes are accepted; any other value throws:

Purpose constantUsed for
EMAIL_CONTACT_FORMContact-form dispatch (sendContactFormEmail)
EMAIL_SHOP_MAILEREverything else (orders, reviews, gift cards, etc.)

Each purpose resolves to a registry group with 8 required keys:

  • SMTP_HOST
  • SMTP_USER
  • SMTP_PASS (encrypted)
  • SMTP_PORT
  • SMTP_AUTO_SSL_TLS
  • SENDER_EMAIL
  • FROM_NAME
  • ADMIN_EMAIL

The admin UI for managing these lives in application/views/admin/settings/email_block.php and ecommercen/settings/controllers/Adv_settings.php:60-180.

There is no MailHog, Mailgun, or SendGrid integration — the platform sends pure SMTP.


Localization

  • ExternalLang (ecommercen/libraries/ExternalLang.php) is passed into every email template as $externalLang.
  • Admin viewer: new ExternalLang($this->language_abbr) (admin's UI language).
  • Real sends: new ExternalLang($customerData->lang, $this->languageAbbr) at application/models/Adv_mailer.php:597 — customer's locale takes precedence, admin language is the fallback.
  • Subject strings come from registry->value('EMAIL_SUBJECTS', $key, $customerData->lang) and are filled via vsprintf for placeholders.
  • Admin emails fall back to getRegistryValue('TITLE', 'SITE', $this->languageAbbr) or $data['subject'].
  • No MUI tables exist for email content — translations live in CI language files via ExternalLang and in registry subject entries.

Dispatch Points

Representative list of where each template is triggered. Line numbers refer to application/models/Adv_mailer.php unless stated otherwise.

TemplateMailer methodDispatched from
order_createdorder_complete() L8517 sites in ecommercen/checkout/controllers/Adv_checkout.php (L178, 222, 266, 343, 545, 745, 788, 853, 919, 1107, 1352, 1814, 2479, 2733, 2900, 3051, 3164, 3286); Adv_orders_admin.php:2289, 3394
notify_adminside-effect of order_complete() L175same as above
order_on_storeorder_is_on_store() L295Adv_orders_admin.php:371, AdvSendEmailAndSMSBasedOnFromStoreStatus.php:37, Cronjob.php:168
order_updateorder_has_been_updated() L182Adv_orders_admin.php:367, 905, AdvSendEmailBasedOnSentStatus.php:25, AdvSendEmailAndSMSBasedOnSentStatus.php:43, Cronjob.php:76, 113
reset_passwordrecover_password_mail() L50ecommercen/eshop/controllers/Adv_customer.php:455
waiting_list_successsendEmailForWaitingList() L222AdvSendEmailForWaitingList.php:40, Cronjob.php:221
product_review_accept / _rejectsendUserReviewStatusUpdateEmail() L336Adv_product_reviews_admin.php:101, 145
review_for_facebook / _skroutz / _googlesendCustomerPromptReview() L355Adv_order_model.php:1909
gift_card / gift_card_inform_customersendGiftCardToEmail() / sendGiftCardToInformCustomer() L490AdvSendGiftCardsToEmails.php:25, 27, AdvResendGiftCardEmail.php:21
blog_comment_accept / _rejectsendBlogCommentStatusUpdateEmail() L548Adv_blog_comments_admin.php:108
contact_email / contact_email_generatedsendContactFormEmail() L321ecommercen/forms/controllers/Adv_forms.php:60 (template chosen via FormsConfig::getEmailTemplateView())
apologize_shipping_delay (orderInformDelay)sendCustomerInformDelay() L394Adv_order_model.php:2736, AdvSendEmailForDeliveryDelay job — template file MISSING, calls fail
ask_us_email(none)orphan — never dispatched

Special case: sendExpiringCertificate() at L417 uses a direct ExternalLang body and not a template view. It is called from Cronjob.php:823 and AdvCheckCDNCertificate.php:95 and is therefore not part of the viewer.


Architecture

ComponentPathPurpose
AdvEmailViewerecommercen/settings/controllers/AdvEmailViewer.phpAdmin controller (188 lines)
Email_viewsapplication/modules/settings/controllers/Email_views.phpHMVC entry point, thin subclass of AdvEmailViewer
Adv_mailerapplication/models/Adv_mailer.phpRuntime mailer (715 lines, Adv_base_model)
Mailerecommercen/core/Mailer.phpPHPMailer wrapper (SMTP only)
Advisable\Template\Templatesrc/Template/Template.phpResolves layouts / components via emailViews.json
Advisable\Template\Configsrc/Template/Config.phpLoads template JSON config
Email views configapplication/config/emailViews.jsonLayout and component mappings (77 lines)
Default subject seedsapplication/config/db_default_values.php:42-157Baseline EMAIL_SUBJECTS values
Subject seederdatabase/seeders/InitialSeed.php:8569-8580Inserts defaults into registry
FormsConfigapplication/modules/forms/libraries/FormsConfig.phpEmpty subclass of AdvFormsConfig
AdvFormsConfigecommercen/forms/libraries/AdvFormsConfig.phpLoads application/config/contact_forms.php, exposes getContactFormKeys(), getContactFormKeysDropDown(), getEmailTemplateView(), etc. — not a list of email templates
ExternalLangecommercen/libraries/ExternalLang.phpPer-locale string resolver passed to every template
Admin menuapplication/config/admin_menu.php:934-947Menu entries gated on [AUTH_ROLE_ADVISABLE, AUTH_ROLE_ADMIN]
Viewer templateapplication/views/admin/settings/email_views.phpIterates $email_views and loads each
Subject editor viewapplication/views/admin/settings/email_subjects.phpRenders per-language subject inputs

Role of FormsConfig

FormsConfig is purely a contact-form configuration accessor. AdvEmailViewer::editEmailSubjects() uses it at L159 only to seed empty defaults:

php
$contactFormEmptyData = array_map(
    fn ($item) => '',
    array_flip((new FormsConfig())->getContactFormKeys())
);

It does not enumerate email templates.


Subject Editor Flow

AdvEmailViewer::editEmailSubjects() (ecommercen/settings/controllers/AdvEmailViewer.php:157-187):

  1. Seeds empty defaults for contact-form keys via FormsConfig::getContactFormKeys() (L159).
  2. On POST ($this->input->post('submit')):
    • For each admin language in $this->adminLanguages:
      • For each posted field whose name contains $langAbbr:
        • Strips the language suffix via rtrim($postKey, "_$langAbbr") — see Known Issues #5.
        • Calls $this->registry->setValue('EMAIL_SUBJECTS', $postKey, $postValue, $langAbbr).
    • Flashes t('eshop.admin.success.entryedit') and redirects.
  3. On GET:
    • For each admin language, loads $this->registry->getGroupAsArray('EMAIL_SUBJECTS', $langAbbr).
    • Merges with the empty contact-form defaults.
    • Renders admin/settings/email_subjects.php with inputs named {key}_{langAbbr}.

Data Model

No dedicated tables. Email subjects are stored in the registry table under the EMAIL_SUBJECTS group with per-language entries. See Subject Registry Keys for the full list.

Registry GroupKey PatternDescription
EMAIL_SUBJECTS{template_name_or_const}Subject line for each email template, per language
EMAIL_CONTACT_FORMSMTP credentials (8 keys)Transport for contact-form emails
EMAIL_SHOP_MAILERSMTP credentials (8 keys)Transport for all other emails

Configuration

  • Admin menu: application/config/admin_menu.php:934-947 — two entries (settings/email_views, settings/email_views/editEmailSubjects) gated on [AUTH_ROLE_ADVISABLE, AUTH_ROLE_ADMIN].
  • Template config: application/config/emailViews.json.
  • Email template file name: application/config/app.php:15 (emailTemplate = 'emailViews.json').
  • Client views setting: application/config/app.php:17-19 ($config['client_views'] = 'default', deprecated — source of the viewer/runtime divergence).
  • Role constants: application/config/constants.php:99, 101 (AUTH_ROLE_ADVISABLE = 1, AUTH_ROLE_ADMIN = 3).

Required roles: AUTH_ROLE_ADVISABLE and AUTH_ROLE_ADMIN. The constructor at AdvEmailViewer.php:9-12 calls allowRole([AUTH_ROLE_ADVISABLE, AUTH_ROLE_ADMIN], $this->sessionRoles)error_401() on mismatch. The admin menu enforces the same set. There is no separation between viewing and editing — both operations share one role gate.


Client Extension Points

  • Custom email templates: Client repos can add templates under application/views/main/mail/ and wire them into emailViews.json + Adv_mailer. Client repos should use custom/ for new modern code, but the legacy mail pipeline still lives under application/.
  • Override controller: Extend AdvEmailViewer via application/modules/settings/controllers/Email_views.php to add client-specific preview entries.
  • Additional subjects: Register client-specific subjects under the EMAIL_SUBJECTS registry group per language.

Business Rules

  1. Preview-only: The viewer renders emails with synthetic test data — it does not send actual emails.
  2. Per-language subjects: Subjects are stored per language in the registry, allowing full multi-language customization.
  3. Sample data: Test data is Greek-market (names, addresses) inline in initTestEmailViews().
  4. No in-browser template editing: The viewer shows rendered output only. Template HTML is edited in code, not through the admin UI.
  5. Customer locale wins: At dispatch, the customer's language is preferred over the admin's language for both subject and body.
  6. SMTP only: Mail is sent via PHPMailer over SMTP using one of two credential sets (EMAIL_CONTACT_FORM or EMAIL_SHOP_MAILER).

Known Issues & Security Gaps

  1. Missing template apologize_shipping_delay.php — referenced by application/config/emailViews.json:50-52, consumed by Adv_mailer::sendCustomerInformDelay() at application/models/Adv_mailer.php:400, called from application/models/Adv_order_model.php:2736 and the AdvSendEmailForDeliveryDelay job. File does not exist in either main/mail/ or default/mail/ — delivery-delay emails fail at CI view loading. Not visible in the viewer because it is absent from $email_views.
  2. Orphan template ask_us_email.php — present in both mail directories and in AdvEmailViewer.php:121, but has no layout in emailViews.json, no subject key in EMAIL_SUBJECTS, and no caller in Adv_mailer. Dead code preserved only for viewer rendering.
  3. Divergent viewer / runtime template directories — viewer reads application/views/default/mail/ (because of the deprecated client_views = 'default' at application/config/app.php:17-19) while Adv_mailer reads application/views/main/mail/ (templateFolder in emailViews.json). The two directories have drifted; 5 templates listed by the viewer (birthday_wishes, blog_comment_accept, blog_comment_reject, gift_card_inform_customer, remaining_points) will fail to load from default/. A 6th template that exists only in main/mail/contact_email_generated — is not even listed in the viewer's $email_views array, so the viewer never tries to render it (separate gap, see below). The 17 older files in default/mail/ are stale duplicates.
  4. Viewer $email_views list incompletecontact_email_generated (layout contactEmailGenerated) and apologize_shipping_delay (layout orderInformDelay) are not listed at AdvEmailViewer.php:120-142, so admins cannot preview them (and the latter is broken anyway).
  5. rtrim mask bug in subject editorAdvEmailViewer.php::editEmailSubjects() strips the language suffix with rtrim($postKey, "_$langAbbr"). rtrim's second argument is a character mask, not a literal suffix, so it will over-strip any trailing characters in the set. It happens to work for current keys but is fragile.
  6. inform_new_product flagged BROKENapplication/models/Adv_mailer.php:241 carries a @todo refactor ... BROKEN ATM comment, yet the method is still wired through notify_admin_new_product.

  • AD-13 Settings — general settings admin where SMTP credentials (EMAIL_CONTACT_FORM, EMAIL_SHOP_MAILER) are configured
  • AD-52 Review Moderation — dispatches product_review_accept / product_review_reject via sendUserReviewStatusUpdateEmail()
  • SY-11 Review Reminders — sends review_for_facebook / _skroutz / _google via sendCustomerPromptReview()
  • SY-24 Email Dispatch — the runtime pipeline (Adv_mailer + Mailer + PHPMailer) that delivers the templates previewed here
  • AD-03 Order Management Admin — order emails are the highest-volume dispatchers in the table above