Component

Module

Form Builder & Form Submission

Here contain 2 modules:

1. Form Builder: To create any of of form to display in frontend

2. Form Submission: This is a admin panel to see all form submissions

Directory

File Folder Link
M - 3 files \\SYNAS\Allan\DOCUMENTATION\Module\Form Builder & Form Submission\rckuc\wolf\app\models
V - 3 folders \\SYNAS\Allan\DOCUMENTATION\Module\Form Builder & Form Submission\rckuc\wolf\app\views
C - 2 files \\SYNAS\Allan\DOCUMENTATION\Module\Form Builder & Form Submission\rckuc\wolf\app\controllers
backend.php \\SYNAS\Allan\DOCUMENTATION\Module\Form Builder & Form Submission\rckuc\wolf\app\layouts
Backend - style.css \\SYNAS\Allan\DOCUMENTATION\Module\Form Builder & Form Submission\rckuc\wolf\admin\themes\black_and_white
Backend - Javascript files \\SYNAS\Allan\DOCUMENTATION\Module\Form Builder & Form Submission\rckuc\wolf\admin\javascript
mailer.php \\SYNAS\Allan\DOCUMENTATION\Module\Form Builder & Form Submission\rckuc\wolf\frontend
Frontend - default.css \\SYNAS\Allan\DOCUMENTATION\Module\Form Builder & Form Submission\rckuc\public\themes\rckuc\css
Frontend - Javascript \\SYNAS\Allan\DOCUMENTATION\Module\Form Builder & Form Submission\rckuc\public\themes\rckuc\js
.user.ini \\SYNAS\Allan\DOCUMENTATION\Module\Form Builder & Form Submission\rckuc
wolf_business_profile.sql & wolf_business_branch.sql \\SYNAS\Allan\DOCUMENTATION\Module\Form Builder & Form Submission

 

Step 1

Update: backend.php

  • Insert scripts file in <head> section
  • Add 2 new tabs
<head> <script type="text/javascript" src="<?php echo URI_PUBLIC; ?>wolf/admin/javascripts/form_builder.js"></script> <script type="text/javascript" src="<?php echo URI_PUBLIC; ?>wolf/admin/javascripts/form_submission.js"></script> <!-- Include FORMEO JS --> <link rel="stylesheet" href="<?php echo URI_PUBLIC; ?>wolf/admin/javascripts/formeo.min.css"> <script type="text/javascript" charset="utf-8" src="<?php echo URI_PUBLIC; ?>wolf/admin/javascripts/formeo.min.js"></script> </head> <!-- Add 2 Tabs --> <body id="body_<?php echo $ctrl.'_'.Dispatcher::getAction(); ?>"> <ul> <li id="page-plugin" class="plugin"><a href="<?php echo get_url('form_builder'); ?>"<?php if ($ctrl=='form_builder') echo ' class="current"'; ?>><?php echo __('Form Builder'); ?></a></li> <li id="page-plugin" class="plugin"><a href="<?php echo get_url('form_submission'); ?>"<?php if ($ctrl=='form_submission') echo ' class="current"'; ?>><?php echo __('Form Submission'); ?></a></li> </ul> </body>
<head>
    <script type="text/javascript" src="<?php echo URI_PUBLIC; ?>wolf/admin/javascripts/form_builder.js"></script>
    <script type="text/javascript" src="<?php echo URI_PUBLIC; ?>wolf/admin/javascripts/form_submission.js"></script>

    <!-- Include FORMEO JS -->
    <link rel="stylesheet" href="<?php echo URI_PUBLIC; ?>wolf/admin/javascripts/formeo.min.css">
    <script type="text/javascript" charset="utf-8" src="<?php echo URI_PUBLIC; ?>wolf/admin/javascripts/formeo.min.js"></script>
</head>

<!-- Add 2 Tabs -->
<body id="body_<?php echo $ctrl.'_'.Dispatcher::getAction(); ?>">
    <ul>
         <li id="page-plugin" class="plugin"><a href="<?php echo get_url('form_builder'); ?>"<?php if ($ctrl=='form_builder') echo ' class="current"'; ?>><?php echo __('Form Builder'); ?></a></li>
         <li id="page-plugin" class="plugin"><a href="<?php echo get_url('form_submission'); ?>"<?php if ($ctrl=='form_submission') echo ' class="current"'; ?>><?php echo __('Form Submission'); ?></a></li>
    </ul>
