Skip to content

Contact Forms

Flow ID: CF-29 | Module(s): forms, Cms domain | Complexity: Medium

Business Overview

Config-driven dynamic contact forms with field-level customization, file attachments, CAPTCHA protection, and email delivery. Submissions are archived in the database and viewable in the admin panel. The system supports multiple form definitions from a single config file, allowing each site to define any number of contact forms with different field sets.

What customers experience:

  • Visit a language-specific contact URL (e.g., /epikoinonia in Greek, /contact in English)
  • Fill in a dynamically rendered form (text, email, textarea, select, radio, checkbox, checkbox-group, file upload fields)
  • CAPTCHA verification (Google reCAPTCHA v2 or CodeIgniter image captcha)
  • On success, see a confirmation page with the generated email content
  • Store owner receives an email with all submitted data and file attachments

Key business behaviors:

  • Forms are entirely config-driven: fields, validation rules, labels, and templates defined in contact_forms.php
  • Two rendering modes: hand-crafted HTML (contactForm view) and auto-generated from config (contactFormGenerated view)
  • File uploads stored in ../storage/forms/ with type/size validation per field
  • Submissions archived in contact_emails table with JSON metadata and rendered HTML
  • Email subject resolved from registry (EMAIL_SUBJECTS group, keyed by form name and language)
  • Sender email validation: submissions without a valid sender email are silently skipped (no email sent, no DB record)
  • If a logged-in customer submits, their customer_id is linked to the submission
  • Meta Conversions API integration: Contact event type available for Facebook pixel tracking

API Reference

REST Endpoints (Admin)

MethodPathAuthDescription
GET/rest/cms/contact-emailBackend (ADMIN, CMS)List submissions with filtering/sorting
GET/rest/cms/contact-email/{id}Backend (ADMIN, CMS)Get single submission
GET/rest/cms/contact-email/itemBackend (ADMIN, CMS)Get single submission by filters
POST/rest/cms/contact-emailBackend (ADMIN, CMS)Create submission record
POST/rest/cms/contact-email/{id}Backend (ADMIN, CMS)Update submission record
DELETE/rest/cms/contact-email/{id}Backend (ADMIN, CMS)Delete submission record

Filters: id (exact), email (partial), customerId (exact), emailId (exact) Sorts: id, emailDatetime, email, emailIdRelations: customer (belongs_to)

Legacy Storefront

URLController MethodDescription
/epikoinonia (Greek)Adv_forms::contactForm()Default contact form
/en/contact (English)Adv_forms::contactForm()Default contact form
/de/kontakt (German)Adv_forms::contactForm()Default contact form

Custom forms are accessed by passing a contactFormKey parameter: contactForm('fullSampleForm').

Admin Panel

URLControllerDescription
/contacts/listAdmin_contact_email_viewer::index()Paginated list with filters
/contacts/view/{id}Admin_contact_email_viewer::view()View rendered email in popup

Code Flow

Storefront Submission

File: ecommercen/forms/controllers/Adv_forms.php

  1. Load config: FormsConfig loads contact_forms.php and resolves the form definition by key (default: contactForm)
  2. Set validation: setContactValidation() iterates config fields, setting CI form_validation rules per field (skips upload type fields)
  3. Set CAPTCHA: setCaptchaValidationRule() adds CAPTCHA validation (trait-based: Google reCAPTCHA, CI image captcha, or no-op)
  4. Check required files: requiredFilesInPost() validates that required upload fields have files in $_FILES
  5. Upload files: uploadFiles() processes file uploads via AdvUploader to ../storage/forms/ with per-field type/size constraints
  6. Collect post data: getContactPostData() builds an associative array from POST values, handling text, email, textarea, select, radio, checkbox, and checkbox-group types
  7. Render email: Loads the email template view with form data, external language helper, config, and attachment paths
  8. Allow check: allowContactEmail() verifies the sender email field is present and non-empty (if configured)
  9. Send email: Adv_mailer::sendContactFormEmail() sends via SMTP using EMAIL_CONTACT_FORM registry credentials, with file attachments
  10. Archive: Insert into contact_emails table: email, customer_id (if logged in), form key as email_id, timestamp, JSON metadata, and rendered HTML
  11. Success view: Swap the form view for contactSuccess template showing the generated email content

