Appearance
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
| Route | Controller | Method | HTTP | Description |
|---|---|---|---|---|
settings/email_views | AdvEmailViewer | index() | GET | Browse and preview all email templates |
settings/email_views/editEmailSubjects | AdvEmailViewer | editEmailSubjects() | GET/POST | Edit 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.
| File | Purpose |
|---|---|
ask_us_email.php | Legacy "ask us" form — orphan, only referenced by the viewer's hard-coded list |
birthday_wishes.php | Birthday greetings + optional loyalty points |
blog_comment_accept.php | Blog comment moderation — approved notification |
blog_comment_reject.php | Blog comment moderation — rejected notification |
contact_email.php | Default contact-form email |
contact_email_generated.php | Generic contact-form email via generated form config (fullSampleForm) |
email_products_summary.php | Shared partial included by order_created.php and notify_admin.php via componentView('emailProductsSummary') |
gift_card.php | Gift card delivery to recipient |
gift_card_inform_customer.php | Gift card purchase confirmation to buyer |
notify_admin.php | Admin order notification |
notify_admin_low_stock.php | Admin low-stock alert |
notify_admin_new_product.php | Admin new-product notification (method inform_new_product flagged @todo refactor ... BROKEN ATM at application/models/Adv_mailer.php:241) |
order_created.php | Customer order confirmation |
order_on_store.php | "Your order arrived at the store" |
order_update.php | Order status update (shipping / paid / invoiced) |
product_review_accept.php | Review moderation — approved notification |
product_review_reject.php | Review moderation — rejected notification |
remaining_points.php | Loyalty "you have remaining points" reminder |
reset_password.php | Password reset with encrypted token |
review_for_facebook.php | Post-purchase review prompt for Facebook |
review_for_skroutz.php | Post-purchase review prompt for Skroutz |
review_for_google.php | Post-purchase review prompt for Google |
waiting_list_success.php | Back-in-stock notification |
Two Mail Directories
There are two mail view directories, and they have drifted apart:
| Directory | File Count | Reader |
|---|---|---|
application/views/main/mail/ | 23 | Adv_mailer (runtime sends) — driven by emailViews.json::templateFolder = "main/mail" |
application/views/default/mail/ | 17 | AdvEmailViewer (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_wishesblog_comment_acceptblog_comment_rejectcontact_email_generated(also missing from the viewer's$email_viewslist — see below)gift_card_inform_customerremaining_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(layoutorderInformDelay → apologize_shipping_delay) - Consumed by
Adv_mailer::sendCustomerInformDelay()atapplication/models/Adv_mailer.php:400 - Called from
application/models/Adv_order_model.php:2736and theAdvSendEmailForDeliveryDelayjob - The file does not exist in either
main/mail/ordefault/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(layoutcontactEmailGenerated)apologize_shipping_delay(layoutorderInformDelay) — 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 Key | Default (EN) | Consumer in application/models/Adv_mailer.php |
|---|---|---|
contactForm | Contact Form | Adv_forms::contactForm() at ecommercen/forms/controllers/Adv_forms.php:62 |
fullSampleForm | Test Contact Form | Adv_forms::contactForm('fullSampleForm') |
PASSWORD_RESET | Password Reset | recover_password_mail() L73 |
ORDER_COMPLETE | Order Complete | order_complete() L169 |
ORDER_UPDATE | Order Update | order_has_been_updated() L208 |
WAITING_LIST | Availability Update | sendEmailForWaitingList() L236 |
ORDER_ON_STORE | Your order is at the store | order_is_on_store() L316 |
USER_REVIEW_ACCEPT | The product review has been approved | sendUserReviewStatusUpdateEmail() L351 |
USER_REVIEW_REJECT | The product review has been rejected | sendUserReviewStatusUpdateEmail() L351 |
REVIEW_FOR_FACEBOOK | Tell us your opinion! | sendCustomerPromptReview() L365 |
REVIEW_FOR_SKROUTZ | Tell us your opinion! | sendCustomerPromptReview() L368 |
REVIEW_FOR_GOOGLE | Tell us your opinion! | sendCustomerPromptReview() L371 |
CUSTOMER_INFORM_DELAY | Delivery Delay | sendCustomerInformDelay() L405 |
REMAINING_POINTS | You have remaining points! | sentRemainingPoints() L453 |
BIRTHDAY_WISHES | Happy Birthday! | sentBirthdayWishes() L486 |
GIFT_CARD | You have received a gift card! | sendGiftCardToEmailRenderer() L532, L542 |
GIFT_CARD_INFORM_CUSTOMER | Gift Card Purchase | sendGiftCardToEmailRenderer() L531, L541 |
BLOG_COMMENT_ACCEPT | Blog comment approval | sendBlogCommentStatusUpdateEmail() L562 |
BLOG_COMMENT_REJECT | Blog comment rejection | sendBlogCommentStatusUpdateEmail() 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_product—Adv_mailer::sendAdmin()falls back togetRegistryValue('TITLE', 'SITE', ...)atapplication/models/Adv_mailer.php:630, or uses$data['subject'](set from thesite.emails.inform_new_product.titlelang 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 viaapplication/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.txtwhenAPP_TEMPLATE_DEBUG=true.
emailViews.json structure
application/config/emailViews.json (77 lines) contains:
template— emptytemplateFolder—"main/mail"(read byAdv_mailerat runtime)layouts— 22 named layouts mappinglayoutName → { 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 triggersnotify_adminat L175order_has_been_updated(L182)sendEmailForWaitingList(L222)inform_new_product(L241, flagged BROKEN in a@todocomment)order_with_low_stockorder_is_on_store(L295)sendContactFormEmail(L321)sendUserReviewStatusUpdateEmail(L336)sendCustomerPromptReview(L355)sendCustomerInformDelay(L394)sendExpiringCertificate(L417, uses directExternalLangbody — not template-based)sentRemainingPoints(L453)sentBirthdayWishes(L486)sendGiftCardToInformCustomer/sendGiftCardToEmail(L490-ish)sendBlogCommentStatusUpdateEmail(L548)
Private helpers:
send()L580 — rendering pipelinesendAdmin()L621 — admin variant with subject fallbackdoSend()L655 — builds aMailervianew Mailer(), sets SMTP, sendsaddEmailViewsToRender()addEmailToCustomerHistory()L608 — logs the rendered email toeshop/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,$messagesend()
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 constant | Used for |
|---|---|
EMAIL_CONTACT_FORM | Contact-form dispatch (sendContactFormEmail) |
EMAIL_SHOP_MAILER | Everything else (orders, reviews, gift cards, etc.) |
Each purpose resolves to a registry group with 8 required keys:
SMTP_HOSTSMTP_USERSMTP_PASS(encrypted)SMTP_PORTSMTP_AUTO_SSL_TLSSENDER_EMAILFROM_NAMEADMIN_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)atapplication/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 viavsprintffor 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
ExternalLangand 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.
| Template | Mailer method | Dispatched from |
|---|---|---|
order_created | order_complete() L85 | 17 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_admin | side-effect of order_complete() L175 | same as above |
order_on_store | order_is_on_store() L295 | Adv_orders_admin.php:371, AdvSendEmailAndSMSBasedOnFromStoreStatus.php:37, Cronjob.php:168 |
order_update | order_has_been_updated() L182 | Adv_orders_admin.php:367, 905, AdvSendEmailBasedOnSentStatus.php:25, AdvSendEmailAndSMSBasedOnSentStatus.php:43, Cronjob.php:76, 113 |
reset_password | recover_password_mail() L50 | ecommercen/eshop/controllers/Adv_customer.php:455 |
waiting_list_success | sendEmailForWaitingList() L222 | AdvSendEmailForWaitingList.php:40, Cronjob.php:221 |
product_review_accept / _reject | sendUserReviewStatusUpdateEmail() L336 | Adv_product_reviews_admin.php:101, 145 |
review_for_facebook / _skroutz / _google | sendCustomerPromptReview() L355 | Adv_order_model.php:1909 |
gift_card / gift_card_inform_customer | sendGiftCardToEmail() / sendGiftCardToInformCustomer() L490 | AdvSendGiftCardsToEmails.php:25, 27, AdvResendGiftCardEmail.php:21 |
blog_comment_accept / _reject | sendBlogCommentStatusUpdateEmail() L548 | Adv_blog_comments_admin.php:108 |
contact_email / contact_email_generated | sendContactFormEmail() L321 | ecommercen/forms/controllers/Adv_forms.php:60 (template chosen via FormsConfig::getEmailTemplateView()) |
apologize_shipping_delay (orderInformDelay) | sendCustomerInformDelay() L394 | Adv_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
| Component | Path | Purpose |
|---|---|---|
AdvEmailViewer | ecommercen/settings/controllers/AdvEmailViewer.php | Admin controller (188 lines) |
Email_views | application/modules/settings/controllers/Email_views.php | HMVC entry point, thin subclass of AdvEmailViewer |
Adv_mailer | application/models/Adv_mailer.php | Runtime mailer (715 lines, Adv_base_model) |
Mailer | ecommercen/core/Mailer.php | PHPMailer wrapper (SMTP only) |
Advisable\Template\Template | src/Template/Template.php | Resolves layouts / components via emailViews.json |
Advisable\Template\Config | src/Template/Config.php | Loads template JSON config |
| Email views config | application/config/emailViews.json | Layout and component mappings (77 lines) |
| Default subject seeds | application/config/db_default_values.php:42-157 | Baseline EMAIL_SUBJECTS values |
| Subject seeder | database/seeders/InitialSeed.php:8569-8580 | Inserts defaults into registry |
FormsConfig | application/modules/forms/libraries/FormsConfig.php | Empty subclass of AdvFormsConfig |
AdvFormsConfig | ecommercen/forms/libraries/AdvFormsConfig.php | Loads application/config/contact_forms.php, exposes getContactFormKeys(), getContactFormKeysDropDown(), getEmailTemplateView(), etc. — not a list of email templates |
ExternalLang | ecommercen/libraries/ExternalLang.php | Per-locale string resolver passed to every template |
| Admin menu | application/config/admin_menu.php:934-947 | Menu entries gated on [AUTH_ROLE_ADVISABLE, AUTH_ROLE_ADMIN] |
| Viewer template | application/views/admin/settings/email_views.php | Iterates $email_views and loads each |
| Subject editor view | application/views/admin/settings/email_subjects.php | Renders 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):
- Seeds empty defaults for contact-form keys via
FormsConfig::getContactFormKeys()(L159). - 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).
- Strips the language suffix via
- For each posted field whose name contains
- Flashes
t('eshop.admin.success.entryedit')and redirects.
- For each admin language in
- 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.phpwith inputs named{key}_{langAbbr}.
- For each admin language, loads
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 Group | Key Pattern | Description |
|---|---|---|
EMAIL_SUBJECTS | {template_name_or_const} | Subject line for each email template, per language |
EMAIL_CONTACT_FORM | SMTP credentials (8 keys) | Transport for contact-form emails |
EMAIL_SHOP_MAILER | SMTP 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 intoemailViews.json+Adv_mailer. Client repos should usecustom/for new modern code, but the legacy mail pipeline still lives underapplication/. - Override controller: Extend
AdvEmailViewerviaapplication/modules/settings/controllers/Email_views.phpto add client-specific preview entries. - Additional subjects: Register client-specific subjects under the
EMAIL_SUBJECTSregistry group per language.
Business Rules
- Preview-only: The viewer renders emails with synthetic test data — it does not send actual emails.
- Per-language subjects: Subjects are stored per language in the registry, allowing full multi-language customization.
- Sample data: Test data is Greek-market (names, addresses) inline in
initTestEmailViews(). - No in-browser template editing: The viewer shows rendered output only. Template HTML is edited in code, not through the admin UI.
- Customer locale wins: At dispatch, the customer's language is preferred over the admin's language for both subject and body.
- SMTP only: Mail is sent via PHPMailer over SMTP using one of two credential sets (
EMAIL_CONTACT_FORMorEMAIL_SHOP_MAILER).
Known Issues & Security Gaps
- Missing template
apologize_shipping_delay.php— referenced byapplication/config/emailViews.json:50-52, consumed byAdv_mailer::sendCustomerInformDelay()atapplication/models/Adv_mailer.php:400, called fromapplication/models/Adv_order_model.php:2736and theAdvSendEmailForDeliveryDelayjob. File does not exist in eithermain/mail/ordefault/mail/— delivery-delay emails fail at CI view loading. Not visible in the viewer because it is absent from$email_views. - Orphan template
ask_us_email.php— present in both mail directories and inAdvEmailViewer.php:121, but has no layout inemailViews.json, no subject key inEMAIL_SUBJECTS, and no caller inAdv_mailer. Dead code preserved only for viewer rendering. - Divergent viewer / runtime template directories — viewer reads
application/views/default/mail/(because of the deprecatedclient_views = 'default'atapplication/config/app.php:17-19) whileAdv_mailerreadsapplication/views/main/mail/(templateFolderinemailViews.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 fromdefault/. A 6th template that exists only inmain/mail/—contact_email_generated— is not even listed in the viewer's$email_viewsarray, so the viewer never tries to render it (separate gap, see below). The 17 older files indefault/mail/are stale duplicates. - Viewer
$email_viewslist incomplete —contact_email_generated(layoutcontactEmailGenerated) andapologize_shipping_delay(layoutorderInformDelay) are not listed atAdvEmailViewer.php:120-142, so admins cannot preview them (and the latter is broken anyway). rtrimmask bug in subject editor —AdvEmailViewer.php::editEmailSubjects()strips the language suffix withrtrim($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.inform_new_productflagged BROKEN —application/models/Adv_mailer.php:241carries a@todo refactor ... BROKEN ATMcomment, yet the method is still wired throughnotify_admin_new_product.
Related Flows
- 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_rejectviasendUserReviewStatusUpdateEmail() - SY-11 Review Reminders — sends
review_for_facebook/_skroutz/_googleviasendCustomerPromptReview() - 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