</body>

Step 2

Update: model Page.php
Update function for includeSnippet()
To key in form name in $vars so that we can display any form

public function includeSnippet($name, $vars = []) { $snippet = Snippet::findByName($name); if (false !== $snippet) { // Extract variables into scope for the snippet extract($vars); eval('?>'.$snippet->content_html); return true; } return false; }
public function includeSnippet($name, $vars = []) {
    $snippet = Snippet::findByName($name);

    if (false !== $snippet) {
        // Extract variables into scope for the snippet
        extract($vars);

        eval('?>'.$snippet->content_html);
        return true;
    }

    return false;
}

Step 3

Insert SQL Record into wolf_module_setting:

Add 2 settings:

  1. Data Sitekey
  2. Secret
INSERT INTO `wolf_module_setting` (`id`, `module`, `name`, `value_type`, `value`, `created_on`, `updated_on`, `created_by_id`, `updated_by_id`) VALUES (NULL, 'form_builder', 'Data Sitekey', 'textarea', '***Replace Your Data Sitekey***', NOW(), NULL, NULL, NULL), (NULL, 'form_builder', 'Secret', 'textarea', '***Replace Your Secret***', NOW(), NULL, NULL, NULL);
INSERT INTO `wolf_module_setting`
(`id`, `module`, `name`, `value_type`, `value`, `created_on`, `updated_on`, `created_by_id`, `updated_by_id`)
VALUES 
(NULL, 'form_builder', 'Data Sitekey', 'textarea', '***Replace Your Data Sitekey***', NOW(), NULL, NULL, NULL),
(NULL, 'form_builder', 'Secret', 'textarea', '***Replace Your Secret***', NOW(), NULL, NULL, NULL);

Step 4

Update: style.css

/* FORM BUILDER */ .save-form { display: none !important; } #email_template { display: none; } #email_list { display: flex; flex-direction: column; } #email_list input { width: 20rem; } .email_container { display: flex; align-items: center; justify-content: flex-start; gap: 0.5rem; margin-bottom: 0.5rem; } .delete_email { cursor: pointer; height: 20px; width: 20px; } .email_container:first-child > img { display: none; } #preview_form_container { background-color: lightgray; padding: 1rem; margin-bottom: 1rem; } #formeo-editor h1, #preview_form_container h1 { color: unset; background-color: unset; text-shadow: unset; margin: unset; } #formeo-editor .field-attrs-name, #formeo-editor input[value="select_first_option"], #formeo-editor .options-panel input.name, #formeo-editor .options-panel input.value { display: none; } /* FORM SUBMISSSION */ #body_form_submission_index #content-wrapper { width: 95%; float: unset; margin: 1rem auto; } .email_data_table { margin: auto; } .email_data_table th, .email_data_table td { border-bottom: 1px solid #ddd !important; padding: 0.5rem !important; text-align: left; } .email_data_table td { border-color: #eee !important; } .email_data_table th:nth-child(2), .email_data_table td:nth-child(2) { padding-left: 1rem !important; }
/* FORM BUILDER */
.save-form {
    display: none !important;
  }
  
#email_template {
    display: none;
}

#email_list {
  display: flex;
  flex-direction: column;
}

#email_list input {
  width: 20rem;
}

.email_container {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 0.5rem;
  margin-bottom: 0.5rem;
}

.delete_email {
  cursor: pointer;
  height: 20px;
  width: 20px;
}

.email_container:first-child > img {
  display: none;
}

#preview_form_container {
  background-color: lightgray;
  padding: 1rem;
  margin-bottom: 1rem;
}

#formeo-editor h1,
#preview_form_container h1 {
  color: unset;
  background-color: unset;
  text-shadow: unset;
  margin: unset;
}

#formeo-editor .field-attrs-name,
#formeo-editor input[value="select_first_option"],
#formeo-editor .options-panel input.name,
#formeo-editor .options-panel input.value {
  display: none;
}


/* FORM SUBMISSSION */
#body_form_submission_index #content-wrapper {
  width: 95%;
  float: unset;
  margin: 1rem auto;
}