Email Delivery

File: application/models/Adv_mailer.php method sendContactFormEmail()

  • Retrieves SMTP credentials from registry group EMAIL_CONTACT_FORM via getDefaultSmtpCredentials()
  • Sends to the admin email configured in that registry group
  • Attaches uploaded files from ../storage/forms/ directory

Admin Viewing

File: ecommercen/forms/controllers/AdvAdminContactEmailViewer.php

  • Paginated list (15 per page) with filters: email (LIKE), type/email_id (exact), date (LIKE)
  • Inline JSON metadata expansion per row
  • Popup window to view the original rendered HTML email
  • Customer name resolved from customer_model for linked submissions

Domain Layer

ComponentPath
Entitysrc/Domains/Cms/ContactEmail/Repository/Entity.php
Repositorysrc/Domains/Cms/ContactEmail/Repository/Repository.php
RepositoryConfiguratorsrc/Domains/Cms/ContactEmail/Repository/RepositoryConfigurator.php
WriteRepositorysrc/Domains/Cms/ContactEmail/Repository/WriteRepository.php
Service (Read)src/Domains/Cms/ContactEmail/Service.php
WriteServicesrc/Domains/Cms/ContactEmail/WriteService.php
WriteDatasrc/Domains/Cms/ContactEmail/WriteData.php
Validatorsrc/Domains/Cms/ContactEmail/Validator.php
ListRequestsrc/Domains/Cms/ContactEmail/ListRequest.php
REST Controllersrc/Rest/Cms/Controllers/ContactEmail.php
Resourcesrc/Rest/Cms/Resources/ContactEmail/Resource.php
Collectionsrc/Rest/Cms/Resources/ContactEmail/Collection.php
Domain DIsrc/Domains/Cms/container.php
REST DIsrc/Rest/Cms/container.php

Architecture

Forms Config System

File: ecommercen/forms/libraries/AdvFormsConfig.php (extended by application/modules/forms/libraries/FormsConfig.php)

The FormsConfig class wraps application/config/contact_forms.php and provides accessor methods:

MethodPurpose
getContactFormKeys()All defined form keys
getContactFormKeysDropDown()Translated dropdown for admin filters
getContactFormConfig($key)Full config array for a form
getContactFormSubmitName($key)POST field name for submit detection
getFrontTemplateView($key)Template key for storefront layout
getEmailTemplateView($key)Template key for email body
getTranslatedSelectOptions($key, $field)Translated options for select/radio/checkbox-group
getRequiredFileKeys($key)Upload fields marked as required
getClientEmailFieldKey($key)Which field holds the sender's email
getPageTitleKey($key)Translation key for the page title

CAPTCHA Strategy

Three interchangeable traits (used on the front controller hierarchy):

TraitFileMechanism
CaptchaTraitecommercen/core/CaptchaTrait.phpNo-op (disabled)
GoogleRecaptchaTraitecommercen/core/GoogleRecaptchaTrait.phpGoogle reCAPTCHA v2 via registry keys GOOGLE.RECAPTCHA_KEY / GOOGLE.RECAPTCHA_SECRET
CodeigniterCaptchaTraitecommercen/core/CodeigniterCaptchaTrait.phpServer-generated image captcha using SimpleImage

The active trait is applied on the front controller base class. Only one is active at a time.

Template System

Two rendering strategies mapped in template.json / mainTemplate.json:

Template KeyViewDescription
contactFormlayouts/contact_form/contact_form.phpHand-crafted HTML for the default form
contactFormGeneratedlayouts/contact_form/contact_form_generated.phpAuto-generated from config (iterates fields by type)
contactSuccesslayouts/contact_form/contact_form_success.phpSuccess confirmation page

Email templates in emailViews.json:

Template KeyView
contactEmailmail/contact_email.php
contactEmailGeneratedmail/contact_email_generated.php

The generated templates (contactFormGenerated / contactEmailGenerated) dynamically render all field types from config, while the standard templates (contactForm / contactEmail) have hardcoded field HTML for the default contact form.


Data Model

contact_emails

ColumnTypeDescription
idint(11) unsigned PK AISubmission ID
emailvarchar(255) nullableSender email address
customer_idint(11) nullableFK to customer if logged in
email_idvarchar(255) NOT NULLForm key identifier (e.g., contactForm)
email_datetimedatetime NOT NULLSubmission timestamp
meta_datalongtext nullableJSON-encoded form field values
generated_emaillongtext nullableRendered HTML email content

