Skip to content

Developer notes

Path Purpose
app/addons/kv_email_log/addon.xml Settings, tables, privileges
app/addons/kv_email_log/init.php Hook registration
app/addons/kv_email_log/func.php Hook handlers, GDPR callbacks, cron info, uninstall
app/addons/kv_email_log/controllers/backend/kv_email_log.php Log, details, preview, resend, queue, templates, test, privacy, export, cron
app/addons/kv_email_log/controllers/backend/orders.post.php, profiles.post.php E-mails tabs
Tygh/Addons/KvEmailLog/Capture.php Decides per e-mail: send and log, mute or queue
Tygh/Addons/KvEmailLog/Transport/ProxyTransport.php Transport wrapper used for one send
Tygh/Addons/KvEmailLog/Sender.php Sends a logged message, records the result, retries, resend
Tygh/Addons/KvEmailLog/Queue.php Deferred sending, cron worker, rate limit, stale claims
Tygh/Addons/KvEmailLog/Repository.php Storage, search, retention, privacy
Tygh/Addons/KvEmailLog/Codec.php Message description, body packing, order/customer links
Tygh/Addons/KvEmailLog/Diagnostics.php Test e-mail, SMTP transcript, DNS checks
Tygh/Addons/KvEmailLog/Presenter.php View data, preview document, CSV
Tygh/Addons/KvEmailLog/License/*, Upgrade/Synchronizer.php License client, self-healing upgrades
schemas/gdpr/user_data.post.php Core GDPR integration
schemas/menu/*, schemas/permissions/* Menu entries, privileges, vendor access, cron URL
design/backend/templates/addons/kv_email_log/ Admin and vendor pages
design/backend/css/addons/kv_email_log/styles.less, js/addons/kv_email_log/admin.js Styles, preview switch

Every e-mail of CS-Cart goes through Tygh\Mailer\Mailer::send(), which calls the hook mailer_send_pre($mailer, $transport, $message, $area, $lang_code) right before $transport->sendMessage($message). The add-on registers this hook with priority 4294967000, so it runs after the add-ons that change the message (for example the core PDF documents add-on attaching an invoice). The handler replaces $transport (passed by reference) with ProxyTransport for this one send:

  • send: a log row is written, the real transport sends, the result (status, error, duration, transport type) is recorded. A temporary failure becomes a retry.
  • mute: logged as muted, not sent, success returned (no error notice).
  • queue: the message (body, addresses, attachment copies) is stored and success is returned at once; Queue sends it at the end of the request (fastcgi_finish_request() / litespeed_finish_request() first) or from cron, with the transport Mailer::send() would have chosen (company transport or the default one).

If anything in the add-on fails (database error, missing table), the proxy calls the real transport directly: the e-mail always goes out. When Mailer::send() is called with its own transport settings, the e-mail is never queued.

Callers of Mailer::send() get true for queued and muted e-mails. A queued e-mail that finally fails is visible in the log, not to the caller.

Table Contents
?:kv_eml_log One row per e-mail: status, origin, area, language, template code, company, storefront, owner (vendor) company, order, customer, addresses, subject, size, attachments (JSON), transport, duration, attempts and their history, next attempt, error, resend link, message state, queue claim
?:kv_eml_recipients Recipient index (address, entry, To/CC/BCC) for search and privacy requests
?:kv_eml_bodies Stored messages (deflated and base64-encoded, so any character survives a utf8 connection) and metadata (charset, exact subject, embedded images)

Attachment copies live in var/kv_email_log/<log_id>/ (web access denied).

Hook Where (4.17.2 and 4.21.2, identical arguments) Use
mailer_send_pre app/Tygh/Mailer/Mailer.php Capture
update_addon_status_pre app/functions/fn.addons.php Send the queue before the add-on is disabled

P sending, S sent, F failed, R retrying, Q queued, M muted, X cancelled. Origin: M store, R resend, T test.

php admin.php --dispatch=kv_email_log.cron # send the queue and due retries, daily cleanup
php admin.php --dispatch=kv_email_log.cron --purge=1 # also force the cleanup
php admin.php --dispatch=kv_email_log_license.check # license check

Claims are atomic (UPDATE ... SET lock_token ... LIMIT n), so several workers never send the same e-mail. Claims older than 10 minutes go back to the queue; inline sends that never finished are closed as failed (their outcome is unknown, so they are not sent twice).

Logging adds the row, the recipient index and the stored message per e-mail (a few milliseconds; the mail server itself usually takes 100 ms or more). Queued sending answers in a few milliseconds instead of waiting for the mail server. List pages use indexed filters; the daily cleanup deletes in batches of 5000 (10,000 entries in about half a second on a test store).

fn_kv_email_log_is_licensed() (License\Client::isActive()) together with the Log e-mails setting gates the capture (logging, muting, queueing), resend, bulk resend and the test e-mail. The log, the preview, the export, the cleanup, the privacy tools and the delivery of e-mails already queued always work. The licence check itself runs only in the admin panel or from cron.