.email_data_table {
  margin: auto;
}

.email_data_table th,
.email_data_table td {
  border-bottom: 1px solid #ddd !important; 
  padding: 0.5rem !important;
  text-align: left;
}

.email_data_table td {
  border-color:  #eee !important;
}

.email_data_table th:nth-child(2),
.email_data_table td:nth-child(2) {
  padding-left: 1rem !important;
}

Step 5

Create New Layout: mailer-php under 'Layouts' Tab

Then insert this code:

<?php include('wolf/frontend/mailer.php'); ?>
<?php include('wolf/frontend/mailer.php'); ?>

Step 6

Introduction : mailer.php
This is a php script to:

  1. Handle form validation
  2. Send email
  3. Save email data to database 'wolf_form_submission' 
  4. Upload attached files to server

Step 7

Create New Page: mailer-php under 'Pages' Tab

  • Set Status to Hidden
  • Set Layout to mailer.php

Step 8

Create New Snippet: js-formeo
Introduction render-form:

  1. Render Formeo form
  2. Validate form data
  3. Use Ajax to submit form
<link rel="stylesheet" href="<?php echo THEME_PATH ?>js/formeo.min.css"> <script src="<?php echo THEME_PATH ?>js/formeo.min.js"></script> <script src="<?php echo THEME_PATH ?>js/render_form.js"></script>
<link rel="stylesheet" href="<?php echo THEME_PATH ?>js/formeo.min.css">
<script src="<?php echo THEME_PATH ?>js/formeo.min.js"></script>
<script src="<?php echo THEME_PATH ?>js/render_form.js"></script>

Step 9

Create New Snippet: render-form