Indexes: customer_id, email, email_id, email_datetime, composite (email, email_id), composite (email, email_datetime), composite (email_id, email_datetime)


Configuration

Form Definition (application/config/contact_forms.php)

Each form is a keyed entry in $config['contactForms'] with this structure:

php
'formKey' => [
    'pageTitleKey' => 'translation.key',         // Page <title> translation key
    'fields' => [
        'fieldName' => [
            'type' => 'text|email|textarea|select|radio|checkbox|checkbox-group|upload',
            'labelKey' => 'translation.key',      // Field label translation key
            'validation' => 'trim|required|...',  // CI form_validation rules
            'options' => [...],                    // For select/radio/checkbox-group
            'upload' => [                          // For upload type
                'allowed_types' => 'pdf|png|jpg',
                'size' => 2048                     // Max KB
            ],
        ],
    ],
    'submit' => [
        'name' => 'postFieldName',               // POST key for submit detection
        'labelKey' => 'translation.key',
    ],
    'senderEmailField' => 'fieldName',           // Which field holds sender email
    'frontTemplateView' => 'contactForm',        // Template key for storefront
    'emailTemplateView' => 'contactEmail',       // Template key for notification email
]

Default form (contactForm): email, name, surname, phone, mobile, message Sample form (fullSampleForm): all field types including uploads, select, radio, checkbox-group

Registry Keys

GroupKeyDescription
EMAIL_CONTACT_FORM(SMTP config)SMTP credentials for sending contact emails
EMAIL_SUBJECTS{formKey}Email subject line per form per language
GOOGLERECAPTCHA_KEYreCAPTCHA v2 site key (if Google trait active)
GOOGLERECAPTCHA_SECRETreCAPTCHA v2 secret key
TITLESITEFallback email subject (site name)

REST Policies

php
ContactEmail::class => [
    'defaults' => ['auth' => 'backend', 'roles' => [AUTH_ROLE_ADMIN, AUTH_ROLE_CMS]],
],

Admin Menu

Listed under the CUSTOMERS group at route contacts/list.

Constants

php
const FORMS_ATTACHMENTS_PATH = '../storage/forms/';  // application/config/constants.php

Client Extension Points

  • Custom forms: Add entries to application/config/contact_forms.php with unique keys and views
  • FormsConfig override: application/modules/forms/libraries/FormsConfig.php extends AdvFormsConfig -- add custom methods or override existing ones
  • Controller override: Adv_forms can be extended in application/controllers/ to add pre/post-submission hooks via contactFormExtras()
  • CAPTCHA strategy: Switch the active captcha trait on the front controller (no-op, Google reCAPTCHA, or CI image captcha)
  • Email templates: Override contact_email.php / contact_email_generated.php in application/views/main/mail/
  • Form views: Override contact_form.php / contact_form_generated.php / contact_form_success.php in application/views/main/layouts/contact_form/
  • Template mapping: Override contactForm, contactFormGenerated, contactSuccess keys in mainTemplate.json
  • Custom routes: Add language-specific routes in application/config/my_routes.php pointing to forms/contactForm/{formKey}
  • Block sender: Override allowContactEmail() to implement email blocklists
  • Meta Conversions API: EventType::CONTACT available for Facebook pixel tracking of form submissions

Business Rules

RuleImplementation
Sender email required for archivalallowContactEmail() returns false if the configured senderEmailField is empty
One email per submissionEmail sent to admin only (single EMAIL_CONTACT_FORM SMTP config)
File uploads validated per-fieldallowed_types and size from config; AdvUploader enforces constraints
Required file uploads checked before processingrequiredFilesInPost() verifies required upload fields have files in $_FILES before upload attempt
CAPTCHA requiredEnforced at validation level; trait determines which implementation
Customer linkingIf session has customer_id, it is stored with the submission
Language-scoped URLsRoutes per language (Greek /epikoinonia, English /contact, etc.) with contactFormLinks() generating hreflang alternates
Email subject from registryEMAIL_SUBJECTS.{formKey} per language, falls back to site name

See also the Contact Forms from Config guide for details on creating custom form definitions.