<?php $form_name = $vars['form_name'] ?? ''; $form = FormBuilder::findByFormName($form_name); if ($form->status == 1) { $hiddenFields = [ 'ajax_url_mailer' => URL_PUBLIC . 'mailer-php', 'email_subject' => $form->name ?? '', 'email_to' => $form->email_to ?? '', 'show_label' => $form->show_label ?? '', 'show_placeholder' => $form->show_placeholder ?? '', 'auto_reply' => $form->auto_reply ?? '', 'auto_subject' => $form->auto_subject ?? '', 'auto_header' => $form->auto_header ?? '', 'auto_body' => $form->auto_body ?? '', 'auto_footer' => $form->auto_footer ?? '', 'success_msg' => $form->success_msg ?? 'Email Submitted Successfully', 'error_msg' => $form->error_msg ?? 'Email Submit Failed', ]; foreach ($hiddenFields as $id => $value) { // Escape value except for success_msg/error_msg if you trust them $escapedValue = htmlspecialchars($value, ENT_QUOTES); echo "<input id=\"$id\" type=\"hidden\" value=\"$escapedValue\">"; } $countries = Country::findAll(); echo '<div class="country_code_list">'; foreach ($countries as $country) { if (!empty($country->country_phone_code)) { echo '<div data-code="'.$country->country_phone_code.'">'.$country->country_name.' ('.$country->country_phone_code.')</div>'; } } echo '</div>'; echo '<form action="" enctype="multipart/form-data" data-form="'.htmlspecialchars($form->form_json, ENT_QUOTES).'"></form>'; ?> <?php } ?>
<?php 
$form_name = $vars['form_name'] ?? '';

$form = FormBuilder::findByFormName($form_name);

if ($form->status == 1) {
    $hiddenFields = [
        'ajax_url_mailer'    => URL_PUBLIC . 'mailer-php',
        'email_subject'    => $form->name ?? '',
        'email_to'         => $form->email_to ?? '',
        'show_label'       => $form->show_label ?? '',
        'show_placeholder' => $form->show_placeholder ?? '',
        'auto_reply'       => $form->auto_reply ?? '',
        'auto_subject'     => $form->auto_subject ?? '',
        'auto_header'      => $form->auto_header ?? '',
        'auto_body'        => $form->auto_body ?? '',
        'auto_footer'      => $form->auto_footer ?? '',
        'success_msg'      => $form->success_msg ?? 'Email Submitted Successfully',
        'error_msg'        => $form->error_msg ?? 'Email Submit Failed',
    ];

    foreach ($hiddenFields as $id => $value) {
        // Escape value except for success_msg/error_msg if you trust them
        $escapedValue = htmlspecialchars($value, ENT_QUOTES);
        echo "<input id=\"$id\" type=\"hidden\" value=\"$escapedValue\">";
    }

    $countries = Country::findAll();
    echo '<div class="country_code_list">';
    foreach ($countries as $country) {
        if (!empty($country->country_phone_code)) {
            echo '<div data-code="'.$country->country_phone_code.'">'.$country->country_name.' ('.$country->country_phone_code.')</div>';
        }
    }
    echo '</div>';
    
    echo '<form action="" enctype="multipart/form-data" data-form="'.htmlspecialchars($form->form_json, ENT_QUOTES).'"></form>';
?>

<?php } ?>

Step 10

Update: default.css

/* Formeo Form */ #formeo-form { background-color: #f3f3f3; border-bottom: solid 8px #f7a81b; } div.g-recaptcha { margin: 0; } .form-message { text-align: center; /* padding-bottom: 1rem; */ } .form-message.info { color: #0050a2; } .form-message.success { color: green; } .form-message.error { color: red; } .dots::after { content: ''; display: inline-block; width: 1em; text-align: left; animation: dots 1s steps(3,end) infinite; } @keyframes dots { 0% { content: ''; } 33% { content: '.'; } 66% { content: '..'; } 100% { content: '...'; } } /* Move the form under menu header */ #formeo-form { scroll-margin-top: 100px; font-family: 'Work Sans', Arial, Helvetica, sans-serif; } /* Text <select> in will overflow, so change font-family */ #formeo-form form * { font-family: 'Work Sans', Arial, Helvetica, sans-serif !important; } .formeo.formeo-render .formeo-column { padding: 0 8px; } #formeo-form input.text, #formeo-form textarea.text, #formeo-form select.text { /* width: 88.8%; */ background-color: #fff; padding: 14px 20px; /* margin: 0 1rem; */ font-size: 15px; position: relative; z-index: 1; font-family: 'Open Sans', Arial, Helvetica, sans-serif; line-height: 1; border: solid 1px #cecece; height: unset; border-radius: unset; } #formeo-form select.text { color: #999; appearance: none; -webkit-appearance: none; -moz-appearance: none; background-image: url(images/dropdown-icon.png); background-repeat: no-repeat; background-position: right 1rem center; background-size: 1rem; } #formeo-form select.text option:not([disabled]) { color: black; } #formeo-form select.text.has-value { color: unset; } #formeo-form input[type="submit"].btn-blue { border: none; cursor: pointer; outline: none; background-color: #0050a2; padding: 18px 35px; color: #f7a81b; font-weight: bold; font-size: 14px; border-radius: 30px; min-width: 100px; text-align: center; text-transform: uppercase; width: unset; height: unset; line-height: unset; float: right; } #formeo-form input[type='file'] { opacity: 0; padding: 23px 20px; } #formeo-form .holder { flex: 1; border: solid 1px #cecece; background-color: white; position: relative; } #formeo-form .custom-file-label { color: black; cursor: pointer; position: absolute; top: 0; left: 0; height: 100%; width: 30%; display: flex; align-items: center; justify-content: center; background-color: #F3F3F3; border-right: 1px solid #cecece; } #formeo-form .custom-file-name { position: absolute; top: 0; left: 30%; height: 100%; width: 70%; padding: 0 1.5rem; display: flex; align-items: center; } #formeo-form .custom-file-name.placeholder { color: #999; } #formeo-form .file-input:valid + .custom-file-label::after { content: ' - ' attr(data-file-name); } @media only screen and (max-width: 768px) { #formeo-form .formeo-row { flex-direction: column; padding: 0; gap: 1em; } #formeo-form input[type="submit"].btn-blue { display: block; margin: 0 auto; float: unset !important; } #formeo-form .formeo-column { width: 100% !important; padding: 0; } } #formeo-form .error { border-color: red !important; } #formeo-form select.error { color: red; } #formeo-form .holder.error { border-color: red; } /* To style radio & checkbox red if error */ #formeo-form input[type='radio'].error, #formeo-form input[type='checkbox'].error { visibility: hidden; } #formeo-form input[type='radio'].error:after, #formeo-form input[type='checkbox'].error:after { width: 0.5rem; height: 0.5rem; content: ''; display: inline-block; visibility: visible; border: 0.1rem solid red; } #formeo-form input[type='radio'].error:after { border-radius: 0.5rem; } .contact_number_container { position: relative; } .country_code_button { position: absolute; z-index: 10; height: 100%; display: flex; align-items: center; justify-content: flex-start; width: 50px; padding: 0 15px; cursor: pointer; background-image: url(images/dropdown-icon.png); background-repeat: no-repeat; background-position: right center; background-size: 1rem; } .contact_number_container > input[type='tel'] { padding-left: 100px !important; } .country_code_list { display: none; position: absolute; top: 47px; left: 0; border: solid 1px #cecece; max-height: 20rem; overflow-x: auto; cursor: pointer; background-color: white; z-index: 10; } .country_code_list > div { display: flex; align-items: center; justify-content: flex-start; padding: 0.5rem; } .country_code_list > div:hover { background-color: lightgray; } #formeo-form .btn-wrapper { position: relative; display: inline-block; float: right; } /* Loading state: hide text */ #formeo-form .btn-wrapper.loading input { color: transparent; /* hide the button text */ pointer-events: none; } /* The spinner overlay */ #formeo-form .btn-wrapper.loading::after { content: ""; position: absolute; top: 50%; left: 50%; width: 20px; height: 20px; transform: translate(-50%, -50%); border: 3px solid #f7a81b; border-top-color: transparent; border-radius: 50%; animation: spin 0.8s linear infinite; z-index: 2; } @keyframes spin { to { transform: translate(-50%, -50%) rotate(360deg); } }
/* Formeo Form */

#formeo-form { background-color: #f3f3f3; border-bottom: solid 8px #f7a81b; }

div.g-recaptcha {
	margin: 0;
}

.form-message {
	text-align: center;
	/* padding-bottom: 1rem; */
}

.form-message.info {
	color: #0050a2;
}

.form-message.success {
	color: green;
}

.form-message.error {
	color: red;
}

.dots::after {
    content: '';
    display: inline-block;
    width: 1em;
    text-align: left;
    animation: dots 1s steps(3,end) infinite;
}

@keyframes dots {
    0%   { content: ''; }
    33%  { content: '.'; }
    66%  { content: '..'; }
    100% { content: '...'; }
}

/* Move the form under menu header */
#formeo-form {
  scroll-margin-top: 100px;
  font-family: 'Work Sans', Arial, Helvetica, sans-serif;
}

/* Text <select> in will overflow, so change font-family */
#formeo-form form * {
  font-family: 'Work Sans', Arial, Helvetica, sans-serif !important;
}

.formeo.formeo-render .formeo-column {
	padding: 0 8px;
}

#formeo-form input.text,
#formeo-form textarea.text,
#formeo-form select.text { 
	/* width: 88.8%;  */
	background-color: #fff; 
	padding: 14px 20px; 
	/* margin: 0 1rem;  */
	font-size: 15px; 
	position: relative; 
	z-index: 1; 
	font-family: 'Open Sans', Arial, Helvetica, sans-serif; 
	line-height: 1; 
	border: solid 1px #cecece; 
	height: unset;
	border-radius: unset;
}


#formeo-form select.text {
	color: #999;
	appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    background-image: url(images/dropdown-icon.png);
    background-repeat: no-repeat;
    background-position: right 1rem center;
    background-size: 1rem;
}

#formeo-form select.text option:not([disabled]) {
    color: black;
}

#formeo-form select.text.has-value {
	color: unset;
}

#formeo-form input[type="submit"].btn-blue { 
	border: none; 
	cursor: pointer;
	outline: none; 
	background-color: #0050a2;
	padding: 18px 35px;
	color: #f7a81b;
	font-weight: bold;
	font-size: 14px;
	border-radius: 30px;
	min-width: 100px;
	text-align: center;
	text-transform: uppercase;
	width: unset;
	height: unset;
	line-height: unset;
	float: right;
}

#formeo-form input[type='file'] {
	opacity: 0;
	padding: 23px 20px;
}

#formeo-form .holder {
	flex: 1;
    border: solid 1px #cecece;
    background-color: white;
    position: relative;
}

#formeo-form .custom-file-label {
	color: black;
	cursor: pointer;
	position: absolute;
	top: 0;
	left: 0;
	height: 100%;
	width: 30%;
	display: flex;
	align-items: center;
	justify-content: center;
	background-color: #F3F3F3;
	border-right: 1px solid #cecece;
}

#formeo-form .custom-file-name {
	position: absolute;
	top: 0;
	left: 30%;
	height: 100%;
	width: 70%;
	padding: 0 1.5rem;
	display: flex;
	align-items: center;
}

#formeo-form .custom-file-name.placeholder {
	color: #999;
}

#formeo-form .file-input:valid + .custom-file-label::after {
	content: ' - ' attr(data-file-name);
}

@media only screen and (max-width: 768px) {
	#formeo-form .formeo-row {
		flex-direction: column;
		padding: 0;
		gap: 1em;
	}

	#formeo-form input[type="submit"].btn-blue {
		display: block;
		margin: 0 auto;
		float: unset !important;
	}

	#formeo-form .formeo-column {
		width: 100% !important;
		padding: 0;
	}
}

#formeo-form .error {
	border-color: red !important;
}

#formeo-form select.error {
	color: red;
}

#formeo-form .holder.error {
	border-color: red;
}

/* To style radio & checkbox red if error */
#formeo-form input[type='radio'].error,
#formeo-form input[type='checkbox'].error {
	visibility: hidden;
}

#formeo-form input[type='radio'].error:after,
#formeo-form input[type='checkbox'].error:after {
	width: 0.5rem;
	height: 0.5rem;
	content: '';
	display: inline-block;
	visibility: visible;
	border: 0.1rem solid red;
}

#formeo-form input[type='radio'].error:after {
	border-radius: 0.5rem;
}

.contact_number_container {
	position: relative;
}

.country_code_button {
	position: absolute;
	z-index: 10;
    height: 100%;
    display: flex;
    align-items: center;
	justify-content: flex-start;
	width: 50px;
	padding: 0 15px;
	cursor: pointer;

	background-image: url(images/dropdown-icon.png);
    background-repeat: no-repeat;
    background-position: right center;
    background-size: 1rem;
}

.contact_number_container > input[type='tel'] {
	padding-left: 100px !important;
}

.country_code_list {
	display: none;
	position: absolute;
	top: 47px;
	left: 0;
	border: solid 1px #cecece;
	max-height: 20rem;
	overflow-x: auto;
	cursor: pointer;
	background-color: white;
	z-index: 10;
}

.country_code_list > div {
	display: flex;
	align-items: center;
	justify-content: flex-start;
	padding: 0.5rem;
}

.country_code_list > div:hover {
	background-color: lightgray;
}


#formeo-form .btn-wrapper {
  position: relative;
  display: inline-block;
  float: right;
}

/* Loading state: hide text */
#formeo-form .btn-wrapper.loading input {
  color: transparent;  /* hide the button text */
  pointer-events: none;
}

/* The spinner overlay */
#formeo-form .btn-wrapper.loading::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 20px;
  height: 20px;
  transform: translate(-50%, -50%);
  border: 3px solid #f7a81b;
  border-top-color: transparent;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
  z-index: 2;
}

@keyframes spin {
  to {
    transform: translate(-50%, -50%) rotate(360deg);
  }
}

Step 11

Copy all related files to your project and import sql table

Step 12

Create Form and Display:

  1. Create some form under 'Form_Builder' Tab
  2. In 'Layouts' Tab go to any frontend layout that you wish to display form and insert this code:

Eg. I want to display 'Job Application Form'

<div id="formeo-form"><div class="wrapper"><?php $this->includeSnippet('render-form', ['form_name' => 'Job Application Form']) ?></div></div>
<div id="formeo-form"><div class="wrapper"><?php $this->includeSnippet('render-form', ['form_name' => 'Job Application Form']) ?></div></div>

Step 13

If you want to adjust file upload limit, create a file '.user.ini' in same level folder as '.htaccess' and insert the code below, adjust your limit to your liking

upload_max_filesize = 25M post_max_size = 25M max_file_uploads = 25
upload_max_filesize = 25M
post_max_size = 25M
max_file_uploads = 25
Code Copied To Clipboard!