first commit
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms;
|
||||
|
||||
use NRFramework\Cache;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
/**
|
||||
* Analytics Helper Class
|
||||
*/
|
||||
class Analytics
|
||||
{
|
||||
/**
|
||||
* Returns average submission in current month
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public static function getLeadsAverageThisMonth()
|
||||
{
|
||||
return number_format(self::getTotal('this_month') / date('d'), 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current month projection
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public static function getMonthProjection()
|
||||
{
|
||||
return number_format(self::getTotal('this_month') / date('d') * date('t'), 1);
|
||||
}
|
||||
|
||||
public static function getRows($type = null, $options = [])
|
||||
{
|
||||
return number_format(self::getTotal($type, $options));
|
||||
}
|
||||
|
||||
public static function getTotal($type = null, $options = [])
|
||||
{
|
||||
$hash = md5($type . serialize($options));
|
||||
|
||||
if (Cache::has($hash))
|
||||
{
|
||||
return Cache::get($hash);
|
||||
}
|
||||
|
||||
$model = \JModelLegacy::getInstance('Conversions', 'ConvertFormsModel', ['ignore_request' => true]);
|
||||
|
||||
$model->setState('filter.state', [1, 2]);
|
||||
$model->setState('filter.join_campaigns', 'skip');
|
||||
$model->setState('filter.join_forms', 'skip');
|
||||
|
||||
if ($type)
|
||||
{
|
||||
$model->setState('filter.period', $type);
|
||||
}
|
||||
|
||||
if ($type == 'range')
|
||||
{
|
||||
$model->setState('filter.created_from', $options['created_from']);
|
||||
$model->setState('filter.created_to', $options['created_to']);
|
||||
}
|
||||
|
||||
$query = $model->getListQuery();
|
||||
|
||||
$query->clear('select');
|
||||
$query->select('count(a.id)');
|
||||
|
||||
$db = \JFactory::getDbo();
|
||||
|
||||
// Prevent the "The SELECT would examine more than MAX_JOIN_SIZE rows; " MySQL error
|
||||
// on websites with a big number of menu items in the db.
|
||||
$db->setQuery('SET SQL_BIG_SELECTS = 1')->execute();
|
||||
|
||||
$db->setQuery($query);
|
||||
|
||||
$count = $db->loadResult();
|
||||
|
||||
return Cache::set($hash, $count);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
131
administrator/components/com_convertforms/ConvertForms/Api.php
Normal file
131
administrator/components/com_convertforms/ConvertForms/Api.php
Normal file
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
/**
|
||||
* This is the main Convert Forms API helper class meant to be used ONLY by 3rd party developers and advanced users.
|
||||
* Do not ever use this class to implement and rely any core feture.
|
||||
*/
|
||||
class Api
|
||||
{
|
||||
/**
|
||||
* Delete a submission from the database
|
||||
*
|
||||
* @param integer $id The submission's primary key
|
||||
*
|
||||
* @return bool True on success
|
||||
*/
|
||||
public static function removeSubmission($submission_id)
|
||||
{
|
||||
if (!$submission_id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
\JTable::addIncludePath(JPATH_ADMINISTRATOR . '/components/com_convertforms/tables');
|
||||
$table = \JTable::getInstance('Conversion', 'ConvertFormsTable');
|
||||
return $table->delete($submission_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all form submissions from the database
|
||||
*
|
||||
* @param integer $form_id The form's primary key
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function removeFormSubmissions($form_id)
|
||||
{
|
||||
if (!$form_id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$db = \JFactory::getDbo();
|
||||
|
||||
$query = $db->getQuery(true)
|
||||
->delete($db->quoteName('#__convertforms_conversions'))
|
||||
->where($db->quoteName('form_id') . ' = ' . $form_id);
|
||||
|
||||
$db->setQuery($query);
|
||||
|
||||
return $db->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all form submissions
|
||||
*
|
||||
* @param integer $form_id The form's ID
|
||||
*
|
||||
* @return Object
|
||||
*/
|
||||
public static function getFormSubmissions($form_id)
|
||||
{
|
||||
if (!$form_id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$db = \JFactory::getDbo();
|
||||
|
||||
$query = $db->getQuery(true)
|
||||
->select('*')
|
||||
->from($db->quoteName('#__convertforms_conversions'))
|
||||
->where($db->quoteName('form_id') . ' = ' . $form_id);
|
||||
|
||||
$db->setQuery($query);
|
||||
|
||||
$rows = $db->loadObjectList();
|
||||
|
||||
foreach ($rows as $key => $row)
|
||||
{
|
||||
$row->params = json_decode($row->params);
|
||||
}
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the total number of form total submissions
|
||||
*
|
||||
* @param integer $form_id The form's ID
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public static function getFormSubmissionsTotal($form_id)
|
||||
{
|
||||
return number_format(Form::getSubmissionsTotal($form_id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the visitor's device type: desktop, tablet, mobile
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getDeviceType()
|
||||
{
|
||||
return \NRFramework\WebClient::getDeviceType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate if the visitor is browsing the site via a mobile
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isMobile()
|
||||
{
|
||||
return self::getDeviceType() == 'mobile';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,287 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2022 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms;
|
||||
|
||||
use Joomla\Registry\Registry;
|
||||
use ConvertForms\Helper;
|
||||
use NRFramework\File;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
/**
|
||||
* Export submissions to CSV and JSON
|
||||
*/
|
||||
class Export
|
||||
{
|
||||
/**
|
||||
* Export submissions to CSV or JSON file
|
||||
*
|
||||
* @param array $options The export options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function export($options)
|
||||
{
|
||||
// Increase memory size and execution time to prevent PHP errors on datasets > 20K
|
||||
set_time_limit(300); // 5 Minutes
|
||||
ini_set('memory_limit', '-1');
|
||||
|
||||
$options = new Registry($options);
|
||||
|
||||
// Load submissions model
|
||||
\JModelLegacy::addIncludePath(JPATH_ADMINISTRATOR . '/components/com_convertforms/models');
|
||||
$model = \JModelLegacy::getInstance('Conversions', 'ConvertFormsModel', ['ignore_request' => true]);
|
||||
|
||||
// When we're exporting certain IDs, there's no need to check the state.
|
||||
if (strpos($options->get('filter_search', ''), 'id:') !== false || $options->get('filter_state') == '*')
|
||||
{
|
||||
$filter_state = 'skip';
|
||||
} else
|
||||
{
|
||||
$filter_state = $options->get('filter_state');
|
||||
}
|
||||
|
||||
$model->setState('filter.state', $filter_state);
|
||||
$model->setState('filter.join_campaigns', 'skip');
|
||||
$model->setState('filter.join_forms', 'skip');
|
||||
$model->setState('list.limit', $options->get('limit', 0));
|
||||
$model->setState('list.start', $options->get('offset', 0));
|
||||
$model->setState('list.direction', 'asc');
|
||||
$model->setState('filter.search', $options->get('filter_search'));
|
||||
$model->setState('filter.form_id', $options->get('filter_form_id'));
|
||||
$model->setState('filter.period', $options->get('filter_period', ''));
|
||||
$model->setState('filter.created_from', $options->get('filter_created_from', ''));
|
||||
$model->setState('filter.created_to', $options->get('filter_created_to', ''));
|
||||
|
||||
// Proceed only if we have submissions
|
||||
if (!$submissions = $model->getItems())
|
||||
{
|
||||
$error = \JText::sprintf('COM_CONVERTFORMS_NO_RESULTS_FOUND', strtolower(\JText::_('COM_CONVERTFORMS_SUBMISSIONS')));
|
||||
throw new \Exception($error);
|
||||
}
|
||||
|
||||
foreach ($submissions as $key => &$submission)
|
||||
{
|
||||
self::prepareSubmission($submission);
|
||||
}
|
||||
|
||||
$export_type = $options->get('export_type', 'csv');
|
||||
|
||||
$pagination = $model->getPagination();
|
||||
$firstRun = $pagination->pagesCurrent == 1;
|
||||
$append = !$firstRun || $options->get('export_append', false);
|
||||
|
||||
// Check whether the path does exist. If not, attemp to create it.
|
||||
$export_path = $options->get('export_path', File::getTempFolder());
|
||||
|
||||
if (!File::createDirs($export_path, false))
|
||||
{
|
||||
throw new \Exception('Export path does not exist');
|
||||
}
|
||||
|
||||
$filename = \JPath::clean($export_path . DIRECTORY_SEPARATOR) . $options->get('filename', 'convertforms_submissions.' . $export_type);
|
||||
|
||||
switch ($export_type)
|
||||
{
|
||||
case 'json':
|
||||
self::toJSON($submissions, $filename, $append);
|
||||
break;
|
||||
|
||||
default:
|
||||
$excel_security = (bool) Helper::getComponentParams()->get('excel_security', true);
|
||||
self::toCSV($submissions, $filename, $append, $excel_security);
|
||||
break;
|
||||
}
|
||||
|
||||
return [
|
||||
'options' => $options,
|
||||
'pagination' => $pagination
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a key value array with submission's submitted data
|
||||
*
|
||||
* @param object $submission The submission object
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function prepareSubmission(&$submission)
|
||||
{
|
||||
$result = [
|
||||
'id' => $submission->id,
|
||||
'created' => $submission->created,
|
||||
'state' => $submission->state
|
||||
];
|
||||
|
||||
foreach ($submission->prepared_fields as $field_name => $field)
|
||||
{
|
||||
// Always return the raw value and let the export type decide how the value should be displayed.
|
||||
$result[$field_name] = $field->value_raw;
|
||||
}
|
||||
|
||||
$submission = $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a JSON file with given data
|
||||
*
|
||||
* @param array $data The data to populate the file
|
||||
* @param string $destination The path where the store the JSON file
|
||||
* @param bool $append If true, given data will be appended to the end of the file.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function toJSON($data, $destination, $append = false, $check_for_duplicates = true)
|
||||
{
|
||||
$content = \JFile::exists($destination) ? (array) json_decode(file_get_contents($destination), true) : [];
|
||||
$content = $append ? array_merge($content, $data) : $data;
|
||||
|
||||
if ($content && $append && $check_for_duplicates)
|
||||
{
|
||||
$newArr = [];
|
||||
|
||||
foreach ($content as $row)
|
||||
{
|
||||
$newArr[$row['id']] = $row;
|
||||
}
|
||||
|
||||
$content = $newArr;
|
||||
}
|
||||
|
||||
$content = json_encode(array_values($content), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
|
||||
// Save the file
|
||||
\JFile::write($destination, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a CSV file with given data
|
||||
*
|
||||
* @param array $data The data to populate the file
|
||||
* @param string $destination The path where the store the CSV file
|
||||
* @param bool $append If true, given data will be appended to the end of the file.
|
||||
* @param boolean $excel_security If enabled, certain row values will be prefixed by a tab to avoid any CSV injection.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function toCSV($data, $destination, $append = false, $excel_security = true, $check_for_duplicates = true)
|
||||
{
|
||||
$resource = fopen($destination, $append ? 'a+' : 'w');
|
||||
|
||||
if (!$append)
|
||||
{
|
||||
// Support UTF-8 on Microsoft Excel
|
||||
fputs($resource, "\xEF\xBB\xBF");
|
||||
|
||||
// Add column names in the first line
|
||||
fputcsv($resource, array_keys($data[0]));
|
||||
}
|
||||
|
||||
// Get CSV content
|
||||
$existingRows = [];
|
||||
if ($append && $check_for_duplicates)
|
||||
{
|
||||
while (($existingData = fgetcsv($resource)) !== false)
|
||||
{
|
||||
$existingRows[(int) $existingData[0]] = $existingData;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($data as $row)
|
||||
{
|
||||
if (!empty($existingRows) && isset($row['id']) && array_key_exists($row['id'], $existingRows))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Prevent CSV Injection: https://vel.joomla.org/articles/2140-introducing-csv-injection
|
||||
if ($excel_security)
|
||||
{
|
||||
foreach ($row as &$value)
|
||||
{
|
||||
$value = is_array($value) ? implode(', ', $value) : $value;
|
||||
|
||||
$firstChar = substr($value, 0, 1);
|
||||
|
||||
// Prefixe values starting with a =, +, - or @ by a tab character
|
||||
if (in_array($firstChar, array('=', '+', '-', '@')))
|
||||
{
|
||||
$value = ' ' . $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fputcsv($resource, $row);
|
||||
}
|
||||
|
||||
fclose($resource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirects to the error layout and displays the given error message
|
||||
*
|
||||
* @param string $error_message
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function error($error_message)
|
||||
{
|
||||
$app = \JFactory::getApplication();
|
||||
|
||||
$optionsQuery = http_build_query(array_filter([
|
||||
'option' => 'com_convertforms',
|
||||
'view' => 'export',
|
||||
'layout' => 'error',
|
||||
'error' => $error_message,
|
||||
'tmpl' => $app->input->get('tmpl')
|
||||
]));
|
||||
|
||||
$app->redirect('index.php?' . $optionsQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the export file does exist
|
||||
*
|
||||
* @param string $filename
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function exportFileExists($filename)
|
||||
{
|
||||
return \JFile::exists(File::getTempFolder() . $filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the Export popup to the page which can be triggered by toolbar buttons.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function renderModal()
|
||||
{
|
||||
\JHtml::script('com_convertforms/export.js', ['relative' => true, 'version' => 'auto']);
|
||||
|
||||
\JFactory::getDocument()->addScriptDeclaration('
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
new exportModal("'.\JFactory::getApplication()->input->get('view') . '");
|
||||
});
|
||||
');
|
||||
|
||||
$html = \JHtml::_('bootstrap.renderModal', 'cfExportSubmissions', [
|
||||
'backdrop' => 'static'
|
||||
]);
|
||||
|
||||
// This is the only way to add a custom CSS class to the popup container
|
||||
echo str_replace('modal hide', 'modal hide transparent', $html);
|
||||
}
|
||||
}
|
||||
470
administrator/components/com_convertforms/ConvertForms/Field.php
Normal file
470
administrator/components/com_convertforms/ConvertForms/Field.php
Normal file
@@ -0,0 +1,470 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms;
|
||||
|
||||
use Joomla\CMS\Filter\InputFilter;
|
||||
use Joomla\Registry\Registry;
|
||||
use ConvertForms\Helper;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
/**
|
||||
* Convert Forms Field Main Class
|
||||
*/
|
||||
class Field
|
||||
{
|
||||
/**
|
||||
* Field Object
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $field;
|
||||
|
||||
/**
|
||||
* The prefix name used for input names
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $namePrefix = 'cf';
|
||||
|
||||
/**
|
||||
* Filter user value before saving into the database. By default converts the input to a string; strips all HTML tags / attributes.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $filterInput = 'HTML';
|
||||
|
||||
/**
|
||||
* Exclude common fields from the form rendering
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $excludeFields;
|
||||
|
||||
/**
|
||||
* Data passed to layout rendering
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
private $layoutData;
|
||||
|
||||
/**
|
||||
* Indicates whether it accepts multiple values
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $multiple = false;
|
||||
|
||||
/**
|
||||
* Indicates the default required behavior on the form
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $required = true;
|
||||
|
||||
/**
|
||||
* The data submitted by the user in the form
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param mixed $field_options Object or Array Field options
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($field_options = null, $form_data = null)
|
||||
{
|
||||
$this->data = $form_data;
|
||||
|
||||
if ($field_options)
|
||||
{
|
||||
$field_options['required'] = isset($field_options['required']) ? $field_options['required'] : $this->required;
|
||||
|
||||
// We should better call $this->setField() here
|
||||
$this->field = new Registry($field_options);
|
||||
}
|
||||
|
||||
$this->app = \JFactory::getApplication();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set field object
|
||||
*
|
||||
* Rename to prepareField
|
||||
*
|
||||
* @param mixed $field Object or Array Field options
|
||||
*/
|
||||
public function setField($field)
|
||||
{
|
||||
$field = is_array($field) ? (object) $field : $field;
|
||||
|
||||
if (!isset($field->name) || empty($field->name))
|
||||
{
|
||||
$field->name = $this->getName() . '_' . $field->key;
|
||||
}
|
||||
|
||||
$field->input_id = 'form' . $field->namespace . '_' . Helper::makeHTMLAttributeSafe($field->name);
|
||||
$field->input_name = $this->namePrefix . '[' . $field->name . ']';
|
||||
|
||||
$field->htmlattributes = [];
|
||||
|
||||
$this->field = $field;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is bullshit. We should call setField() in the Field's Class constructor so we can access the field object correctly. Consider this as a temporary workround.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getField()
|
||||
{
|
||||
return get_class($this->field) == 'Joomla\Registry\Registry' ? $this->field->toObject() : $this->field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event fired during form saving in the backend to help us validate user options.
|
||||
*
|
||||
* @param object $model The Form Model
|
||||
* @param array $form_data The form data to be saved
|
||||
* @param array $field_options The field data
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function onBeforeFormSave($model, $form_data, &$field_options)
|
||||
{
|
||||
if (isset($field_options['name']) && $field_options['name'] == '')
|
||||
{
|
||||
$field_options['name'] = $field_options['type'] . '_' . $field_options['key'];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Discovers the actual field name from the called class
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getName()
|
||||
{
|
||||
$class_parts = explode('\\', get_called_class());
|
||||
return strtolower(end($class_parts));
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the field's input element
|
||||
*
|
||||
* @return string HTML output
|
||||
*/
|
||||
protected function getInput()
|
||||
{
|
||||
$layoutsPath = JPATH_ADMINISTRATOR . '/components/com_convertforms/layouts/fields/';
|
||||
|
||||
// Override layout path if it's available
|
||||
$layoutName = isset($this->inheritInputLayout) ? $this->inheritInputLayout : $this->getName();
|
||||
|
||||
// Check if an admininistrator layout is available
|
||||
if ($this->app->isClient('administrator') && \JFile::exists($layoutsPath . $layoutName . '_admin.php'))
|
||||
{
|
||||
$layoutName .= '_admin';
|
||||
}
|
||||
|
||||
// @todo - Pass only this class to payload as $field and change all layouts accordingly. Mind the backwards compatibility.
|
||||
return Helper::layoutRender('fields.' . $layoutName, $this->getInputData());
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the field's input layout data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getInputData()
|
||||
{
|
||||
return array(
|
||||
'class' => $this,
|
||||
'field' => $this->field,
|
||||
'form' => $this->field->form
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the Field's control group that will contain both input and label parts.
|
||||
*
|
||||
* @return string HTML Output
|
||||
*/
|
||||
public function getControlGroup()
|
||||
{
|
||||
\JPluginHelper::importPlugin('convertformstools');
|
||||
$this->app->triggerEvent('onConvertFormsFieldBeforeRender', [&$this->field, $this->field->form]);
|
||||
|
||||
$this->field->input = $this->getInput();
|
||||
|
||||
$layoutData = [
|
||||
'field' => $this->field,
|
||||
'form' => $this->field->form
|
||||
];
|
||||
|
||||
$html = Helper::layoutRender('controlgroup', $layoutData);
|
||||
|
||||
$this->app->triggerEvent('onConvertFormsFieldAfterRender', [&$html, $this->field, $this->field->form]);
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the Field's Options Form used in the backend
|
||||
*
|
||||
* @param string $formControl From control prefix
|
||||
* @param array $loadData Form data to bind
|
||||
*
|
||||
* @return string The final HTML output
|
||||
*/
|
||||
public function getOptionsForm($formControl = 'jform', $loadData = null)
|
||||
{
|
||||
// Setup the common form first
|
||||
$form = new \JForm('cf', array('control' => $formControl));
|
||||
|
||||
$form->addFieldPath(JPATH_COMPONENT_ADMINISTRATOR . '/models/forms/fields');
|
||||
$form->addFieldPath(JPATH_PLUGINS . '/system/nrframework/fields');
|
||||
$form->loadFile(JPATH_COMPONENT_ADMINISTRATOR . '/ConvertForms/xml/field.xml');
|
||||
|
||||
// Exclude Fields
|
||||
if (is_array($this->excludeFields))
|
||||
{
|
||||
$reservedFields = array(
|
||||
'key',
|
||||
'type'
|
||||
);
|
||||
|
||||
foreach ($form->getFieldSets() as $key => $fieldSetName)
|
||||
{
|
||||
$fields = $form->getFieldset($fieldSetName->name);
|
||||
|
||||
foreach ($fields as $key => $field)
|
||||
{
|
||||
// We can't exclude reserved fields
|
||||
if (in_array($field->fieldname, $reservedFields))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!in_array($field->fieldname, $this->excludeFields) &&
|
||||
!in_array('*', $this->excludeFields))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$form->removeField($field->fieldname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load field based options
|
||||
$form->loadFile(JPATH_COMPONENT_ADMINISTRATOR . '/ConvertForms/xml/field/' . $this->getName() . '.xml');
|
||||
|
||||
\JPluginHelper::importPlugin('convertformstools');
|
||||
$this->app->triggerEvent('onConvertFormsBackendRenderOptionsForm', [&$form, $this->getName()]);
|
||||
|
||||
// Bind Data
|
||||
$form->bind($loadData);
|
||||
|
||||
// Give individual field classes to manipulate the form before the render
|
||||
if (method_exists($this, 'onBeforeRenderOptionsForm'))
|
||||
{
|
||||
$this->onBeforeRenderOptionsForm($form);
|
||||
}
|
||||
|
||||
// Render Layout
|
||||
$data = array(
|
||||
'form' => $form,
|
||||
'header' => $this->getOptionsFormHeader(),
|
||||
'fieldTypeName' => $this->getName(),
|
||||
'loadData' => $loadData
|
||||
);
|
||||
|
||||
$html = Helper::layoutRender('optionsform', $data);
|
||||
|
||||
// Give individual field classes to manipulate the form after the render
|
||||
if (method_exists($this, 'onAfterRenderOptionsForm'))
|
||||
{
|
||||
$this->onAfterRenderOptionsForm($html);
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a text before the form options
|
||||
*
|
||||
* @return string The text to display
|
||||
*/
|
||||
protected function getOptionsFormHeader()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate form submitted value
|
||||
*
|
||||
* @param mixed $value The field's value to validate (Passed by reference)
|
||||
*
|
||||
* @return mixed True on success, throws an exception on error
|
||||
*/
|
||||
public function validate(&$value)
|
||||
{
|
||||
$isEmpty = $this->isEmpty($value);
|
||||
$isRequired = $this->field->get('required');
|
||||
|
||||
if ($isEmpty && $isRequired)
|
||||
{
|
||||
$this->throwError(\JText::_('COM_CONVERTFORMS_FIELD_REQUIRED'), $field_options);
|
||||
}
|
||||
|
||||
// Let's do some filtering.
|
||||
$value = $this->filterInput($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if submitted value is empty
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isEmpty($value)
|
||||
{
|
||||
if (is_array($value) && count($value) == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Note: Do not use empty() as evaluates '0' as true which is a valid value for the Number field.
|
||||
if ($value == '' || is_null($value))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter user input
|
||||
*
|
||||
* @param mixed $input User input value
|
||||
*
|
||||
* @return mixed The filtered user input
|
||||
*/
|
||||
public function filterInput($input)
|
||||
{
|
||||
$filter = $this->field->get('filter', $this->filterInput);
|
||||
|
||||
// Safehtml is a special filter and we need to initialize InputFilter class differently
|
||||
if ($filter == 'safehtml')
|
||||
{
|
||||
return InputFilter::getInstance([], [], 1, 1)->clean($input, 'html');
|
||||
}
|
||||
|
||||
return InputFilter::getInstance()->clean($input, $filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the field's label which is also parsed for Smart Tags
|
||||
*/
|
||||
public function getLabel()
|
||||
{
|
||||
if ($label = $this->field->get('label'))
|
||||
{
|
||||
return \NRFramework\SmartTags::getInstance()->replace($label);
|
||||
}
|
||||
|
||||
// In case the Label option is empty, use the name of the field.
|
||||
return $this->field->get('name');
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw an error exception
|
||||
*
|
||||
* @param [type] $message [description]
|
||||
*
|
||||
* @return [type] [description]
|
||||
*/
|
||||
public function throwError($message)
|
||||
{
|
||||
if (!$label = $this->getLabel())
|
||||
{
|
||||
$label = $this->field->get('name', ucfirst($this->field->get('type')));
|
||||
}
|
||||
|
||||
throw new \Exception($label . ': ' . \JText::_($message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare value to be displayed to the user as plain text
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function prepareValue($value)
|
||||
{
|
||||
if (is_bool($value))
|
||||
{
|
||||
return $value ? '1' : '0';
|
||||
}
|
||||
|
||||
if (is_array($value))
|
||||
{
|
||||
return implode(', ', $value);
|
||||
}
|
||||
|
||||
// Strings and numbers
|
||||
return Helper::escape($value);
|
||||
}
|
||||
|
||||
public function prepareValueHTML($value)
|
||||
{
|
||||
return $this->prepareValue($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a field object to Widget
|
||||
*
|
||||
* @param array $widget_options The widget options
|
||||
*
|
||||
* @return string The widget final layout
|
||||
*/
|
||||
public function toWidget($widget_options = [])
|
||||
{
|
||||
$default_options = [
|
||||
'id' => $this->field->input_id,
|
||||
'name' => $this->field->input_name,
|
||||
'readonly' => isset($this->field->readonly) ? (bool) $this->field->readonly : false,
|
||||
'value' => isset($this->field->value) ? (string) $this->field->value : null,
|
||||
'required' => isset($this->field->required) ? (bool) $this->field->required : false,
|
||||
'placeholder' => isset($this->field->placeholder) ? $this->field->placeholder : '',
|
||||
'load_css_vars' => false,
|
||||
'input_class' => 'cf-input',
|
||||
];
|
||||
|
||||
$widget_options = array_merge($default_options, $widget_options);
|
||||
|
||||
return \NRFramework\Widgets\Helper::render($this->getName(), $widget_options);
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2022 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms\Field;
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use NRFramework\Executer;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
class Captcha extends Text
|
||||
{
|
||||
/**
|
||||
* Exclude all common fields
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $excludeFields = [
|
||||
'name',
|
||||
'required',
|
||||
'size',
|
||||
'value',
|
||||
'placeholder',
|
||||
'browserautocomplete',
|
||||
'inputcssclass'
|
||||
];
|
||||
|
||||
/**
|
||||
* This is bullshit. We should call setField() in the Field's Class constructor so we can access the field's ID correctly. Consider this as a workround.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getSessionNamespace()
|
||||
{
|
||||
return 'cf.' . $this->getField()->key . '.captcha';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set field object
|
||||
*
|
||||
* @param mixed $field Object or Array Field options
|
||||
*/
|
||||
public function setField($field)
|
||||
{
|
||||
parent::setField($field);
|
||||
|
||||
// Once we start calling $this->setField() in the constructo, we can get rid of this line.
|
||||
$this->field->required = true;
|
||||
|
||||
$complexity = isset($this->field->complexity) ? $this->field->complexity : '';
|
||||
|
||||
switch ($complexity)
|
||||
{
|
||||
case 'high':
|
||||
$min = 1;
|
||||
$max = 30;
|
||||
$comparators = ['+', '-', '*'];
|
||||
break;
|
||||
|
||||
case 'medium':
|
||||
$min = 1;
|
||||
$max = 20;
|
||||
$comparators = ['+', '-'];
|
||||
break;
|
||||
|
||||
// low
|
||||
default:
|
||||
$min = 1;
|
||||
$max = 10;
|
||||
$comparators = ['+'];
|
||||
}
|
||||
|
||||
// Pick random numbers
|
||||
$number1 = rand($min, $max);
|
||||
$number2 = rand($min, $max);
|
||||
|
||||
// Pick a random math comparison operator
|
||||
shuffle($comparators);
|
||||
$comparator = end($comparators);
|
||||
|
||||
// Calculate the Captcha answer
|
||||
$equation = "return ($number1 $comparator $number2)";
|
||||
$executer = new Executer($equation);
|
||||
$answer = $executer->run();
|
||||
|
||||
// Store the Captcha answer in the Session object.
|
||||
Factory::getSession()->set($this->getSessionNamespace(), $answer);
|
||||
|
||||
// Pass data to template
|
||||
$this->field->question = [
|
||||
'number1' => $number1,
|
||||
'number2' => $number2,
|
||||
'comparator' => $comparator,
|
||||
];;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate field value
|
||||
*
|
||||
* @param mixed $value The field's value to validate
|
||||
*
|
||||
* @return mixed True on success, throws an exception on error
|
||||
*/
|
||||
public function validate(&$value)
|
||||
{
|
||||
// In case this is a submission via URL, skip the check.
|
||||
if (Factory::getApplication()->input->get('task') == 'optin')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
$math_solution = (string) Factory::getSession()->get($this->getSessionNamespace());
|
||||
|
||||
$field = $this->getField();
|
||||
|
||||
// Once we start calling $this->setField() in the constructor we can easily find the field's name by using $this->field->name instead of relying on the submitted data.
|
||||
$user_solution = (string) $this->data['captcha_' . $field->key];
|
||||
|
||||
// In v3.2.9 we added an option to set the Wrong Answer Text in the Field Settings. In the previous version we were using a language strings instead.
|
||||
// To prevnt breaking the user's form, we need to check whether the new option is available. Otherwise we fallback to the old language string.
|
||||
// We can get rid of compatibility check in a few months.
|
||||
$wrong_answer_text = isset($field->wrong_answer_text) && !empty($field->wrong_answer_text) ? $field->wrong_answer_text : \JText::_('COM_CONVERTFORMS_FIELD_CAPTCHA_WRONG_ANSWER');
|
||||
|
||||
if ($math_solution !== $user_solution)
|
||||
{
|
||||
$this->throwError($wrong_answer_text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms\Field;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
class Checkbox extends \ConvertForms\FieldChoice
|
||||
{
|
||||
/**
|
||||
* Indicates whether it accepts multiple values
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $multiple = true;
|
||||
|
||||
/**
|
||||
* Remove common fields from the form rendering
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $excludeFields = array(
|
||||
'placeholder',
|
||||
'browserautocomplete',
|
||||
'size',
|
||||
);
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms\Field;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
class Divider extends \ConvertForms\Field
|
||||
{
|
||||
/**
|
||||
* Remove common fields from the form rendering
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $excludeFields = [
|
||||
'name',
|
||||
'placeholder',
|
||||
'browserautocomplete',
|
||||
'size',
|
||||
'required',
|
||||
'label',
|
||||
'description',
|
||||
'cssclass',
|
||||
'hidelabel',
|
||||
'inputcssclass',
|
||||
'value'
|
||||
];
|
||||
|
||||
/**
|
||||
* Indicates the default required behavior on the form
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $required = false;
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms\Field;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
class Dropdown extends \ConvertForms\FieldChoice
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2021 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms\Field;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
class Editor extends Textarea
|
||||
{
|
||||
/**
|
||||
* Remove common fields from the form rendering
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $excludeFields = [
|
||||
'placeholder',
|
||||
'browserautocomplete',
|
||||
'size',
|
||||
];
|
||||
|
||||
/**
|
||||
* Event fired before the field options form is rendered in the backend
|
||||
*
|
||||
* @param object $form
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function onAfterRenderOptionsForm(&$html)
|
||||
{
|
||||
// Remove the 'None' editor from dropdown options
|
||||
$html = str_replace('<option value="none">Editor - None</option>', '', $html);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the field's input element
|
||||
*
|
||||
* @return string HTML output
|
||||
*/
|
||||
protected function getInput()
|
||||
{
|
||||
$selected_editor = empty($this->field->editor) ? \JFactory::getConfig()->get('editor') : $this->field->editor;
|
||||
|
||||
if (!$selected_editor)
|
||||
{
|
||||
return \JText::sprintf('COM_CONVERTFORMS_EDITOR_NOT_FOUND', $selected_editor);
|
||||
}
|
||||
|
||||
// Instantiate the editor
|
||||
$editor = \Joomla\CMS\Editor\Editor::getInstance($selected_editor);
|
||||
|
||||
$id = $this->field->input_id;
|
||||
$name = $this->field->input_name;
|
||||
$contents = htmlspecialchars($this->field->value, ENT_COMPAT, 'UTF-8');
|
||||
$width = '100%';
|
||||
$height = (int) $this->field->height;
|
||||
$row = 1;
|
||||
$col = 10;
|
||||
$buttons = false;
|
||||
$author = null;
|
||||
$asset = null;
|
||||
$params = [
|
||||
'readonly' => $this->field->readonly,
|
||||
];
|
||||
|
||||
$this->field->richeditor = $editor->display($name, $contents, $width, $height, $col, $row, $buttons, $id, $author, $asset, $params);
|
||||
|
||||
return parent::getInput();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the HTML version of the submitted value.
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function prepareValueHTML($value)
|
||||
{
|
||||
// Editor's value is already in HTML format in the database
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms\Field;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
use ConvertForms\Validate;
|
||||
|
||||
class Email extends \ConvertForms\Field
|
||||
{
|
||||
protected $inheritInputLayout = 'text';
|
||||
|
||||
/**
|
||||
* Validate field value
|
||||
*
|
||||
* @param mixed $value The field's value to validate
|
||||
*
|
||||
* @return mixed True on success, throws an exception on error
|
||||
*/
|
||||
public function validate(&$value)
|
||||
{
|
||||
parent::validate($value);
|
||||
|
||||
if ($this->isEmpty($value))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Validate::email($value) || $this->field->get('dnscheck') && !Validate::emaildns($value))
|
||||
{
|
||||
$this->throwError(\JText::sprintf('COM_CONVERTFORMS_FIELD_EMAIL_INVALID'), $this->field);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare value to be displayed to the user as HTML/text
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function prepareValueHTML($value)
|
||||
{
|
||||
if (!$value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
return '<a target="_blank" href="mailto:' . $value . '">' . $value . '</a>';
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2018 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms\Field;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
class EmptySpace extends \ConvertForms\Field
|
||||
{
|
||||
/**
|
||||
* Indicates the default required behavior on the form
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $required = false;
|
||||
|
||||
/**
|
||||
* Remove common fields from the form rendering
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $excludeFields = [
|
||||
'name',
|
||||
'placeholder',
|
||||
'browserautocomplete',
|
||||
'size',
|
||||
'required',
|
||||
'label',
|
||||
'description',
|
||||
'cssclass',
|
||||
'hidelabel',
|
||||
'inputcssclass',
|
||||
'value'
|
||||
];
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,361 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms\Field;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
jimport('joomla.filesystem.file');
|
||||
jimport('joomla.filesystem.folder');
|
||||
jimport('joomla.filesystem.path');
|
||||
|
||||
use ConvertForms\Helper;
|
||||
use ConvertForms\Validate;
|
||||
use NRFramework\File;
|
||||
use Joomla\Registry\Registry;
|
||||
|
||||
class FileUpload extends \ConvertForms\Field
|
||||
{
|
||||
/**
|
||||
* The default upload folder
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $default_upload_folder = '/media/com_convertforms/uploads/{randomid}_{file.basename}';
|
||||
|
||||
/**
|
||||
* Remove common fields from the form rendering
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $excludeFields = [
|
||||
'size',
|
||||
'value',
|
||||
'browserautocomplete',
|
||||
'placeholder',
|
||||
'inputcssclass'
|
||||
];
|
||||
|
||||
/**
|
||||
* Set field object
|
||||
*
|
||||
* @param mixed $field Object or Array Field options
|
||||
*/
|
||||
public function setField($field)
|
||||
{
|
||||
parent::setField($field);
|
||||
|
||||
$field = $this->field;
|
||||
|
||||
if (!isset($field->limit_files))
|
||||
{
|
||||
$field->limit_files = 1;
|
||||
}
|
||||
|
||||
if (!isset($field->upload_types) || empty($field->upload_types))
|
||||
{
|
||||
$field->upload_types = 'image/*';
|
||||
}
|
||||
|
||||
// Accept multiple values
|
||||
if ((int) $field->limit_files != 1)
|
||||
{
|
||||
$field->input_name .= '[]';
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate field value
|
||||
*
|
||||
* @param mixed $value The field's value to validate
|
||||
*
|
||||
* @return mixed True on success, throws an exception on error
|
||||
*/
|
||||
public function validate(&$value)
|
||||
{
|
||||
$is_required = $this->field->get('required');
|
||||
$max_files_allowed = $this->field->get('limit_files', 1);
|
||||
$allowed_types = $this->field->get('upload_types');
|
||||
$upload_folder = $this->field->get('upload_folder_type', 'auto') == 'auto' ? $this->default_upload_folder : $this->field->get('upload_folder', $this->default_upload_folder);
|
||||
|
||||
// Remove null and empty values
|
||||
$value = is_array($value) ? $value : (array) $value;
|
||||
$value = array_filter($value);
|
||||
|
||||
// We expect a not empty array
|
||||
if ($is_required && empty($value))
|
||||
{
|
||||
$this->throwError(\JText::_('COM_CONVERTFORMS_FIELD_REQUIRED'));
|
||||
}
|
||||
|
||||
// Do we have the correct number of files?
|
||||
if ($max_files_allowed > 0 && count($value) > $max_files_allowed)
|
||||
{
|
||||
$this->throwError(\JText::sprintf('COM_CONVERTFORMS_UPLOAD_MAX_FILES_LIMIT', $max_files_allowed));
|
||||
}
|
||||
|
||||
// Validate file paths
|
||||
foreach ($value as $key => &$source_file)
|
||||
{
|
||||
$source_file = base64_decode($source_file);
|
||||
$source_file_info = File::pathinfo($source_file);
|
||||
$source_basename = $source_file_info['basename'];
|
||||
|
||||
if (!\JFile::exists($source_file))
|
||||
{
|
||||
$this->throwError(\JText::sprintf('COM_CONVERTFORMS_UPLOAD_FILE_IS_MISSING', $source_basename));
|
||||
}
|
||||
|
||||
// Although the file is already checked during upload, make another sanity check here.
|
||||
File::checkMimeOrDie($allowed_types, ['tmp_name' => $source_file]);
|
||||
|
||||
// Remove the random ID added to file's name during upload process
|
||||
$source_file_info['filename'] = preg_replace('#cf_(.*?)_(.*?)#', '', $source_file_info['filename']);
|
||||
$source_file_info['basename'] = preg_replace('#cf_(.*?)_(.*?)#', '', $source_basename);
|
||||
$source_file_info['index'] = $key + 1;
|
||||
|
||||
// Replace Smart Tags in the upload folder value
|
||||
// Unfortunately at this time, we don't have submitted data available yet and so, we can't replace field-based Smart Tags. See @todo below.
|
||||
$SmartTags = new \NRFramework\SmartTags();
|
||||
$SmartTags->add($source_file_info, 'file.');
|
||||
$destination_file = JPATH_ROOT . DIRECTORY_SEPARATOR . $SmartTags->replace($upload_folder);
|
||||
|
||||
// Validate destination file
|
||||
$destination_file_info = File::pathinfo($destination_file);
|
||||
if (!isset($destination_file_info['extension']))
|
||||
{
|
||||
$destination_file = implode(DIRECTORY_SEPARATOR, [$destination_file_info['dirname'], $destination_file_info['basename'], $source_basename]);
|
||||
}
|
||||
|
||||
// Move uploaded file to the destination folder after the form passes all validation checks.
|
||||
// Thus, if an error is triggered by another field, the file will remain in the temp folder and the user will be able to re-submit the form.
|
||||
$this->app->registerEvent('onConvertFormsSubmissionBeforeSave', function(&$data) use ($key, $source_file, $destination_file)
|
||||
{
|
||||
try
|
||||
{
|
||||
// get the data
|
||||
$tmpData = $data;
|
||||
if (defined('nrJ4'))
|
||||
{
|
||||
$tmpData = $data->getArgument('0');
|
||||
}
|
||||
|
||||
// This is a temporary workaround to support field-based Smart Tags in the upload folder
|
||||
// @todo 1: We need to prepare $data with ConvertFormsModelConversion->prepare() and pass it down to Submission::replaceSmartTags() in order for submitted values to be prepared with each field prepare() method.
|
||||
// @todo 2: Do Smart Tags replacement once. Merge previous replacement for file Smart Tags with this one.
|
||||
$SmartTags = new \NRFramework\SmartTags();
|
||||
$SmartTags->add($tmpData['params'], 'field.');
|
||||
$destination_file = $SmartTags->replace($destination_file);
|
||||
|
||||
// Move uploaded file from the temp folder to the destination folder.
|
||||
$destination_file = File::move($source_file, $destination_file);
|
||||
|
||||
// Give a chance to manipulate the file with a hook.
|
||||
// We can move the file to another folder, rename it, resize it or even uploaded it to a cloud storage service.
|
||||
$this->app->triggerEvent('onConvertFormsFileUpload', [&$destination_file, $tmpData]);
|
||||
|
||||
// Always save the relative path to the database.
|
||||
$destination_file = Helper::pathTorelative($destination_file);
|
||||
|
||||
// Update fields' value
|
||||
$tmpData['params'][$this->field->get('name')][$key] = $destination_file;
|
||||
|
||||
// Set back the new value to $data object
|
||||
if (defined('nrJ4'))
|
||||
{
|
||||
$data->setArgument(0, $tmpData);
|
||||
}
|
||||
else
|
||||
{
|
||||
$data = $tmpData;
|
||||
}
|
||||
|
||||
} catch (\Throwable $th)
|
||||
{
|
||||
$this->throwError($th->getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event fired before the field options form is rendered in the backend
|
||||
*
|
||||
* @param object $form
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function onBeforeRenderOptionsForm($form)
|
||||
{
|
||||
// Set the maximum upload size limit to the respective options form field
|
||||
$max_upload_size_str = \JHtml::_('number.bytes', \JUtility::getMaxUploadSize());
|
||||
$max_upload_size_int = (int) $max_upload_size_str;
|
||||
|
||||
$form->setFieldAttribute('max_file_size', 'max', $max_upload_size_int);
|
||||
|
||||
$desc_lang_str = $form->getFieldAttribute('max_file_size', 'description');
|
||||
$desc = \JText::sprintf($desc_lang_str, $max_upload_size_str);
|
||||
$form->setFieldAttribute('max_file_size', 'description', $desc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax method triggered by System Plugin during file upload.
|
||||
*
|
||||
* @param string $form_id
|
||||
* @param string $field_key
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function onAjax($form_id, $field_key)
|
||||
{
|
||||
// Make sure we have a valid form and a field key
|
||||
if (!$form_id || !$field_key)
|
||||
{
|
||||
$this->uploadDie('COM_CONVERTFORMS_UPLOAD_ERROR');
|
||||
}
|
||||
|
||||
// Get field settings
|
||||
if (!$upload_field_settings = \ConvertForms\Form::getFieldSettingsByKey($form_id, $field_key))
|
||||
{
|
||||
$this->uploadDie('COM_CONVERTFORMS_UPLOAD_ERROR_INVALID_FIELD');
|
||||
}
|
||||
|
||||
$allow_unsafe = $upload_field_settings->get('allow_unsafe', false);
|
||||
|
||||
// Make sure we have a valid file passed
|
||||
if (!$file = $this->app->input->files->get('file', null, ($allow_unsafe ? 'raw' : 'cmd')))
|
||||
{
|
||||
$this->uploadDie('COM_CONVERTFORMS_UPLOAD_ERROR_INVALID_FILE');
|
||||
}
|
||||
|
||||
// In case we allow multiple uploads the file parameter is a 2 levels array.
|
||||
$first_property = array_pop($file);
|
||||
if (is_array($first_property))
|
||||
{
|
||||
$file = $first_property;
|
||||
}
|
||||
|
||||
// Upload temporarily to the default upload folder
|
||||
$allowed_types = $upload_field_settings->get('upload_types');
|
||||
|
||||
try {
|
||||
$uploaded_filename = File::upload($file, null, $allowed_types, $allow_unsafe, 'cf_');
|
||||
|
||||
return [
|
||||
// Since the path of the uploaded file will be included in the form's POST data, obfuscate the path for security reasons.
|
||||
// This will also prevent Akeeba Admin Tools DFIShield from mistakenly blocking File Uploads.
|
||||
'file' => base64_encode($uploaded_filename),
|
||||
];
|
||||
} catch (\Throwable $th)
|
||||
{
|
||||
$this->uploadDie($th->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DropzoneJS detects errors based on the response error code.
|
||||
*
|
||||
* @param string $error_message
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function uploadDie($error_message)
|
||||
{
|
||||
http_response_code('500');
|
||||
die(\JText::_($error_message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare value to be displayed to the user as plain text
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function prepareValue($value)
|
||||
{
|
||||
if (!$value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$value = (array) $value;
|
||||
|
||||
foreach ($value as &$link)
|
||||
{
|
||||
$link = Helper::absURL($link);
|
||||
}
|
||||
|
||||
return implode(', ', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare value to be displayed to the user as HTML/text
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function prepareValueHTML($value)
|
||||
{
|
||||
if (!$value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$links = (array) $value;
|
||||
$value = '';
|
||||
|
||||
foreach ($links as $link)
|
||||
{
|
||||
$link = Helper::absURL($link);
|
||||
$value .= '<div><a download href="' . $link . '">' . File::pathinfo($link)['basename'] . '</a></div>';
|
||||
}
|
||||
|
||||
return '<div class="cf-links">' . $value . '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a text before the form options
|
||||
*
|
||||
* @return string The text to display
|
||||
*/
|
||||
protected function getOptionsFormHeader()
|
||||
{
|
||||
$html = '';
|
||||
|
||||
$temp_folder = File::getTempFolder();
|
||||
|
||||
if (!@is_writable($temp_folder))
|
||||
{
|
||||
$html .= '
|
||||
<div class="alert alert-danger">
|
||||
' . \JText::sprintf('COM_CONVERTFORMS_FILEUPLOAD_TEMP_FOLDER_NOT_WRITABLE', $temp_folder) . '
|
||||
</div>
|
||||
';
|
||||
}
|
||||
|
||||
// Check if the Fileinfo PHP extension is installed required to detect the mime type.
|
||||
if (!extension_loaded('fileinfo') || !function_exists('mime_content_type'))
|
||||
{
|
||||
$html .= '
|
||||
<div class="alert alert-danger">
|
||||
' . \JText::sprintf('COM_CONVERTFORMS_FILEUPLOAD_MIME_CONTENT_TYPE_MISSING') . '
|
||||
</div>
|
||||
';
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms\Field;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
use \ConvertForms\Helper;
|
||||
|
||||
class Hcaptcha extends \ConvertForms\Field
|
||||
{
|
||||
/**
|
||||
* Exclude all common fields
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $excludeFields = array(
|
||||
'name',
|
||||
'required',
|
||||
'size',
|
||||
'value',
|
||||
'placeholder',
|
||||
'browserautocomplete',
|
||||
'inputcssclass'
|
||||
);
|
||||
|
||||
/**
|
||||
* Set field object
|
||||
*
|
||||
* @param mixed $field Object or Array Field options
|
||||
*/
|
||||
public function setField($field)
|
||||
{
|
||||
parent::setField($field);
|
||||
|
||||
$this->field->required = true;
|
||||
|
||||
// When captcha is in invisible mode, don't show the control group
|
||||
if ($this->field->hcaptcha_type == 'invisible')
|
||||
{
|
||||
$this->field->cssclass .= 'hide';
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the hCaptcha Site Key used in Javascript code
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSiteKey()
|
||||
{
|
||||
return Helper::getComponentParams()->get('hcaptcha_sitekey');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the hCaptcha Secret Key used in communication between the website and the hCaptcha server
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSecretKey()
|
||||
{
|
||||
return Helper::getComponentParams()->get('hcaptcha_secretkey');
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate field value
|
||||
*
|
||||
* @param mixed $value The field's value to validate
|
||||
*
|
||||
* @return mixed True on success, throws an exception on error
|
||||
*/
|
||||
public function validate(&$value)
|
||||
{
|
||||
if (!$this->field->get('required'))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// In case this is a submission via URL, skip the check.
|
||||
if (\JFactory::getApplication()->input->get('task') == 'optin')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
$hcaptcha = new \NRFramework\Integrations\HCaptcha(
|
||||
['secret' => $this->getSecretKey()]
|
||||
);
|
||||
|
||||
$response = isset($this->data['h-captcha-response']) ? $this->data['h-captcha-response'] : null;
|
||||
|
||||
$hcaptcha->validate($response);
|
||||
|
||||
if (!$hcaptcha->success())
|
||||
{
|
||||
throw new \Exception($hcaptcha->getLastError());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a text before the form options
|
||||
*
|
||||
* @return string The text to display
|
||||
*/
|
||||
protected function getOptionsFormHeader()
|
||||
{
|
||||
if ($this->getSiteKey() && $this->getSecretKey())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$url = \JURI::base() . 'index.php?option=com_config&view=component&component=com_convertforms#hcaptcha';
|
||||
|
||||
return
|
||||
\JText::_('COM_CONVERTFORMS_FIELD_RECAPTCHA_KEYS_NOTE') .
|
||||
' <a onclick=\'window.open("' . $url . '", "cfrecaptcha", "width=1000, height=750");\' href="#">'
|
||||
. \JText::_("COM_CONVERTFORMS_FIELD_HCAPTCHA_CONFIGURE") .
|
||||
'</a>.';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2018 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms\Field;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
class Heading extends \ConvertForms\Field
|
||||
{
|
||||
/**
|
||||
* Indicates the default required behavior on the form
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $required = false;
|
||||
|
||||
/**
|
||||
* Remove common fields from the form rendering
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $excludeFields = array(
|
||||
'name',
|
||||
'placeholder',
|
||||
'browserautocomplete',
|
||||
'size',
|
||||
'required',
|
||||
'hidelabel',
|
||||
'inputcssclass',
|
||||
'value'
|
||||
);
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms\Field;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
class Hidden extends \ConvertForms\Field
|
||||
{
|
||||
/**
|
||||
* Remove common fields from the form rendering
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $excludeFields = [
|
||||
'label',
|
||||
'placeholder',
|
||||
'description',
|
||||
'cssclass',
|
||||
'hidelabel',
|
||||
'browserautocomplete',
|
||||
'size',
|
||||
'inputcssclass'
|
||||
];
|
||||
|
||||
/**
|
||||
* Indicates the default required behavior on the form
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $required = false;
|
||||
|
||||
/**
|
||||
* Set field object
|
||||
*
|
||||
* @param mixed $field Object or Array Field options
|
||||
*/
|
||||
public function setField($field)
|
||||
{
|
||||
parent::setField($field);
|
||||
|
||||
// Always hide the container of a hidden field
|
||||
$this->field->cssclass = 'cf-hide';
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms\Field;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
class Number extends \ConvertForms\Field
|
||||
{
|
||||
/**
|
||||
* Filter user value before saving into the database
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $filterInput = 'FLOAT';
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms\Field;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
use ConvertForms\Validate;
|
||||
|
||||
class Password extends \ConvertForms\Field
|
||||
{
|
||||
protected $inheritInputLayout = 'text';
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms\Field;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
class Radio extends \ConvertForms\FieldChoice
|
||||
{
|
||||
/**
|
||||
* Remove common fields from the form rendering
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $excludeFields = array(
|
||||
'placeholder',
|
||||
'browserautocomplete',
|
||||
'size',
|
||||
);
|
||||
|
||||
/**
|
||||
* Radio buttons expect single text value. So we need to transform the submitted array data to string.
|
||||
*
|
||||
* @param mixed $input User input value
|
||||
*
|
||||
* @return mixed The filtered user input
|
||||
*/
|
||||
public function filterInput($input)
|
||||
{
|
||||
$value = parent::filterInput($input);
|
||||
|
||||
if (is_array($value) && isset($value[0]))
|
||||
{
|
||||
return $value[0];
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms\Field;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
use \ConvertForms\Helper;
|
||||
|
||||
class Recaptcha extends \ConvertForms\Field
|
||||
{
|
||||
/**
|
||||
* Exclude all common fields
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $excludeFields = array(
|
||||
'name',
|
||||
'required',
|
||||
'size',
|
||||
'value',
|
||||
'placeholder',
|
||||
'browserautocomplete',
|
||||
'inputcssclass'
|
||||
);
|
||||
|
||||
/**
|
||||
* Set field object
|
||||
*
|
||||
* @param mixed $field Object or Array Field options
|
||||
*/
|
||||
public function setField($field)
|
||||
{
|
||||
parent::setField($field);
|
||||
|
||||
$this->field->required = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reCAPTCHA Site Key used in Javascript code
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSiteKey()
|
||||
{
|
||||
return Helper::getComponentParams()->get('recaptcha_sitekey');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reCAPTCHA Secret Key used in communication between the website and the reCAPTCHA server
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSecretKey()
|
||||
{
|
||||
return Helper::getComponentParams()->get('recaptcha_secretkey');
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate field value
|
||||
*
|
||||
* @param mixed $value The field's value to validate
|
||||
*
|
||||
* @return mixed True on success, throws an exception on error
|
||||
*/
|
||||
public function validate(&$value)
|
||||
{
|
||||
if (!$this->field->get('required'))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// In case this is a submission via URL, skip the check.
|
||||
if (\JFactory::getApplication()->input->get('task') == 'optin')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
jimport('recaptcha', JPATH_PLUGINS . '/system/nrframework/helpers/wrappers');
|
||||
|
||||
$recaptcha = new \NR_ReCaptcha(
|
||||
['secret' => $this->getSecretKey()]
|
||||
);
|
||||
|
||||
$response = isset($this->data['g-recaptcha-response']) ? $this->data['g-recaptcha-response'] : null;
|
||||
|
||||
$recaptcha->validate($response);
|
||||
|
||||
if (!$recaptcha->success())
|
||||
{
|
||||
throw new \Exception($recaptcha->getLastError());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a text before the form options
|
||||
*
|
||||
* @return string The text to display
|
||||
*/
|
||||
protected function getOptionsFormHeader()
|
||||
{
|
||||
if ($this->getSiteKey() && $this->getSecretKey())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$url = \JURI::base() . 'index.php?option=com_config&view=component&component=com_convertforms#recaptcha';
|
||||
|
||||
return
|
||||
\JText::_('COM_CONVERTFORMS_FIELD_RECAPTCHA_KEYS_NOTE') .
|
||||
' <a onclick=\'window.open("' . $url . '", "cfrecaptcha", "width=1000, height=750");\' href="#">'
|
||||
. \JText::_("COM_CONVERTFORMS_FIELD_RECAPTCHA_CONFIGURE") .
|
||||
'</a>.';
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms\Field;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
use \ConvertForms\Helper;
|
||||
|
||||
class RecaptchaV2Invisible extends \ConvertForms\Field\Recaptcha
|
||||
{
|
||||
/**
|
||||
* Set field object
|
||||
*
|
||||
* @param mixed $field Object or Array Field options
|
||||
*/
|
||||
public function setField($field)
|
||||
{
|
||||
parent::setField($field);
|
||||
|
||||
// When the widget is shown at the bottom right or left position, we need to remove the .control-group's div default padding.
|
||||
if ($this->field->badge != 'inline')
|
||||
{
|
||||
$this->field->cssclass .= 'cf-no-padding';
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reCAPTCHA Site Key used in Javascript code
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSiteKey()
|
||||
{
|
||||
return Helper::getComponentParams()->get('recaptcha_sitekey_invs');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reCAPTCHA Secret Key used in communication between the website and the reCAPTCHA server
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSecretKey()
|
||||
{
|
||||
return Helper::getComponentParams()->get('recaptcha_secretkey_invs');
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms\Field;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
class Submit extends \ConvertForms\Field
|
||||
{
|
||||
/**
|
||||
* Remove common fields from the form rendering
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $excludeFields = array(
|
||||
'name',
|
||||
'description',
|
||||
'label',
|
||||
'placeholder',
|
||||
'required',
|
||||
'hidelabel',
|
||||
'browserautocomplete',
|
||||
'value',
|
||||
);
|
||||
|
||||
/**
|
||||
* Indicates the default required behavior on the form
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $required = false;
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms\Field;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
class Tel extends \ConvertForms\Field
|
||||
{
|
||||
protected $inheritInputLayout = 'text';
|
||||
|
||||
/**
|
||||
* Prepare value to be displayed to the user as HTML/text
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function prepareValueHTML($value)
|
||||
{
|
||||
if (!$value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
return '<a href="tel:' . $value . '">' . $value . '</a>';
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms\Field;
|
||||
|
||||
use Joomla\String\StringHelper;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
class Text extends \ConvertForms\Field
|
||||
{
|
||||
|
||||
/**
|
||||
* Validate form submitted value
|
||||
*
|
||||
* @param mixed $value The field's value to validate (Passed by reference)
|
||||
*
|
||||
* @return mixed True on success, throws an exception on error
|
||||
*/
|
||||
public function validate(&$value)
|
||||
{
|
||||
$isEmpty = $this->isEmpty($value);
|
||||
$isRequired = $this->field->get('required');
|
||||
|
||||
// If the field is empty and its not required, skip validation
|
||||
if ($isEmpty && !$isRequired)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ($isEmpty && $isRequired)
|
||||
{
|
||||
$this->throwError(\JText::_('COM_CONVERTFORMS_FIELD_REQUIRED'), $field_options);
|
||||
}
|
||||
|
||||
// Validate Min / Max characters
|
||||
$min_chars = $this->field->get('minchars', 0);
|
||||
$max_chars = $this->field->get('maxchars', 0);
|
||||
$value_length = StringHelper::strlen($value);
|
||||
|
||||
if ($min_chars > 0 && $value_length < $min_chars)
|
||||
{
|
||||
$this->throwError(\JText::sprintf('COM_CONVERTFORMS_FIELD_VALIDATION_MIN_CHARS', $min_chars, $value_length), $field_options);
|
||||
}
|
||||
|
||||
if ($max_chars > 0 && $value_length > $max_chars)
|
||||
{
|
||||
$this->throwError(\JText::sprintf('COM_CONVERTFORMS_FIELD_VALIDATION_MAX_CHARS', $max_chars, $value_length), $field_options);
|
||||
}
|
||||
|
||||
// Validate Min / Max words
|
||||
$min_words = $this->field->get('minwords', 0);
|
||||
$max_words = $this->field->get('maxwords', 0);
|
||||
|
||||
// Find words count
|
||||
$words_temp = preg_replace('/\s+/', ' ', trim($value));
|
||||
$words_temp = array_filter(explode(' ', $words_temp));
|
||||
$words_count = count($words_temp);
|
||||
|
||||
if ($min_words > 0 && $words_count < $min_words)
|
||||
{
|
||||
$this->throwError(\JText::sprintf('COM_CONVERTFORMS_FIELD_VALIDATION_MIN_WORDS', $min_words, $words_count), $field_options);
|
||||
}
|
||||
|
||||
if ($max_words > 0 && $words_count > $max_words)
|
||||
{
|
||||
$this->throwError(\JText::sprintf('COM_CONVERTFORMS_FIELD_VALIDATION_MAX_WORDS', $max_words, $words_count), $field_options);
|
||||
}
|
||||
|
||||
// Let's do some filtering.
|
||||
$value = $this->filterInput($value);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms\Field;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
class Textarea extends \ConvertForms\Field\Text
|
||||
{
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms\Field;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
use ConvertForms\Validate;
|
||||
|
||||
class Url extends \ConvertForms\Field
|
||||
{
|
||||
protected $inheritInputLayout = 'text';
|
||||
|
||||
/**
|
||||
* Validate field value
|
||||
*
|
||||
* @param mixed $value The field's value to validate
|
||||
*
|
||||
* @return mixed True on success, throws an exception on error
|
||||
*/
|
||||
public function validate(&$value)
|
||||
{
|
||||
parent::validate($value);
|
||||
|
||||
if ($this->isEmpty($value))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Validate::url($value))
|
||||
{
|
||||
$this->throwError(\JText::sprintf('COM_CONVERTFORMS_FIELD_URL_INVALID'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare value to be displayed to the user as HTML/text
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function prepareValueHTML($value)
|
||||
{
|
||||
if (!$value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
return '<a href="' . $value . '">' . $value . '</a>';
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,236 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2021 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
/**
|
||||
* Field Choice Class used by dropdown, checkbox and radio fields
|
||||
*/
|
||||
class FieldChoice extends \ConvertForms\Field
|
||||
{
|
||||
/**
|
||||
* Remove common fields from the form rendering
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $excludeFields = [
|
||||
'browserautocomplete',
|
||||
];
|
||||
|
||||
/**
|
||||
* Set field object
|
||||
*
|
||||
* @param mixed $field Object or Array Field options
|
||||
*/
|
||||
public function setField($field)
|
||||
{
|
||||
parent::setField($field);
|
||||
|
||||
// Get input options
|
||||
$this->field->choices = $this->getChoices();
|
||||
|
||||
// Use the selected choice as the field value if we don't have a default value set.
|
||||
if ($this->field->value == '')
|
||||
{
|
||||
foreach ($this->field->choices as $choice)
|
||||
{
|
||||
if ($choice['selected'])
|
||||
{
|
||||
$this->field->value = $choice['value'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->multiple && !is_array($this->field->value))
|
||||
{
|
||||
$this->field->value = explode(',', $this->field->value);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of all field options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getOptions()
|
||||
{
|
||||
$options = [];
|
||||
|
||||
foreach ($this->field->get('choices.choices', []) as $option)
|
||||
{
|
||||
$option = (array) $option;
|
||||
|
||||
if (!isset($option['label']) || $option['label'] == '')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Prepare all options, not only the Label (Remember, we can use Smart Tags in any option, right?) with Smart Tags.
|
||||
$option = \NRFramework\SmartTags::getInstance()->replace($option);
|
||||
|
||||
$label = trim($option['label']);
|
||||
$value = $option['value'] == '' ? $label : $option['value'];
|
||||
$calcValue = isset($option['calc-value']) && $option['calc-value'] !== '' ? $option['calc-value'] : $value;
|
||||
$isSelected = isset($option['default']) && $option['default'] ? true : false;
|
||||
|
||||
$options[] = [
|
||||
'label' => $label,
|
||||
'value' => $value,
|
||||
'calc-value' => $calcValue,
|
||||
'selected' => $isSelected
|
||||
];
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the field choices
|
||||
*
|
||||
* Return Array sample
|
||||
*
|
||||
* $choices = array(
|
||||
* 'label' => 'Color',
|
||||
* 'value' => 'color,
|
||||
* 'calc-value' => '150r,
|
||||
* 'selected' => true,
|
||||
* 'disabled' => false
|
||||
* )
|
||||
*
|
||||
* @return array The field choices array
|
||||
*/
|
||||
protected function getChoices()
|
||||
{
|
||||
$field = $this->field;
|
||||
|
||||
if (!isset($field->choices) || !isset($field->choices['choices']))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$choices = array();
|
||||
$hasPlaceholder = (isset($field->placeholder) && !empty($field->placeholder));
|
||||
|
||||
// Create a new array of valid only choices
|
||||
// @todo Use $this->getOptions() to get the field options
|
||||
foreach ($field->choices['choices'] as $key => $choiceValue)
|
||||
{
|
||||
if (!isset($choiceValue['label']) || $choiceValue['label'] == '')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$label = trim($choiceValue['label']);
|
||||
$value = $choiceValue['value'] == '' ? strip_tags($label) : $choiceValue['value'];
|
||||
|
||||
$choices[] = array(
|
||||
'label' => $label,
|
||||
'value' => $value,
|
||||
'calc-value' => (isset($choiceValue['calc-value']) && $choiceValue['calc-value'] != '' ? $choiceValue['calc-value'] : $value),
|
||||
'selected' => (isset($choiceValue['default']) && $choiceValue['default'] && !$hasPlaceholder) ? true : false
|
||||
);
|
||||
}
|
||||
|
||||
// If we have a placeholder available, add it to dropdown choices.
|
||||
if ($hasPlaceholder)
|
||||
{
|
||||
array_unshift($choices, array(
|
||||
'label' => trim($field->placeholder),
|
||||
'value' => '',
|
||||
'selected' => true,
|
||||
'disabled' => $field->required == '1' ? true : false
|
||||
));
|
||||
}
|
||||
|
||||
return $choices;
|
||||
}
|
||||
|
||||
/**
|
||||
* In choice-based fields, it makes more sense to display the choice's label instead of the choice's value when the field is used for presentation purposes like in the
|
||||
*
|
||||
* 1. Thank you Message
|
||||
* 2. {all_fields} Smart Tag
|
||||
* 3. Form editing page in the back-end
|
||||
* 4. PDF Form Submission addon.
|
||||
*
|
||||
* While we keep using the choice's value in functions like
|
||||
* 1. Calculations
|
||||
* 2. Conditional fields
|
||||
* 3. JSON API.
|
||||
* 4. {field.FIELDNAME}
|
||||
*
|
||||
* @param string $value The raw value as stored in the database / submitted by the user
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function prepareValueHTML($value)
|
||||
{
|
||||
if (is_array($value))
|
||||
{
|
||||
foreach ($value as &$value_)
|
||||
{
|
||||
$value_ = $this->findChoiceLabelByValue($value_);
|
||||
}
|
||||
} else
|
||||
{
|
||||
$value = $this->findChoiceLabelByValue($value);
|
||||
}
|
||||
|
||||
return parent::prepareValueHTML($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to assosiate a choice value with a choice label. As a fallback the raw value will be returned.
|
||||
*
|
||||
* The attempt may fail for one of the reasons below:
|
||||
*
|
||||
* 1. A choice value is renamed.
|
||||
* 2. A choice is removed.
|
||||
* 3. Multiple choices has the same value (which obviously is a mistake by the user).
|
||||
*
|
||||
* The most reliable way to always know the choice's label, is to implement this task https://smilemotive.teamwork.com/#/tasks/30244858
|
||||
* and start storing in the database a choice's unique ID instead of the choice's value.
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function findChoiceLabelByValue($value)
|
||||
{
|
||||
// In multiple choice fields, the value can't be empty.
|
||||
if ($value == '')
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
|
||||
if ($options = $this->getOptions())
|
||||
{
|
||||
foreach ($options as $option)
|
||||
{
|
||||
// We might lowercase both values?
|
||||
if ($option['value'] == $value)
|
||||
{
|
||||
return $option['label'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we can't assosiacte the given value with a label, return the raw value as a fallback.
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,261 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
use Joomla\Registry\Registry;
|
||||
use ConvertForms\Helper;
|
||||
|
||||
/**
|
||||
* ConvertForms Fields Helper Class
|
||||
*/
|
||||
class FieldsHelper
|
||||
{
|
||||
/**
|
||||
* List of available field groups and types
|
||||
*
|
||||
* Consider using a field class property in order to declare the field group instead.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $fields = [
|
||||
'common' => [
|
||||
'text',
|
||||
'textarea',
|
||||
'dropdown',
|
||||
'radio',
|
||||
'checkbox',
|
||||
'number',
|
||||
'email',
|
||||
'tel',
|
||||
'url',
|
||||
'submit',
|
||||
],
|
||||
'layout' => [
|
||||
'html',
|
||||
'heading',
|
||||
'emptyspace',
|
||||
'divider',
|
||||
],
|
||||
'advanced' => [
|
||||
'hidden',
|
||||
'datetime',
|
||||
'password',
|
||||
'fileupload',
|
||||
'termsofservice',
|
||||
'editor',
|
||||
'confirm',
|
||||
'rating',
|
||||
'rangeslider',
|
||||
'colorpicker',
|
||||
'captcha',
|
||||
'recaptcha',
|
||||
'recaptchav2invisible',
|
||||
'hcaptcha',
|
||||
'signature',
|
||||
'country',
|
||||
'currency'
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns a list of all available field groups and types
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getFieldTypes()
|
||||
{
|
||||
$arr = [];
|
||||
|
||||
foreach (self::$fields as $group => $fields)
|
||||
{
|
||||
if (!count($fields))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$arr[$group] = array(
|
||||
'name' => $group,
|
||||
'title' => \JText::_('COM_CONVERTFORMS_FIELDGROUP_' . strtoupper($group))
|
||||
);
|
||||
|
||||
foreach ($fields as $key => $field)
|
||||
{
|
||||
$arr[$group]['fields'][] = array(
|
||||
'name' => $field,
|
||||
'title' => \JText::_('COM_CONVERTFORMS_FIELD_' . strtoupper($field)),
|
||||
'desc' => \JText::_('COM_CONVERTFORMS_FIELD_' . strtoupper($field) . '_DESC'),
|
||||
'class' => self::getFieldClass($field)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render field control group used in the front-end
|
||||
*
|
||||
* @param object $fields The fields to render
|
||||
*
|
||||
* @return string The HTML output
|
||||
*/
|
||||
public static function render($fields)
|
||||
{
|
||||
$html = array();
|
||||
|
||||
foreach ($fields as $key => $field)
|
||||
{
|
||||
if (!isset($field['type']))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip unknown field types
|
||||
if (!$class = self::getFieldClass($field['type']))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$html[] = $class->setField($field)->getControlGroup();
|
||||
}
|
||||
|
||||
return implode(' ', $html);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs and returns the field type class
|
||||
*
|
||||
* @param String $name The field type name
|
||||
*
|
||||
* @return Mixed Object on success, Null on failure
|
||||
*/
|
||||
public static function getFieldClass($name, $field_data = null, $form_data = null)
|
||||
{
|
||||
$class = __NAMESPACE__ . '\\Field\\' . ucfirst($name);
|
||||
|
||||
if (!class_exists($class))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return new $class($field_data, $form_data);
|
||||
}
|
||||
|
||||
public static function prepare($form, $classPrefix = 'cf')
|
||||
{
|
||||
$params = $form['params'];
|
||||
|
||||
if (!is_array($form['fields']) || count($form['fields']) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$fields_ = [];
|
||||
|
||||
foreach ($form['fields'] as $key => $field)
|
||||
{
|
||||
$field['namespace'] = $form['id'];
|
||||
|
||||
// Field Classes
|
||||
$fieldClasses = [
|
||||
$classPrefix . "-input",
|
||||
isset($field['size']) ? $field['size'] : null,
|
||||
isset($field['inputcssclass']) ? $field['inputcssclass'] : null
|
||||
];
|
||||
|
||||
$field['class'] = implode(' ', $fieldClasses);
|
||||
$field['form'] = $form;
|
||||
|
||||
$fields_[] = $field;
|
||||
}
|
||||
|
||||
$globalCSSVars = [
|
||||
'color-primary' => '#4285F4',
|
||||
'color-success' => '#0F9D58',
|
||||
'color-danger' => '#DB4437',
|
||||
'color-warning' => '#F4B400',
|
||||
'color-default' => '#444',
|
||||
'color-grey' => '#ccc',
|
||||
];
|
||||
|
||||
$cssVars = [
|
||||
// Form settings
|
||||
'font' => trim($params->get('font')),
|
||||
'max-width' => ($params->get('autowidth', 'auto') == 'auto' ? null : (int) $params->get('width', 500) . 'px'),
|
||||
'background-color' => $params->get('bgcolor'),
|
||||
'border' => $params->get('borderstyle', 'solid') !== 'none' ? implode(' ', [$params->get('borderstyle', 'solid'), (int) $params->get('borderwidth', 2) . 'px', $params->get('bordercolor', '#000')]) : null,
|
||||
'border-radius' => (int) $params->get('borderradius', 0) . 'px',
|
||||
'padding' => $params->get('padding', 20) > 0 ? (int) $params->get('padding', 20) . 'px' : null,
|
||||
|
||||
// Label settings
|
||||
'label-color' => $params->get('labelscolor', '#888'),
|
||||
'label-size' => (int) $params->get('labelsfontsize', 15) . 'px',
|
||||
|
||||
// Input settings
|
||||
'input-color' => $params->get('inputcolor', '#888'),
|
||||
'input-text-align' => $params->get('inputalign', 'left'),
|
||||
'input-background-color' => $params->get('inputbg', '#fff'),
|
||||
'input-border-color' => $params->get('inputbordercolor', '#ccc'),
|
||||
'input-border-radius' => (int) $params->get('inputborderradius', '0') . 'px',
|
||||
'input-size' => (int) $params->get('inputfontsize', 15) . 'px',
|
||||
'input-padding' => (int) $params->get('inputvpadding', 11) . 'px ' . (int) $params->get('inputhpadding', '12') . 'px',
|
||||
];
|
||||
|
||||
// Background Image
|
||||
if ($params->get('bgimage', false))
|
||||
{
|
||||
$imgurl = intval($params->get("bgimage")) == 1 ? \JURI::root() . Helper::cleanLocalImage($params->get('bgfile')) : $params->get("bgurl");
|
||||
$cssVars['background-image'] = 'url' . '(' . $imgurl . ')';
|
||||
$cssVars['background-repeat'] = strtolower($params->get("bgrepeat"));
|
||||
$cssVars['background-size'] = strtolower($params->get("bgsize"));
|
||||
$cssVars['background-position'] = strtolower($params->get("bgposition"));
|
||||
}
|
||||
|
||||
$cssVarsGlobal = self::cssVarsToString($globalCSSVars, '.convertforms');
|
||||
$cssVarsForm = self::cssVarsToString($cssVars, '#cf_' . $form['id']);
|
||||
|
||||
$html = self::render($fields_);
|
||||
|
||||
if (\JFactory::getApplication()->isClient('site'))
|
||||
{
|
||||
Helper::addStyleDeclarationOnce($cssVarsGlobal);
|
||||
Helper::addStyleDeclarationOnce($cssVarsForm);
|
||||
} else
|
||||
{
|
||||
$html .= '
|
||||
<style>' . $cssVarsGlobal . $cssVarsForm . '</style>
|
||||
';
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
public static function cssVarsToString($cssVars, $namespace)
|
||||
{
|
||||
$output = '';
|
||||
|
||||
foreach (array_filter($cssVars) as $key => $value)
|
||||
{
|
||||
$output .= '--' . $key . ': ' . $value . ';' . "\n";
|
||||
}
|
||||
|
||||
return $namespace . ' {
|
||||
' . $output . '
|
||||
}
|
||||
';
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
217
administrator/components/com_convertforms/ConvertForms/Form.php
Normal file
217
administrator/components/com_convertforms/ConvertForms/Form.php
Normal file
@@ -0,0 +1,217 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
use ConvertForms\Helper;
|
||||
use NRFramework\Cache;
|
||||
use Joomla\Registry\Registry;
|
||||
|
||||
class Form
|
||||
{
|
||||
/**
|
||||
* Returns a form object from database
|
||||
*
|
||||
* @param integer $id The ID of the form
|
||||
* @param bool $only_inputs If true, fields that doesn't have an input element such as HTML and reCAPTCHA, won't be returned.
|
||||
*
|
||||
* @return mixed Null on failure, array on success
|
||||
*/
|
||||
public static function load($id, $only_inputs = false, $ignore_state = false)
|
||||
{
|
||||
if (!$id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$hash = 'convertforms_' . $id . '_' . (string) $only_inputs;
|
||||
if (Cache::has($hash))
|
||||
{
|
||||
return Cache::get($hash);
|
||||
}
|
||||
|
||||
// Get a db connection.
|
||||
$db = \JFactory::getDbo();
|
||||
$query = $db->getQuery(true);
|
||||
|
||||
$query->select('*')
|
||||
->from($db->quoteName('#__convertforms'))
|
||||
->where($db->quoteName('id') . ' = '. (int) $id);
|
||||
|
||||
if (!$ignore_state)
|
||||
{
|
||||
$query->where($db->quoteName('state') . ' = 1');
|
||||
}
|
||||
|
||||
$db->setQuery($query);
|
||||
|
||||
if (!$form = $db->loadAssoc())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$form['params'] = json_decode($form['params'], true);
|
||||
|
||||
// Make 3rd party developer's life easier by setting field's name as the array key for faster code manipulation through PHP scripts.
|
||||
foreach ($form['params']['fields'] as $key => $field)
|
||||
{
|
||||
// This is useful when we would like to skip non-interactive fields such as HTML, reCAPTCHA e.t.c.
|
||||
if ($only_inputs && !isset($field['name']))
|
||||
{
|
||||
unset($form['params']['fields'][$key]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$name = isset($field['name']) ? $field['name'] : $key;
|
||||
|
||||
unset($form['params']['fields'][$key]);
|
||||
$form['params']['fields'][$name] = $field;
|
||||
}
|
||||
|
||||
$form['fields'] = $form['params']['fields'];
|
||||
unset($form['params']['fields']);
|
||||
|
||||
return Cache::set($hash, $form);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get settings of a form field
|
||||
*
|
||||
* @param integer $form_id The id of the form
|
||||
* @param integer $field_key The id of the field
|
||||
*
|
||||
* @return mixed Null on failure, Registry object on success
|
||||
*/
|
||||
public static function getFieldSettingsByKey($form_id, $field_key)
|
||||
{
|
||||
if (!$form = self::load($form_id))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$found = array_filter($form['fields'], function($field) use ($field_key)
|
||||
{
|
||||
return ($field['key'] == $field_key);
|
||||
});
|
||||
|
||||
return new Registry(array_pop($found));;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run user-defined PHP scripts on certain form events.
|
||||
*
|
||||
* The available events are:
|
||||
*
|
||||
* formprepare: Called on form data prepare.
|
||||
* formdisplay: Called on form display.
|
||||
* formsubmission: Called on form process.
|
||||
* afterformprocess: Called after the form has been processed and the submission is saved into the database.
|
||||
*
|
||||
* @param Integer $form_id The form's ID
|
||||
* @param String $script_name The script name to run
|
||||
* @param Array $payload The data passed as argument to the PHP script. By reference.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function runPHPScript($form_id, $script_name, &$payload)
|
||||
{
|
||||
// Only on the front-end
|
||||
if (\JFactory::getApplication()->isClient('administrator'))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$form = self::load($form_id))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Abort, if the script is not found
|
||||
if (!isset($form['params']['phpscripts'][$script_name]))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Abort, if the script is empty
|
||||
if (!$php_script = $form['params']['phpscripts'][$script_name])
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isset($payload['form']))
|
||||
{
|
||||
$payload['form'] = $form;
|
||||
}
|
||||
|
||||
$payload['script_name'] = $script_name;
|
||||
|
||||
try
|
||||
{
|
||||
$executer = new \NRFramework\Executer($php_script, $payload);
|
||||
$executer->run();
|
||||
} catch (\Throwable $th)
|
||||
{
|
||||
$error = $th->getMessage() . ' - ' . $th->getFile() . ' on line ' . $th->getLine();
|
||||
|
||||
// Log error
|
||||
Helper::triggerError($error, 'PHP Script', $form_id);
|
||||
|
||||
// Re throw exception
|
||||
throw new \Exception($th->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the total number of form submissions
|
||||
*
|
||||
* @param integer $form_id The form's ID
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public static function getSubmissionsTotal($form_id)
|
||||
{
|
||||
if (!$form_id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$hash = md5('cf_count_' . $form_id);
|
||||
|
||||
if (Cache::has($hash))
|
||||
{
|
||||
return Cache::get($hash);
|
||||
}
|
||||
|
||||
\JModelLegacy::addIncludePath(JPATH_ADMINISTRATOR . '/components/com_convertforms/models');
|
||||
|
||||
$model = \JModelLegacy::getInstance('Conversions', 'ConvertFormsModel', ['ignore_request' => true]);
|
||||
|
||||
$model->setState('filter.form_id', (int) $form_id);
|
||||
$model->setState('filter.state', [1, 2]);
|
||||
$model->setState('filter.join_campaigns', 'skip');
|
||||
$model->setState('filter.join_forms', 'skip');
|
||||
|
||||
$query = $model->getListQuery();
|
||||
|
||||
$query->clear('select');
|
||||
$query->select('count(a.id)');
|
||||
|
||||
$db = \JFactory::getDbo();
|
||||
$db->setQuery($query);
|
||||
|
||||
$count = $db->loadResult();
|
||||
|
||||
return Cache::set($hash, $count);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,802 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
use NRFramework\Cache;
|
||||
use ConvertForms\Form;
|
||||
use Joomla\Registry\Registry;
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
|
||||
jimport('joomla.log.log');
|
||||
|
||||
class Helper
|
||||
{
|
||||
/**
|
||||
* Ensure same script are added once on the page
|
||||
*
|
||||
* @param string $styles The script code
|
||||
*
|
||||
* @return null on failure.
|
||||
*/
|
||||
public static function addScriptDeclarationOnce($script)
|
||||
{
|
||||
$hash = 'cf_script_' . md5($script);
|
||||
|
||||
if (Cache::has($hash))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
\JFactory::getDocument()->addScriptDeclaration($script);
|
||||
|
||||
Cache::set($hash, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure same styles are added once on the page
|
||||
*
|
||||
* @param string $styles The CSS code
|
||||
*
|
||||
* @return null on failure.
|
||||
*/
|
||||
public static function addStyleDeclarationOnce($styles)
|
||||
{
|
||||
$hash = 'cf_styles_' . md5($styles);
|
||||
|
||||
if (Cache::has($hash))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
\JFactory::getDocument()->addStyleDeclaration($styles);
|
||||
|
||||
Cache::set($hash, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current logged in user is authorised to view a resource.
|
||||
*
|
||||
* @param string $action
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function authorise($action, $throw_exception = false)
|
||||
{
|
||||
$authorised = \JFactory::getUser()->authorise($action, 'com_convertforms');
|
||||
|
||||
if (!$authorised && $throw_exception)
|
||||
{
|
||||
throw new \JAccessExceptionNotallowed(\JText::_('JERROR_ALERTNOAUTHOR'), 403);
|
||||
}
|
||||
|
||||
return $authorised;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger Error Event
|
||||
*
|
||||
* @param string $error The error message
|
||||
* @param string $category The error category
|
||||
* @param integer $form_id The form ID assosiated with the error
|
||||
* @param mixed $data Extra data related to the error occured
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function triggerError($error, $category, $form_id, $data = null)
|
||||
{
|
||||
\JPluginHelper::importPlugin('convertforms');
|
||||
\JFactory::getApplication()->triggerEvent('onConvertFormsError', [$error, $category, $form_id, $data]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert all applicable characters to HTML entities
|
||||
*
|
||||
* @param string $input The input string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function escape($input)
|
||||
{
|
||||
if (!is_string($input))
|
||||
{
|
||||
return $input;
|
||||
}
|
||||
|
||||
// Convert all HTML tags to HTML entities.
|
||||
$input = htmlspecialchars($input, ENT_NOQUOTES, 'UTF-8');
|
||||
|
||||
// We do not need any Smart Tag replacements take place here, so we need to escape curly brackets too.
|
||||
$input = str_replace(['{', '}'], ['{', '}'], $input);
|
||||
|
||||
// Respect newline characters, by converting them to <br> tags.
|
||||
$input = nl2br($input);
|
||||
|
||||
return $input;
|
||||
}
|
||||
|
||||
public static function getComponentParams()
|
||||
{
|
||||
$hash = 'cfComponentParams';
|
||||
|
||||
if (Cache::has($hash))
|
||||
{
|
||||
return Cache::get($hash);
|
||||
}
|
||||
|
||||
return Cache::set($hash, \JComponentHelper::getParams('com_convertforms'));
|
||||
}
|
||||
|
||||
public static function getFormLeadsCount($form)
|
||||
{
|
||||
$hash = 'formLeadsCount' . $form;
|
||||
|
||||
if (Cache::has($hash))
|
||||
{
|
||||
return Cache::get($hash);
|
||||
}
|
||||
|
||||
if (!$form || $form == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
$db = \JFactory::getDBO();
|
||||
$query = $db->getQuery(true);
|
||||
|
||||
$query
|
||||
->select('count(*)')
|
||||
->from('#__convertforms_conversions')
|
||||
->where('form_id = ' . $form);
|
||||
|
||||
$db->setQuery($query);
|
||||
|
||||
return Cache::set($hash, $db->loadResult());
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders form template selection modal to the document
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function renderSelectTemplateModal()
|
||||
{
|
||||
echo \JHtml::_('bootstrap.renderModal', 'cfSelectTemplate', array(
|
||||
'url' => 'index.php?option=com_convertforms&view=templates&tmpl=component',
|
||||
'title' => \JText::_('COM_CONVERTFORMS_TEMPLATES_SELECT'),
|
||||
'closeButton' => true,
|
||||
'height' => '100%',
|
||||
'width' => '100%',
|
||||
'modalWidth' => '70',
|
||||
'bodyHeight' => '70',
|
||||
'footer' => '
|
||||
<a href="' . \JURI::base() . 'index.php?option=com_convertforms&view=form&layout=edit" class="btn btn-primary">
|
||||
<span class="icon-new"></span> ' . \JText::_('COM_CONVERTFORMS_TEMPLATES_BLANK') . '
|
||||
</a>
|
||||
'
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the Not Found Items message
|
||||
*
|
||||
* @param string $view
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function noItemsFound($view = 'submissions')
|
||||
{
|
||||
$html[] = '<span style="font-size:16px; position:relative; top:2px;" class="icon-smiley-sad-2"></span>';
|
||||
$html[] = \JText::sprintf('COM_CONVERTFORMS_NO_RESULTS_FOUND', strtolower(\JText::_('COM_CONVERTFORMS_' . $view)));
|
||||
|
||||
return implode(' ', $html);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Visitor ID
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getVisitorID()
|
||||
{
|
||||
return \NRFramework\VisitorToken::getInstance()->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns campaigns list visitor is subscribed to
|
||||
* If the user is logged in, we try to get the campaigns by user's ID
|
||||
* Otherwise, the visitor cookie ID will be used instead
|
||||
*
|
||||
* @return array List of campaign IDs
|
||||
*/
|
||||
public static function getVisitorCampaigns()
|
||||
{
|
||||
$db = \JFactory::getDBO();
|
||||
$query = $db->getQuery(true);
|
||||
$user = \JFactory::getUser();
|
||||
|
||||
$query
|
||||
->select('campaign_id')
|
||||
->from('#__convertforms_conversions')
|
||||
->where('state = 1')
|
||||
->group('campaign_id');
|
||||
|
||||
// Get submissions by user id if visitor is logged in
|
||||
if ($user->id)
|
||||
{
|
||||
$query->where('user_id = ' . (int) $user->id);
|
||||
} else
|
||||
{
|
||||
// Get submissions by Visitor Cookie
|
||||
if (!$visitorID = self::getVisitorID())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$query->where('visitor_id = ' . $db->q($visitorID));
|
||||
}
|
||||
|
||||
$db->setQuery($query);
|
||||
return $db->loadColumn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array with all available campaigns
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getCampaigns()
|
||||
{
|
||||
\JModelLegacy::addIncludePath(JPATH_ADMINISTRATOR . '/components/com_convertforms/models');
|
||||
|
||||
$model = \JModelLegacy::getInstance('Campaigns', 'ConvertFormsModel', array('ignore_request' => true));
|
||||
$model->setState('filter.state', 1);
|
||||
|
||||
return $model->getItems();
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs messages to log file
|
||||
*
|
||||
* @param string $msg The message
|
||||
* @param object $type The log type
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function log($msg, $type = 'debug')
|
||||
{
|
||||
$debugIsEnabled = self::getComponentParams()->get('debug', false);
|
||||
|
||||
if ($type == 'debug' && !$debugIsEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$type = ($type == 'debug') ? \JLog::DEBUG : \JLog::ERROR;
|
||||
|
||||
try {
|
||||
\JLog::add($msg, $type, 'com_convertforms');
|
||||
} catch (\Throwable $th){
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads pre-made form template
|
||||
*
|
||||
* @param string $name The template name
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public static function loadFormTemplate($name)
|
||||
{
|
||||
$form = JPATH_ROOT . '/media/com_convertforms/templates/' . $name . '.cnvf';
|
||||
|
||||
if (!\JFile::exists($form))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$content = file_get_contents($form);
|
||||
|
||||
if (empty($content))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$item = json_decode($content, true);
|
||||
$item = $item[0];
|
||||
|
||||
$data = (object) array_merge((array) $item, (array) json_decode($item['params']));
|
||||
$data->id = 0;
|
||||
$data->campaign = null;
|
||||
$data->fields = (array) $data->fields;
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the Linkbar.
|
||||
*
|
||||
* @param string $vName The name of the active view.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function addSubmenu($vName)
|
||||
{
|
||||
$items = [
|
||||
[
|
||||
'label' => 'NR_DASHBOARD',
|
||||
'view' => 'convertforms',
|
||||
'skip_auth' => true
|
||||
],
|
||||
[
|
||||
'label' => 'COM_CONVERTFORMS_FORMS',
|
||||
'view' => 'forms',
|
||||
],
|
||||
[
|
||||
'label' => 'COM_CONVERTFORMS_CAMPAIGNS',
|
||||
'view' => 'campaigns',
|
||||
],
|
||||
[
|
||||
'label' => 'COM_CONVERTFORMS_SUBMISSIONS',
|
||||
'view' => 'conversions',
|
||||
'view_rule' => 'submissions'
|
||||
],
|
||||
[
|
||||
'label' => 'COM_CONVERTFORMS_ADDONS',
|
||||
'view' => 'addons',
|
||||
]
|
||||
];
|
||||
|
||||
foreach ($items as $item)
|
||||
{
|
||||
if (!isset($item['skip_auth']) && !self::authorise('convertforms.' . (isset($item['view_rule']) ? $item['view_rule'] : $item['view']) . '.manage'))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
\JHtmlSidebar::addEntry(\JText::_($item['label']), 'index.php?option=com_convertforms&view=' . $item['view'], $item['view']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns permissions
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public static function getActions()
|
||||
{
|
||||
$user = \JFactory::getUser();
|
||||
$result = new \JObject;
|
||||
$assetName = 'com_convertforms';
|
||||
|
||||
$actions = array(
|
||||
'core.admin', 'core.manage', 'core.create', 'core.edit', 'core.edit.state', 'core.delete'
|
||||
);
|
||||
|
||||
foreach ($actions as $action)
|
||||
{
|
||||
$result->set($action, $user->authorise($action, $assetName));
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a form object for rendering
|
||||
*
|
||||
* @param Object $form The form object
|
||||
*
|
||||
* @return array The prepared form array
|
||||
*/
|
||||
public static function prepareForm($item)
|
||||
{
|
||||
$item['id'] = isset($item['id']) ? $item['id'] : 0;
|
||||
$classPrefix = 'cf';
|
||||
|
||||
// Replace variables
|
||||
$item = \ConvertForms\SmartTags::replace($item, null, $item['id']);
|
||||
|
||||
$params = new Registry($item['params']);
|
||||
$item['params'] = $params;
|
||||
|
||||
/* Box Classes */
|
||||
$boxClasses = array(
|
||||
"convertforms",
|
||||
$classPrefix,
|
||||
$classPrefix . "-" . $params->get("imgposition"),
|
||||
$classPrefix . "-" . $params->get("formposition"),
|
||||
$params->get("hideform", true) ? $classPrefix . "-success-hideform" : null,
|
||||
$params->get("hidetext", false) ? $classPrefix . "-success-hidetext" : null,
|
||||
!$params->get("hidelabels", false) ? $classPrefix . "-hasLabels" : null,
|
||||
$params->get("centerform", false) ? $classPrefix . "-isCentered" : null,
|
||||
$params->get("classsuffix", null),
|
||||
$classPrefix . '-labelpos-' . $params->get('labelposition', 'top'),
|
||||
);
|
||||
|
||||
/* Box Styles */
|
||||
$font = trim($params->get('font'));
|
||||
|
||||
// Form HTML Attributes
|
||||
$item['boxattributes'] = implode(" ",
|
||||
array(
|
||||
'id="' . $classPrefix . '_' . $item['id'] . '"',
|
||||
'class="' . implode(" ", $boxClasses) . '"',
|
||||
)
|
||||
);
|
||||
|
||||
// Main Image Checks
|
||||
$imageOption = $params->get("image");
|
||||
if ($imageOption == '1')
|
||||
{
|
||||
$imageFile = Helper::cleanLocalImage($params->get('imagefile', ''));
|
||||
|
||||
if (\JFile::exists(JPATH_SITE . '/' . $imageFile))
|
||||
{
|
||||
$item['image'] = \JURI::root() . $imageFile;
|
||||
}
|
||||
}
|
||||
if ($imageOption == '2')
|
||||
{
|
||||
$item['image'] = $params->get("imageurl");
|
||||
}
|
||||
|
||||
// Image Container
|
||||
$item['imagecontainerclasses'] = implode(" ", array(
|
||||
(in_array($params->get("imgposition"), array("img-right", "img-left")) ? $classPrefix . "-col-medium-" . $params->get("imagesize", 6) : null),
|
||||
));
|
||||
|
||||
// Image
|
||||
$item['imagestyles'] = array(
|
||||
"width:" . ($params->get("imageautowidth", "auto") == "auto" ? "auto" : (int) $params->get("imagewidth", "500") . "px"),
|
||||
"left:". (int) $params->get("imagehposition", "0") . "px ",
|
||||
"top:". (int) $params->get("imagevposition", "0") . "px"
|
||||
);
|
||||
$item['imageclasses'] = array(
|
||||
($params->get("hideimageonmobile", false) ? "cf-hide-mobile" : "")
|
||||
);
|
||||
|
||||
// Form Container
|
||||
$item['formclasses'] = array(
|
||||
(in_array($params->get("formposition"), array("form-left", "form-right")) ? $classPrefix . "-col-large-" . $params->get("formsize", 6) : null),
|
||||
);
|
||||
$item['formstyles'] = array(
|
||||
"background-color:" . $params->get("formbgcolor", "none")
|
||||
);
|
||||
|
||||
// Content
|
||||
$item['contentclasses'] = implode(" ", array(
|
||||
(in_array($params->get("formposition"), array("form-left", "form-right")) ? $classPrefix . "-col-large-" . (16 - $params->get("formsize", 6)) : null),
|
||||
));
|
||||
|
||||
// Text Container
|
||||
$item['textcontainerclasses'] = implode(" ", array(
|
||||
(in_array($params->get("imgposition"), array("img-right", "img-left")) ? $classPrefix . "-col-medium-" . (16 - $params->get("imagesize", 6)) : null),
|
||||
));
|
||||
|
||||
$textContent = trim($params->get("text", ''));
|
||||
$footerContent = trim($params->get("footer", ''));
|
||||
|
||||
$item['textIsEmpty'] = empty($textContent);
|
||||
$item['footerIsEmpty'] = empty($footerContent);
|
||||
$item['hascontent'] = !$item['textIsEmpty'] || (bool) isset($item['image']) ? 1 : 0;
|
||||
|
||||
// Prepare Fields
|
||||
$item['fields_prepare'] = \ConvertForms\FieldsHelper::prepare($item);
|
||||
|
||||
// Load custom fonts into the document
|
||||
\NRFramework\Fonts::loadFont($font);
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders form by ID
|
||||
*
|
||||
* @param integer $id The form ID
|
||||
*
|
||||
* @return string The form HTML
|
||||
*/
|
||||
public static function renderFormById($id)
|
||||
{
|
||||
if (!$data = Form::load($id))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self::loadassets(true);
|
||||
|
||||
return self::renderForm($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders Form
|
||||
*
|
||||
* @param integer $formid The form id
|
||||
*
|
||||
* @return string The form HTML
|
||||
*/
|
||||
public static function renderForm($data)
|
||||
{
|
||||
$app = \JFactory::getApplication();
|
||||
|
||||
\JPluginHelper::importPlugin('convertforms');
|
||||
\JPluginHelper::importPlugin('convertformstools');
|
||||
|
||||
// load translation strings
|
||||
self::loadTranslations();
|
||||
|
||||
// @todo - Move PHP Scripts logic into a separate plugin
|
||||
// Let user manipulate the form's settings by running their own PHP script
|
||||
$payload_1 = ['form' => &$data];
|
||||
Form::runPHPScript($data['id'], 'formprepare', $payload_1);
|
||||
|
||||
$app->triggerEvent('onConvertFormsFormBeforeRender', [&$data]);
|
||||
|
||||
// Prepare form and fields
|
||||
$data = self::prepareForm($data);
|
||||
$html = self::layoutRender('form', $data);
|
||||
|
||||
// Prepare HTML with content plugins
|
||||
if ($app->isClient('site'))
|
||||
{
|
||||
$html = HTMLHelper::_('content.prepare', $html);
|
||||
}
|
||||
|
||||
// Let user manipulate the form's HTML by running their own PHP script
|
||||
$payload_2 = [
|
||||
'formLayout' => &$html,
|
||||
'form' => $data
|
||||
];
|
||||
Form::runPHPScript($data['id'], 'formdisplay', $payload_2);
|
||||
|
||||
$app->triggerEvent('onConvertFormsFormAfterRender', [&$html, $data]);
|
||||
|
||||
// Prevent user frustration by fixing broken images in the backend.
|
||||
// This is required since v2.8.0 where we no longer forces absolute URLs in the text editors.
|
||||
if ($app->isClient('administrator'))
|
||||
{
|
||||
$html = \NRFramework\URLHelper::relativePathsToAbsoluteURLs($html, null, false);
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues translations for the front-end
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function loadTranslations()
|
||||
{
|
||||
\JText::script('COM_CONVERTFORMS_INVALID_RESPONSE');
|
||||
\JText::script('COM_CONVERTFORMS_INVALID_TASK');
|
||||
}
|
||||
|
||||
/**
|
||||
* Render HTML overridable layout
|
||||
*
|
||||
* @param string $layout The layout name
|
||||
* @param object $data The data passed to layout
|
||||
*
|
||||
* @return string The rendered HTML layout
|
||||
*/
|
||||
public static function layoutRender($layout, $data)
|
||||
{
|
||||
return \JLayoutHelper::render($layout, $data, null, ['debug' => false, 'client' => 1, 'component' => 'com_convertforms']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads components media files
|
||||
*
|
||||
* @param boolean $front
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function loadassets($frontend = false)
|
||||
{
|
||||
static $run;
|
||||
|
||||
if ($run)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$run = true;
|
||||
|
||||
// Front-end media files
|
||||
if ($frontend)
|
||||
{
|
||||
// Load core.js needed by keepalive script.
|
||||
\JHtml::_('behavior.core');
|
||||
\JHtml::_('behavior.keepalive');
|
||||
|
||||
\JHtml::script('com_convertforms/site.js', ['relative' => true, 'version' => 'auto']);
|
||||
|
||||
$params = self::getComponentParams();
|
||||
|
||||
if ($params->get("loadCSS", true))
|
||||
{
|
||||
\JHtml::stylesheet('com_convertforms/convertforms.css', ['relative' => true, 'version' => 'auto']);
|
||||
}
|
||||
|
||||
$doc = \JFactory::getDocument();
|
||||
$options = $doc->getScriptOptions('com_convertforms');
|
||||
$options = is_array($options) ? $options : [];
|
||||
|
||||
$options = [
|
||||
// Remove trailing slash from the base URL to prevent unwanted redirections during AJAX submission
|
||||
'baseURL' => \Joomla\String\StringHelper::rtrim(\JRoute::_('index.php?option=com_convertforms'), '/'),
|
||||
'debug' => (bool) $params->get('debug', false)
|
||||
];
|
||||
|
||||
$doc->addScriptOptions('com_convertforms', $options);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
\JHtml::_('jquery.framework');
|
||||
\JHtml::stylesheet('com_convertforms/convertforms.sys.css', ['relative' => true, 'version' => 'auto']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Campaign Item by ID
|
||||
*
|
||||
* @param integer $id The campaign ID
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public static function getCampaign($id)
|
||||
{
|
||||
$model = \JModelLegacy::getInstance('Campaign', 'ConvertFormsModel', array('ignore_request' => true));
|
||||
return $model->getItem($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the given error message to log file.
|
||||
*
|
||||
* @param string $error_message The error message
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function logError($error_message)
|
||||
{
|
||||
try {
|
||||
\JLog::add($error_message, \JLog::ERROR, 'convertforms_errors');
|
||||
} catch (\Throwable $th) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format given date based on the DATE_FORMAT_LC5 global format
|
||||
*
|
||||
* @param string $date
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function formatDate($date)
|
||||
{
|
||||
if (!$date || $date == '0000-00-00 00:00:00')
|
||||
{
|
||||
return $date;
|
||||
}
|
||||
|
||||
return \JHtml::_('date', $date, \JText::_('DATE_FORMAT_LC5'));
|
||||
}
|
||||
|
||||
public static function getColumns($form_id)
|
||||
{
|
||||
if (!$form_id)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
$fields = Form::load($form_id, true, true);
|
||||
|
||||
if (!is_array($fields) || !is_array($fields['fields']))
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
// Form Fields
|
||||
$form_fields = array_map(function($value)
|
||||
{
|
||||
return 'param_' . $value;
|
||||
}, array_keys($fields['fields']));
|
||||
|
||||
$default_columns = [
|
||||
'id',
|
||||
'created',
|
||||
'user_id',
|
||||
'user_username',
|
||||
'visitor_id',
|
||||
'form_name',
|
||||
'param_leadnotes'
|
||||
];
|
||||
|
||||
// Set ID and Date Submitted as the first 2 columns
|
||||
$columns = array_merge(array_slice($default_columns, 0, 2), $form_fields, array_slice($default_columns, 2, count($default_columns)));
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return absolute full URL of a path
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function pathTorelative($path)
|
||||
{
|
||||
return str_replace([JPATH_SITE, JPATH_ROOT], '', $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return absolute full URL of a path
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function absURL($path)
|
||||
{
|
||||
$path = str_replace([JPATH_SITE, JPATH_ROOT, \JURI::root()], '', $path);
|
||||
$path = \JPath::clean($path);
|
||||
|
||||
// Convert Windows Path to Unix
|
||||
$path = str_replace('\\','/',$path);
|
||||
|
||||
$path = ltrim($path, '/');
|
||||
$path = \JURI::root() . $path;
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a joke. Joomla 4's media field started including width and height information in the path.
|
||||
* So, we need to clean the path before we can use it.
|
||||
*
|
||||
* images/headers/blue-flower.jpg#joomlaImage://local-images/headers/blue-flower.jpg?width=700&height=180)
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function cleanLocalImage($path)
|
||||
{
|
||||
return defined('nrJ4') ? \Joomla\CMS\Helper\MediaHelper::getCleanMediaFieldValue($path) : $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove special charactes from string in order to be used in a HTML attribute without issues.
|
||||
*
|
||||
* @param string $string The text to clean
|
||||
*
|
||||
* @return string The cleaned text
|
||||
*/
|
||||
public static function makeHTMLAttributeSafe($string)
|
||||
{
|
||||
// Replaces all spaces with hyphens.
|
||||
$string = str_replace(' ', '-', $string);
|
||||
|
||||
// Removes special characters
|
||||
$string = preg_replace('/[^A-Za-z0-9\-]/', '', $string);
|
||||
|
||||
// Replaces multiple hyphens with single one.
|
||||
$string = preg_replace('/-+/', '-', $string);
|
||||
|
||||
return strtolower($string);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
use ConvertForms\Helper;
|
||||
|
||||
/**
|
||||
* ConvertForms API Class
|
||||
*/
|
||||
class JsonApi
|
||||
{
|
||||
/**
|
||||
* Joomla Application Object
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
private $app;
|
||||
|
||||
/**
|
||||
* API Key
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $apikey;
|
||||
|
||||
/**
|
||||
* Class Constructor
|
||||
*
|
||||
* @param string $key User API Key
|
||||
*/
|
||||
public function __construct($apikey)
|
||||
{
|
||||
if (!isset($apikey) || empty($apikey))
|
||||
{
|
||||
$this->throwError('API Key is missing');
|
||||
}
|
||||
|
||||
$this->apikey = $apikey;
|
||||
|
||||
if (!$this->authenticate())
|
||||
{
|
||||
$this->throwError('Invalid API Key: '. $this->apikey);
|
||||
}
|
||||
|
||||
$this->app = \JFactory::getApplication();
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an exception and logs the error message to the log file
|
||||
*
|
||||
* @param string $error The error message
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function throwError($error)
|
||||
{
|
||||
$error = 'ConvertForms API: ' . $error;
|
||||
|
||||
Helper::log($error, 'error');
|
||||
throw new \Exception($error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate API Call
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authenticate()
|
||||
{
|
||||
return $this->getSiteKey() === $this->apikey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Domain Key
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function generateDomainKey()
|
||||
{
|
||||
$parse = parse_url(\JURI::root());
|
||||
$domain = isset($parse['host']) ? $parse['host'] : '-';
|
||||
$hash = md5('CF' . $domain);
|
||||
|
||||
return $hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Domain Key
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getSiteKey()
|
||||
{
|
||||
return Helper::getComponentParams()->get('api_key', null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Route API endpoint to the proper method
|
||||
*
|
||||
* @param string $endpoint The API Endpoint Name
|
||||
*
|
||||
* @return string Response Array
|
||||
*/
|
||||
public function route($endpoint)
|
||||
{
|
||||
$endPointMethod = 'endPoint' . $endpoint;
|
||||
|
||||
if (!method_exists($this, $endPointMethod))
|
||||
{
|
||||
$this->throwError('Endpoint not found:' . $endpoint);
|
||||
}
|
||||
|
||||
return $this->$endPointMethod();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads and populates model
|
||||
*
|
||||
* @param JModel &$model
|
||||
*
|
||||
* @return JModel
|
||||
*/
|
||||
private function getModel($modelName)
|
||||
{
|
||||
if (!$modelName)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$model = \JModelLegacy::getInstance($modelName, 'ConvertFormsModel', array('ignore_request' => true));
|
||||
|
||||
$model->setState('list.limit', $this->app->input->get('limit', 1000));
|
||||
$model->setState('list.start', $this->app->input->get('start', 0));
|
||||
$model->setState('list.ordering', $this->app->input->get('order', 'a.id'));
|
||||
$model->setState('list.direction', $this->app->input->get('dir', 'desc'));
|
||||
|
||||
$state = $this->app->input->get('state', null);
|
||||
|
||||
if (!is_null($state))
|
||||
{
|
||||
$model->setState('filter.state', $state);
|
||||
}
|
||||
|
||||
return $model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Backwards compatibility support for old Leads endpoint
|
||||
*
|
||||
* @deprecated 2.2.0
|
||||
*
|
||||
* @return array Database array
|
||||
*/
|
||||
private function endPointLeads()
|
||||
{
|
||||
return $this->endPointSubmissions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Submissions Endpoint
|
||||
*
|
||||
* @since 2.2.0
|
||||
*
|
||||
* @return array Database array
|
||||
*/
|
||||
private function endPointSubmissions()
|
||||
{
|
||||
// Load Model
|
||||
$model = $this->getModel('Conversions');
|
||||
|
||||
// Apply form filter
|
||||
$formID = $this->app->input->get('form', null);
|
||||
if (!is_null($formID))
|
||||
{
|
||||
$model->setState('filter.form_id', $formID);
|
||||
}
|
||||
|
||||
// Apply campaign filter
|
||||
$campaignID = $this->app->input->get('campaign', null);
|
||||
if (!is_null($campaignID))
|
||||
{
|
||||
$model->setState('filter.campaign_id', $campaignID);
|
||||
}
|
||||
|
||||
// Get Data
|
||||
$leads = $model->getItems();
|
||||
$data = array();
|
||||
|
||||
$tz = new \DateTimeZone($this->app->getCfg('offset', 'UTC'));
|
||||
|
||||
foreach ($leads as $key => $lead)
|
||||
{
|
||||
// Convert created date to ISO8601 format to make Zapier happy.
|
||||
$date_ = new \JDate($lead->created, $tz);
|
||||
$lead->created = $date_->toISO8601(true);
|
||||
|
||||
$data_ = array(
|
||||
'id' => $lead->id,
|
||||
'state' => $lead->state,
|
||||
'created' => $lead->created,
|
||||
'form_id' => $lead->form_id,
|
||||
'campaign_id' => $lead->campaign_id,
|
||||
'user_id' => $lead->user_id,
|
||||
'visitor_id' => $lead->visitor_id
|
||||
);
|
||||
|
||||
// Include custom fields as well
|
||||
if (isset($lead->prepared_fields))
|
||||
{
|
||||
foreach ($lead->prepared_fields as $key => $field)
|
||||
{
|
||||
// This is a temporary workaround for File Upload field, to display files as absolute URLs in array
|
||||
// We can't use $field->value directly as it transforms the array to string.
|
||||
if ($field->options->get('type') == 'fileupload')
|
||||
{
|
||||
$field->value_raw = array_filter(array_map('trim', explode(',', $field->value)));
|
||||
}
|
||||
|
||||
$data_['field_' . $key] = $field->value_raw;
|
||||
|
||||
if ($field->value_raw !== $field->value_html)
|
||||
{
|
||||
$data_['field_' . $key . '.html'] = $field->value_html;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$data[] = $data_;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forms Endpoint
|
||||
*
|
||||
* @return array Database array
|
||||
*/
|
||||
private function endPointForms()
|
||||
{
|
||||
return $this->endPointDefault('Forms');
|
||||
}
|
||||
|
||||
/**
|
||||
* Campaigns Endpoint
|
||||
*
|
||||
* @return array Database array
|
||||
*/
|
||||
private function endPointCampaigns()
|
||||
{
|
||||
return $this->endPointDefault('Campaigns');
|
||||
}
|
||||
|
||||
/**
|
||||
* Common Default Endpoint
|
||||
*
|
||||
* @param string $model ConvertForms model name
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function endPointDefault($model)
|
||||
{
|
||||
// Load Model
|
||||
$model = $this->getModel($model);
|
||||
|
||||
if (!$model)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get Data
|
||||
$items = $model->getItems();
|
||||
|
||||
if (!$items)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$data = array();
|
||||
|
||||
foreach ($items as $key => $item)
|
||||
{
|
||||
$data[] = array(
|
||||
'id' => $item->id,
|
||||
'name' => $item->name,
|
||||
'created' => $item->created,
|
||||
'state' => $item->state
|
||||
);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,217 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms;
|
||||
|
||||
use Joomla\Registry\Registry;
|
||||
use \NRFramework\Countries;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
/**
|
||||
* Convert Forms Migrator
|
||||
*/
|
||||
class Migrator
|
||||
{
|
||||
/**
|
||||
* The database class
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
private $db;
|
||||
|
||||
/**
|
||||
* Indicates the current installed version of the extension
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $installedVersion;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param string $installedVersion The current extension version
|
||||
*/
|
||||
public function __construct($installedVersion)
|
||||
{
|
||||
$this->installedVersion = $installedVersion;
|
||||
$this->db = \JFactory::getDbo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start migration process
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function start()
|
||||
{
|
||||
$this->set_sib_campaigns_default_v2_version();
|
||||
|
||||
if (!$items = $this->getForms())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($items as $item)
|
||||
{
|
||||
$item->params = new Registry($item->params);
|
||||
|
||||
if (version_compare($this->installedVersion, '2.7.3', '<='))
|
||||
{
|
||||
$this->fixUploadFolder($item);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Update item using id as the primary key.
|
||||
$item->params = json_encode($item->params);
|
||||
$this->db->updateObject('#__convertforms', $item, 'id');
|
||||
}
|
||||
}
|
||||
|
||||
private function fixUploadFolder(&$item)
|
||||
{
|
||||
if (!$fields = $item->params->get('fields'))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($fields as &$field)
|
||||
{
|
||||
if ($field->type != 'fileupload')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($field->upload_folder_type))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$field->upload_folder_type = 'custom';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all SendInBlue campaigns and sets the version to v2 if not set.
|
||||
*
|
||||
* @since 2.8.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function set_sib_campaigns_default_v2_version()
|
||||
{
|
||||
if (version_compare($this->installedVersion, '2.8.4', '>='))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$campaigns = $this->getCampaigns('sendinblue'))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($campaigns as $item)
|
||||
{
|
||||
$item->params = new Registry($item->params);
|
||||
|
||||
// version exists, move on
|
||||
if ($version = $item->params->get('version'))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// if no version is found, set it to v2
|
||||
$item->params->set('version', '2');
|
||||
|
||||
// Also set update existing to default value, true
|
||||
$item->params->set('updateexisting', '1');
|
||||
|
||||
// Update item using id as the primary key.
|
||||
$item->params = json_encode($item->params);
|
||||
$this->db->updateObject('#__convertforms_campaigns', $item, 'id');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get Forms List
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function getForms()
|
||||
{
|
||||
$db = $this->db;
|
||||
$query = $db->getQuery(true);
|
||||
|
||||
$query
|
||||
->select('*')
|
||||
->from('#__convertforms');
|
||||
|
||||
$db->setQuery($query);
|
||||
|
||||
return $db->loadObjectList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get form submissions.
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function getFormSubmissions($form_id = null)
|
||||
{
|
||||
if (!$form_id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$db = $this->db;
|
||||
$query = $db->getQuery(true);
|
||||
|
||||
$query
|
||||
->select('*')
|
||||
->from('#__convertforms_conversions')
|
||||
->where($this->db->quoteName('form_id') . ' = ' . $this->db->quote($form_id));
|
||||
|
||||
$db->setQuery($query);
|
||||
|
||||
return $db->loadObjectList();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get Campaigns List
|
||||
*
|
||||
* @param string $service
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function getCampaigns($service = null)
|
||||
{
|
||||
$db = $this->db;
|
||||
$query = $db->getQuery(true);
|
||||
|
||||
$query
|
||||
->select('*')
|
||||
->from('#__convertforms_campaigns');
|
||||
|
||||
if ($service)
|
||||
{
|
||||
$query->where($this->db->quoteName('service') . ' = ' . $this->db->quote($service));
|
||||
}
|
||||
|
||||
$db->setQuery($query);
|
||||
|
||||
return $db->loadObjectList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,390 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2021 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
use ConvertForms\Helper;
|
||||
|
||||
jimport('joomla.filesystem.file');
|
||||
jimport('joomla.filesystem.folder');
|
||||
|
||||
/**
|
||||
* Services main class used by ConvertForms plugins
|
||||
*/
|
||||
class Plugin extends \JPlugin
|
||||
{
|
||||
/**
|
||||
* Application Object
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* Wrappers Directory
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $wrappersDir = '/system/nrframework/helpers/wrappers/';
|
||||
|
||||
/**
|
||||
* Lead row to manipulate
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $lead;
|
||||
|
||||
/**
|
||||
* Auto loads the plugin language file
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $autoloadLanguage = true;
|
||||
|
||||
/**
|
||||
* The campaign data.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $campaignData;
|
||||
|
||||
/**
|
||||
* Method to retrieve available lists/campaigns from API
|
||||
*
|
||||
* @param string $campaignData The Campaign's Data
|
||||
*
|
||||
* @return mixed Array on success, Throws an exception on fail
|
||||
*/
|
||||
public function getLists($campaignData)
|
||||
{
|
||||
$integration = $this->getCampaignIntegration($campaignData);
|
||||
|
||||
$api = new $integration($campaignData);
|
||||
|
||||
if (!method_exists($api, 'getLists'))
|
||||
{
|
||||
throw new \Exception('Method getLists() is missing from the ' . $this->getName() . ' wrapper');
|
||||
}
|
||||
|
||||
$lists = $api->getLists();
|
||||
|
||||
if (!$api->success())
|
||||
{
|
||||
throw new \Exception($api->getLastError());
|
||||
}
|
||||
|
||||
return $lists;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the campaign integration.
|
||||
*
|
||||
* @param array $campaignData
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getCampaignIntegration($campaignData)
|
||||
{
|
||||
$class = str_replace('plgConvertForms', '', get_class($this));
|
||||
|
||||
return '\NRFramework\Integrations\\' . $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event ServiceName - Returns the service information
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function onConvertFormsServiceName()
|
||||
{
|
||||
$service = array(
|
||||
'name' => \JText::_('PLG_CONVERTFORMS_' . strtoupper($this->getName()) . '_ALIAS'),
|
||||
'alias' => $this->getName()
|
||||
);
|
||||
|
||||
return $service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends form.xml to Campaign editing form
|
||||
*
|
||||
* @param JForm $form The form to be altered.
|
||||
* @param mixed $data The associated data for the form.
|
||||
* @param string $string The associated service name.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function onConvertFormsCampaignPrepareForm($form, $data, $service)
|
||||
{
|
||||
if ($service != $this->getName())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Try to load service form
|
||||
try
|
||||
{
|
||||
$form->loadFile($this->getForm(), false);
|
||||
$form->addFieldPath(JPATH_COMPONENT_ADMINISTRATOR . '/models/forms/fields');
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$this->app->enqueueMessage($e->getMessage(), 'error');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event that gets triggered whenever we want to retrieve service's account list
|
||||
*
|
||||
* @param array $campaignData All the Campaign Data
|
||||
*
|
||||
* @return array An array with all available lists
|
||||
*/
|
||||
public function onConvertFormsServiceLists($campaignData)
|
||||
{
|
||||
// Proceed only if we have a valid service
|
||||
if ($campaignData['service'] != $this->getName())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Load service wrapper
|
||||
$this->loadWrapper();
|
||||
|
||||
// Try to get service's account lists
|
||||
try
|
||||
{
|
||||
return $this->getLists($campaignData);
|
||||
}
|
||||
// Catch any exception
|
||||
catch (Exception $e)
|
||||
{
|
||||
Helper::log($e->getMessage(), 'error');
|
||||
return $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Syncs conversion data with the assosiated third-party service.
|
||||
* A conversion is assosiated with a Form who has a Campaign who has a Service
|
||||
* Sync is skipped if the service is empty.
|
||||
*
|
||||
* Content is passed by reference, but after the save, so no changes will be saved.
|
||||
* Method is called right after the content is saved.
|
||||
*
|
||||
* @param string $conversion The Conversion data
|
||||
* @param bool $model The Conversions Model
|
||||
* @param bool $isNew If the Conversion has just been created
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @todo Use onConvertFormsSubmissionAfterSave() event instead.
|
||||
*/
|
||||
public function onConvertFormsConversionAfterSave($conversion, $model, $isNew)
|
||||
{
|
||||
// Proceed only if we have a valid service
|
||||
if ($conversion->campaign->service != $this->getName())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate Lead
|
||||
$this->lead = clone $conversion;
|
||||
$this->validateLead();
|
||||
|
||||
// Load service wrapper
|
||||
$this->loadWrapper();
|
||||
|
||||
// Load Lead row for update
|
||||
$table = $model->getTable();
|
||||
$table->load($conversion->id);
|
||||
|
||||
$params = json_decode($table->params);
|
||||
|
||||
if (!is_object($params))
|
||||
{
|
||||
$params = new \stdClass();
|
||||
}
|
||||
|
||||
$params->sync_service = $conversion->campaign->service;
|
||||
|
||||
// Try to sync the Lead with the assosiated 3rd party service
|
||||
try
|
||||
{
|
||||
$this->subscribe($conversion);
|
||||
|
||||
// Success. Update the Lead record.
|
||||
unset($params->sync_error);
|
||||
|
||||
$table->params = json_encode($params);
|
||||
$table->store();
|
||||
|
||||
// Log debug message
|
||||
Helper::log('Lead #' . $conversion->id . ' successfully synched with ' . $params->sync_service);
|
||||
}
|
||||
|
||||
// Catch any exception and save it to the Lead row.
|
||||
// Then re-throw the same exception in order to be used by the AJAX handler.
|
||||
catch (\Exception $e)
|
||||
{
|
||||
$params->sync_error = $e->getMessage();
|
||||
$table->params = json_encode($params);
|
||||
$table->state = 0;
|
||||
$table->store();
|
||||
|
||||
// Log error message
|
||||
Helper::log('Error syncing lead #' . $conversion->id . ' with ' . $params->sync_service . " - " . $e->getMessage(), 'error');
|
||||
// Re-throw the exception
|
||||
throw new \Exception($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate lead and make sure there is at least 1 email field
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function validateLead()
|
||||
{
|
||||
if (!isset($this->lead->params) || !is_array($this->lead->params))
|
||||
{
|
||||
throw new \Exception(\JText::_('COM_CONVERTFORMS_INVALID_LEAD'));
|
||||
}
|
||||
|
||||
// First, try to find a field with a name set to 'email'.
|
||||
foreach($this->lead->params as $key => $value)
|
||||
{
|
||||
if (strtolower($key) != 'email')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Email field found!
|
||||
$this->lead->email = $value;
|
||||
|
||||
// Remove the parameter in order to avoid sending the email value twice
|
||||
unset($this->lead->params[$key]);
|
||||
}
|
||||
|
||||
// If no email field found, make a second attempt to find an email field by type
|
||||
if (!isset($this->lead->email) || empty($this->lead->email))
|
||||
{
|
||||
if (isset($this->lead->form->fields) && is_array($this->lead->form->fields))
|
||||
{
|
||||
foreach ($this->lead->form->fields as $key => $field)
|
||||
{
|
||||
if ($field['type'] != 'email')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($this->lead->params[$field['name']]))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Email field found!
|
||||
$this->lead->email = $this->lead->params[$field['name']];
|
||||
unset($this->lead->params[$field['name']]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure now we have found an email field
|
||||
if (!isset($this->lead->email) || empty($this->lead->email))
|
||||
{
|
||||
throw new \Exception(\JText::_('COM_CONVERTFORMS_FORM_IS_MISSING_THE_EMAIL_FIELD'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads Service Wrapper
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function loadWrapper()
|
||||
{
|
||||
$wrapper = $this->getWrapperFile();
|
||||
|
||||
if (!\JFile::exists($wrapper))
|
||||
{
|
||||
throw new \Exception('Wrapper ' . $wrapper . ' not found');
|
||||
}
|
||||
|
||||
return include_once($wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Service Wrapper File
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getWrapperFile()
|
||||
{
|
||||
return JPATH_PLUGINS . $this->wrappersDir . $this->getName() . '.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns form.xml file path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getForm()
|
||||
{
|
||||
$xml = JPATH_PLUGINS . '/convertforms/' . $this->getName() . '/form.xml';
|
||||
|
||||
if (!\JFile::exists($xml))
|
||||
{
|
||||
throw new \Exception('XML file is missing: ' . $xml);
|
||||
}
|
||||
|
||||
return $xml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insensitive search for array key
|
||||
*
|
||||
* @param string $name The array key to search for
|
||||
* @param array $array The array
|
||||
*
|
||||
* @return mixed False if not found, string if found
|
||||
*/
|
||||
public function findKey($name, $array)
|
||||
{
|
||||
$result = false;
|
||||
|
||||
foreach ($array as $key => $value)
|
||||
{
|
||||
if (strtolower($key) == $name)
|
||||
{
|
||||
$result = $value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get plugin name alias
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return isset($this->name) ? $this->name : $this->_name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms;
|
||||
|
||||
use ConvertForms\Helper;
|
||||
use ConvertForms\Form;
|
||||
|
||||
defined('_JEXEC') or die();
|
||||
|
||||
class SmartTags
|
||||
{
|
||||
public static function replace($string, $submission = null, $form_id = null)
|
||||
{
|
||||
// Add Submission Tags to collection
|
||||
$smartTags = new \NRFramework\SmartTags();
|
||||
|
||||
$extra_data = [];
|
||||
|
||||
if (is_object($submission))
|
||||
{
|
||||
// Get extra data from plugins that listen to the event
|
||||
\JFactory::getApplication()->triggerEvent('onConvertFormsGetSubmissionSmartTags', [$submission, &$extra_data]);
|
||||
}
|
||||
|
||||
// Register CF Smart Tags
|
||||
$smartTags->register(
|
||||
'\ConvertForms\SmartTags',
|
||||
JPATH_SITE . '/administrator/components/com_convertforms/ConvertForms/SmartTags',
|
||||
[
|
||||
'submission' => $submission,
|
||||
'extra_data' => $extra_data
|
||||
]
|
||||
);
|
||||
|
||||
$result = $smartTags->replace($string);
|
||||
|
||||
// Temporary fix for duplicate site URL.
|
||||
// Since v2.8.0 that we don't force absoluste URLs in the editors, we may don't need to fix duplicate site URL on the fly any longer.
|
||||
// We need to keep it though for a while to prevent backwards compatibility issues.
|
||||
$result = self::fixDuplicateSiteURL($result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* In TinyMCE we are forcing absolute URLs (relative_urls=false). This means that the editors prefixes all 'src' and 'href' properties
|
||||
* with the site's base URL. If we try to use a File Upload Field Smart Tag in a link like in the example below:
|
||||
*
|
||||
* <a href="{field.myuploadfield}">Download File</a>
|
||||
*
|
||||
* The editor will transform the link into
|
||||
*
|
||||
* <a href="http://www.mysite.com/{field.myuploadfield}">Download File</a>
|
||||
*
|
||||
* Even though since v2.7.4 we store the relative path instead of the absolute URL in the database, this issue is not resolved as the relative path
|
||||
* is transformed into an absolute URL by the prepareValue() method before the value arrives in this method.
|
||||
*
|
||||
* @param string $string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function fixDuplicateSiteURL($subject)
|
||||
{
|
||||
if (is_string($subject) && strpos($subject, 'http') !== false)
|
||||
{
|
||||
$domain = \JFactory::getApplication()->input->server->get('HTTP_HOST', '', 'STRING');
|
||||
/**
|
||||
* $domain returns www.site.com
|
||||
* The regex below will not be able to find the duplicate URL given the $subject:
|
||||
* https://site.com/https://www.site.com/path/to/file
|
||||
* Once we drop the "www." part from $domain, we will be able to successfully find and replace the duplicate URL.
|
||||
*/
|
||||
$domain = str_replace('www.', '', $domain);
|
||||
return preg_replace('#http(s)?:\/\/(.*?)' . $domain . '(.*?)\/http#', 'http', $subject);
|
||||
}
|
||||
|
||||
if (is_array($subject))
|
||||
{
|
||||
foreach ($subject as $key => &$item)
|
||||
{
|
||||
if (!is_string($item))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$item = self::fixDuplicateSiteURL($item);
|
||||
}
|
||||
}
|
||||
|
||||
return $subject;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2021 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms\SmartTags;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
use NRFramework\SmartTags\SmartTag;
|
||||
|
||||
/**
|
||||
* Syntax: {all_fields}
|
||||
* Hide labels: {all_fields --hideLabels=true}
|
||||
* Exclude empty values: {all_fields --excludeEmpty=true}
|
||||
* Exclude certain fields: {all_fields --excludeFields=text1,dropdown2}
|
||||
* Exclude certain field types: {all_fields --excludeTypes=text,hidden}
|
||||
*/
|
||||
class All_fields extends SmartTag
|
||||
{
|
||||
/**
|
||||
* Get All Fields value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAll_fields()
|
||||
{
|
||||
if (!$fields = $this->filteredFields())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$all_fields = '';
|
||||
|
||||
$hideLabels = $this->parsedOptions->get('hidelabels');
|
||||
|
||||
foreach ($fields as $field)
|
||||
{
|
||||
if ($hideLabels)
|
||||
{
|
||||
$all_fields .= $field->value_html . '<br>';
|
||||
continue;
|
||||
}
|
||||
|
||||
$all_fields .= '<strong>' . $field->class->getLabel() . '</strong>: ' . $field->value_html . '<br>';
|
||||
}
|
||||
|
||||
return $all_fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter submitted data with given filter options
|
||||
*
|
||||
* @return mixed Null when no submission is found, array otherwise
|
||||
*/
|
||||
private function filteredFields()
|
||||
{
|
||||
$submission = isset($this->data['submission']) ? $this->data['submission'] : '';
|
||||
|
||||
if (!$submission)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
$excludeEmpty = $this->parsedOptions->get('excludeempty', false);
|
||||
$excludeTypes = explode(',', $this->parsedOptions->get('excludetypes', ''));
|
||||
$excludeFields = explode(',', $this->parsedOptions->get('excludefields', ''));
|
||||
|
||||
return array_filter($submission->prepared_fields, function($field) use ($excludeTypes, $excludeFields, $excludeEmpty)
|
||||
{
|
||||
if ($excludeEmpty && trim($field->value) == '')
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ($excludeTypes && in_array($field->options->get('type'), $excludeTypes))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ($excludeFields && in_array($field->options->get('name'), $excludeFields))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2021 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms\SmartTags;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
/**
|
||||
* @deprecated Use {all_fields --excludeEmpty=true} instead.
|
||||
*/
|
||||
class All_fields_filled extends All_fields
|
||||
{
|
||||
/**
|
||||
* Get All Fields Filled value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAll_fields_filled()
|
||||
{
|
||||
$this->parsedOptions->set('excludeempty', true);
|
||||
return $this->getAll_fields();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms\SmartTags;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
use NRFramework\SmartTags\SmartTag;
|
||||
|
||||
class Field extends SmartTag
|
||||
{
|
||||
/**
|
||||
* Run only when we have a valid submissions object.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function canRun()
|
||||
{
|
||||
return isset($this->data['submission']) ? parent::canRun() : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch field value
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function fetchValue($key)
|
||||
{
|
||||
$submission = $this->data['submission'];
|
||||
|
||||
// Separate key parts into an array as it's very likely to have a key in the format: field.label
|
||||
$keyParts = explode('.', $key);
|
||||
$fieldName = strtolower($keyParts[0]);
|
||||
$special_param = isset($keyParts[1]) ? $keyParts[1] : null;
|
||||
$fields = $submission->prepared_fields;
|
||||
|
||||
// Check that the field name does exist in the submission data
|
||||
if (!array_key_exists($fieldName, $fields))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure $fieldName is strtolower-ed as prepared_fields is an assoc array with lower case keys.
|
||||
$field = $submission->prepared_fields[$fieldName];
|
||||
|
||||
// In case of a dropdown and radio fields, make also the label and the calc-value properties available.
|
||||
// This is rather useful when we want to display the dropdown's selected text rather than the dropdown's value.
|
||||
if (in_array($special_param, ['label', 'calcvalue', 'calc-value']) && in_array($field->options->get('type'), ['dropdown', 'radio']))
|
||||
{
|
||||
foreach ($field->class->getOptions() as $choice)
|
||||
{
|
||||
if ($field->value !== $choice['value'])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Special case: Keep old syntax: calcvalue
|
||||
$special_param = $special_param == 'calcvalue' ? 'calc-value' : $special_param;
|
||||
|
||||
if (isset($choice[$special_param]))
|
||||
{
|
||||
return $choice[$special_param];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We need to return the value of the field
|
||||
switch ($special_param)
|
||||
{
|
||||
case 'raw':
|
||||
// If we do care about performance, better call a getValueRaw() method here.
|
||||
// The raw value as saved in the database.
|
||||
return $field->value_raw;
|
||||
break;
|
||||
|
||||
case 'html':
|
||||
// The value as transformed to be shown in HTML.
|
||||
// If we do care about performance, better call a getValueHtml() method here.
|
||||
return $field->value_html;
|
||||
break;
|
||||
|
||||
default:
|
||||
// If we do care about performance, better call a getValue() method here.
|
||||
// The value in plain text. Arrays will be shown comma separated.
|
||||
return $field->value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms\SmartTags;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
use NRFramework\SmartTags\SmartTag;
|
||||
|
||||
class Link extends SmartTag
|
||||
{
|
||||
/**
|
||||
* Returns the link to a single front-end submission.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLink()
|
||||
{
|
||||
return isset($this->data['submission']->link) ? $this->data['submission']->link : '';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms\SmartTags;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
use NRFramework\SmartTags\SmartTag;
|
||||
|
||||
class Pagination extends SmartTag
|
||||
{
|
||||
/**
|
||||
* Returns the pagination links.
|
||||
* Used in Convert Forms Front End Submissions View.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLinks()
|
||||
{
|
||||
if (!isset($this->data['front_end_submission']['pagination']))
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->data['front_end_submission']['pagination']->getPagesLinks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the pagination counter.
|
||||
* Used in Convert Forms Front End Submissions View.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCounter()
|
||||
{
|
||||
if (!isset($this->data['front_end_submission']['pagination']))
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->data['front_end_submission']['pagination']->getPagesCounter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the pagination results.
|
||||
* Used in Convert Forms Front End Submissions View.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getResults()
|
||||
{
|
||||
if (!isset($this->data['front_end_submission']['pagination']))
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->data['front_end_submission']['pagination']->getResultsCounter();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms\SmartTags;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
use NRFramework\SmartTags\SmartTag;
|
||||
|
||||
class Submission extends SmartTag
|
||||
{
|
||||
/**
|
||||
* Returns the submsission ID
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getID()
|
||||
{
|
||||
return isset($this->data['submission']->id) ? $this->data['submission']->id : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the submsission User ID
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUser_ID()
|
||||
{
|
||||
return isset($this->data['submission']->user_id) ? $this->data['submission']->user_id : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the submission created date
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCreated()
|
||||
{
|
||||
return isset($this->data['submission']->created) ? $this->data['submission']->created : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the submission modified date
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getModified()
|
||||
{
|
||||
return isset($this->data['submission']->modified) ? $this->data['submission']->modified : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the submission created date
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDate()
|
||||
{
|
||||
return isset($this->data['submission']->created) ? $this->data['submission']->created : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the submission campaign id
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCampaign_ID()
|
||||
{
|
||||
return isset($this->data['submission']->campaign_id) ? $this->data['submission']->campaign_id : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the submission form id
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getForm_ID()
|
||||
{
|
||||
return isset($this->data['submission']->form_id) ? $this->data['submission']->form_id : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the submission visitor id
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getVisitor_ID()
|
||||
{
|
||||
return isset($this->data['submission']->visitor_id) ? $this->data['submission']->visitor_id : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the submission status
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getStatus()
|
||||
{
|
||||
return isset($this->data['submission']->state) && $this->data['submission']->state === '1' ? \JText::_('COM_CONVERTFORMS_SUBMISSION_CONFIRMED') : \JText::_('COM_CONVERTFORMS_SUBMISSION_UNCONFIRMED');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the submission PDF
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPDF()
|
||||
{
|
||||
if (!isset($this->data['extra_data']['pdf']))
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->data['extra_data']['pdf'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms\SmartTags;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
use NRFramework\SmartTags\SmartTag;
|
||||
use ConvertForms\Helper;
|
||||
|
||||
class Submissions extends SmartTag
|
||||
{
|
||||
/**
|
||||
* Returns the submsissions data.
|
||||
* Used in Convert Forms Front End Submissions View.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSubmissions()
|
||||
{
|
||||
$data = isset($this->data['front_end_submission']) ? $this->data['front_end_submission'] : [];
|
||||
|
||||
if (!$data)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
$submissions = isset($data['submissions']) ? $data['submissions'] : '';
|
||||
|
||||
if (!$submissions)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
$layout_row = isset($data['layout_row']) ? $data['layout_row'] : '';
|
||||
|
||||
if (!$layout_row)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
$html = '';
|
||||
|
||||
foreach ($submissions as $submission)
|
||||
{
|
||||
$html .= \ConvertForms\Submission::replaceSmartTags($submission, $layout_row);
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the submsissions count
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCount()
|
||||
{
|
||||
$submission = isset($this->data['submission']) ? $this->data['submission'] : null;
|
||||
|
||||
if (!$submission)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
$form_id = isset($submission->form_id) ? $submission->form_id : null;
|
||||
|
||||
if (!$form_id)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Helper::getFormLeadsCount($form_id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms\SmartTags;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
use NRFramework\SmartTags\SmartTag;
|
||||
|
||||
class Total extends SmartTag
|
||||
{
|
||||
/**
|
||||
* Returns the total submissions.
|
||||
* Used in Convert Forms Front End Submissions View.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTotal()
|
||||
{
|
||||
return isset($this->data['front_end_submission']['total']) ? $this->data['front_end_submission']['total'] : '';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
class Submission
|
||||
{
|
||||
/**
|
||||
* Replaces the Smart Tags of a submission.
|
||||
*
|
||||
* @param object $submission
|
||||
* @param string $layout
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function replaceSmartTags($submission, $layout)
|
||||
{
|
||||
$st = new \NRFramework\SmartTags();
|
||||
|
||||
// Register CF Smart Tags
|
||||
$st->register(
|
||||
'\ConvertForms\SmartTags',
|
||||
JPATH_SITE . '/administrator/components/com_convertforms/ConvertForms/SmartTags',
|
||||
[
|
||||
'submission' => $submission
|
||||
]
|
||||
);
|
||||
|
||||
return $st->replace($layout);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2022 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms;
|
||||
|
||||
defined('_JEXEC') or die('Restricted access');
|
||||
|
||||
class SubmissionMeta
|
||||
{
|
||||
/**
|
||||
* Logs table
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private static $table = '#__convertforms_submission_meta';
|
||||
|
||||
/**
|
||||
* Adds or updates a record in the database
|
||||
*
|
||||
* @param int $submission_id
|
||||
* @param string $type
|
||||
* @param string $key
|
||||
* @param string $value
|
||||
* @param array $params
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function save($submission_id, $type, $key = '', $value = '', $params = [])
|
||||
{
|
||||
if ($record = self::getMeta($submission_id, $type, $key))
|
||||
{
|
||||
// Update existing record
|
||||
$data = (object) [
|
||||
'id' => $record['id'],
|
||||
'submission_id' => $submission_id,
|
||||
'meta_type' => $type,
|
||||
'meta_key' => $key,
|
||||
'meta_value' => $value,
|
||||
'params' => json_encode($params),
|
||||
'date_modified' => \JFactory::getDate()->toSql()
|
||||
];
|
||||
|
||||
try
|
||||
{
|
||||
\JFactory::getDbo()->updateObject(self::$table, $data, 'id');
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Add new record
|
||||
self::add($submission_id, $type, $key, $value, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a Submission Meta.
|
||||
*
|
||||
* @param int $submission_id
|
||||
* @param string $type
|
||||
* @param string $key
|
||||
* @param string $value
|
||||
* @param array $params
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function add($submission_id, $type, $key = '', $value = '', $params = [])
|
||||
{
|
||||
if (!$submission_id || !$type || !$value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Data to save
|
||||
$data = (object) [
|
||||
'submission_id' => $submission_id,
|
||||
'meta_type' => $type,
|
||||
'meta_key' => $key,
|
||||
'meta_value' => $value,
|
||||
'params' => json_encode($params),
|
||||
'date_created' => \JFactory::getDate()->toSql()
|
||||
];
|
||||
|
||||
// Insert the data
|
||||
try
|
||||
{
|
||||
\JFactory::getDbo()->insertObject(self::$table, $data);
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the meta row.
|
||||
*
|
||||
* @param int $submission_id
|
||||
* @param string $type
|
||||
* @param string $key
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getMeta($submission_id, $type, $key = '')
|
||||
{
|
||||
if (!$submission_id || !$type)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$db = \JFactory::getDbo();
|
||||
$query = $db->getQuery(true);
|
||||
$query
|
||||
->select('*')
|
||||
->from($db->quoteName(self::$table))
|
||||
->where($db->quoteName('submission_id') . ' = ' . $db->quote($submission_id))
|
||||
->where($db->quoteName('meta_type') . ' = ' . $db->quote($type));
|
||||
|
||||
if (!empty($key))
|
||||
{
|
||||
$query->where($db->quoteName('meta_key') . ' = ' . $db->quote($key));
|
||||
}
|
||||
|
||||
$db->setQuery($query);
|
||||
|
||||
return $db->loadAssoc();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves meta value.
|
||||
*
|
||||
* @param int $submission_id
|
||||
* @param string $type
|
||||
* @param string $key
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getValue($submission_id, $type, $key = '')
|
||||
{
|
||||
if (!$data = self::getMeta($submission_id, $type, $key))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
return $data['meta_value'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a submission meta
|
||||
*
|
||||
* @param int $submission_id
|
||||
* @param string $type
|
||||
* @param string $key
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function delete($submission_id, $type, $key = '')
|
||||
{
|
||||
if (!$submission_id || !$type)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$db = \JFactory::getDbo();
|
||||
|
||||
$query = $db->getQuery(true)
|
||||
->delete($db->quoteName(self::$table))
|
||||
->where($db->quoteName('submission_id') . ' = ' . $db->quote($submission_id))
|
||||
->where($db->quoteName('meta_type') . ' = ' . $db->quote($type));
|
||||
|
||||
if (!empty($key))
|
||||
{
|
||||
$query->where($db->quoteName('meta_key') . ' = ' . $db->quote($key));
|
||||
}
|
||||
|
||||
$db->setQuery($query);
|
||||
|
||||
return $db->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a list of submission meta by ID
|
||||
*
|
||||
* @param array $ids
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function deleteAll($ids)
|
||||
{
|
||||
if (!is_array($ids))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$db = \JFactory::getDbo();
|
||||
|
||||
$query = $db->getQuery(true)
|
||||
->delete($db->quoteName(self::$table))
|
||||
->where($db->quoteName('id') . ' IN (' . implode(', ', (array) $ids) . ')');
|
||||
|
||||
$db->setQuery($query);
|
||||
|
||||
return $db->execute();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Convert Forms
|
||||
* @version 3.2.12 Free
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace ConvertForms;
|
||||
|
||||
// No direct access to this file
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
class Validate
|
||||
{
|
||||
/**
|
||||
* Check if given email address is valid
|
||||
*
|
||||
* @param String $email The email address to check
|
||||
*
|
||||
* @return Boolean Return true if the email address is valid
|
||||
*/
|
||||
public static function email($email)
|
||||
{
|
||||
return filter_var($email, FILTER_VALIDATE_EMAIL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check DNS records corresponding to a given email address
|
||||
*
|
||||
* @param String $email The email address to check
|
||||
*
|
||||
* @return Boolean Return true if the email address has valid MX records
|
||||
*/
|
||||
public static function emaildns($email)
|
||||
{
|
||||
// Check if it's an email address format
|
||||
if (!self::email($email))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
list($user, $domain) = explode('@', $email, 2);
|
||||
|
||||
// checkdnsrr for PHP < 5.3.0
|
||||
if (!function_exists('checkdnsrr') && function_exists('exec') && is_callable('exec'))
|
||||
{
|
||||
@exec('nslookup -type=MX ' . escapeshellcmd($domain), $output);
|
||||
|
||||
foreach($output as $line)
|
||||
{
|
||||
if (preg_match('/^' . preg_quote($domain) . '/', $line))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// fallback method...
|
||||
if (!function_exists('checkdnsrr') || !is_callable('checkdnsrr'))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return checkdnsrr($domain, substr(PHP_OS, 0, 3) == 'WIN' ? 'A' : 'MX');
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the given date.
|
||||
*
|
||||
* Note: This method is limited to validate only english-based dates
|
||||
* as the Date PHP class doesn't respect current locale without using hacks
|
||||
*
|
||||
* @param String $date The date string
|
||||
* @param String $format The date format to be used
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function dateFormat($date, $format = null)
|
||||
{
|
||||
return \JDate::createFromFormat($format, trim($date));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates URL
|
||||
*
|
||||
* @param string $url
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function url($url)
|
||||
{
|
||||
return filter_var($url, FILTER_VALIDATE_URL);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<addons>
|
||||
<addon name="mailchimp"
|
||||
label="MailChimp"
|
||||
description="Convert Forms MailChimp addon allows you to create MailChimp newsletter signup forms in Joomla."
|
||||
image="https://static.tassos.gr/images/integrations/mailchimp.png"
|
||||
docalias="sync-leads-with-mailchimp"
|
||||
/>
|
||||
<addon name="getresponse"
|
||||
label="GetResponse"
|
||||
description="Convert Forms GetResponse addon allows you to create GetResponse newsletter signup forms in Joomla."
|
||||
image="https://static.tassos.gr/images/integrations/get-response.png"
|
||||
docalias="sync-leads-with-getresponse"
|
||||
/>
|
||||
<addon name="activecampaign"
|
||||
label="Active Campaign"
|
||||
description="Convert Forms ActiveCampaign addon allows you to create ActiveCampaign newsletter signup forms in Joomla."
|
||||
image="https://static.tassos.gr/images/integrations/active-campaign.png"
|
||||
docalias="sync-leads-with-activecampaign"
|
||||
/>
|
||||
<addon name="acymailing"
|
||||
label="AcyMailing"
|
||||
description="Convert Forms AcyMailing addon allows you to create AcyMailing newsletter signup forms in Joomla."
|
||||
image="https://static.tassos.gr/images/integrations/acymailing.png"
|
||||
docalias="sync-leads-with-acymailing"
|
||||
proonly="false"
|
||||
/>
|
||||
<addon name="aweber"
|
||||
label="AWeber"
|
||||
description="Convert Forms AWeber addon allows you to create AWeber newsletter signup forms in Joomla."
|
||||
image="https://static.tassos.gr/images/integrations/aweber.png"
|
||||
docalias="sync-leads-with-aweber"
|
||||
/>
|
||||
<addon name="campaignmonitor"
|
||||
label="Campaign Monitor"
|
||||
description="Convert Forms CampaignMonitor addon allows you to create CampaignMonitor newsletter signup forms in Joomla."
|
||||
image="https://static.tassos.gr/images/integrations/campaign-monitor.png"
|
||||
docalias="sync-leads-with-campaignmonitor"
|
||||
/>
|
||||
<addon name="convertkit"
|
||||
label="ConvertKit"
|
||||
description="Convert Forms ConvertKit addon allows you to create ConvertKit newsletter signup forms in Joomla."
|
||||
image="https://static.tassos.gr/images/integrations/convertkit.png"
|
||||
docalias="sync-leads-with-convertkit"
|
||||
/>
|
||||
<addon name="hubspot"
|
||||
label="HubSpot"
|
||||
description="Convert Forms HubSpot addon allows you to create HubSpot newsletter signup forms in Joomla."
|
||||
image="https://static.tassos.gr/images/integrations/hubspot.png"
|
||||
docalias="sync-leads-with-hubspot"
|
||||
/>
|
||||
<addon name="sendinblue"
|
||||
label="SendinBlue"
|
||||
description="Convert Forms SendinBlue addon allows you to create SendinBlue newsletter signup forms in Joomla."
|
||||
image="https://static.tassos.gr/images/integrations/sendinblue.png"
|
||||
docalias="sync-leads-with-sendinblue"
|
||||
/>
|
||||
<addon name="zapier"
|
||||
label="Zapier"
|
||||
description="Convert Forms Zapier addon allows you to connect Your Joomla Forms with 500+ Web Apps."
|
||||
image="https://static.tassos.gr/images/integrations/zapier.png"
|
||||
docalias="how-to-connect-convertforms-with-zapier"
|
||||
/>
|
||||
<addon name="zoho"
|
||||
label="Zoho Campaigns"
|
||||
description="Convert Forms Zoho addon allows you to connect Your Joomla Forms with Zoho Campaigns."
|
||||
image="https://static.tassos.gr/images/integrations/zoho.png"
|
||||
docalias="sync-leads-with-zoho"
|
||||
/>
|
||||
<addon name="elasticemail"
|
||||
label="Elastic Email"
|
||||
description="Convert Forms Elastic Email addon allows you to create Elastic Email newsletter signup forms in Joomla."
|
||||
image="https://static.tassos.gr/images/integrations/elasticemail.png"
|
||||
docalias="sync-leads-with-elasticemail"
|
||||
/>
|
||||
<addon name="icontact"
|
||||
label="iContact"
|
||||
description="Convert Forms iContact addon allows you to create iContact newsletter signup forms in Joomla."
|
||||
image="https://static.tassos.gr/images/integrations/icontact.png"
|
||||
docalias="how-to-connect-convertforms-with-icontact"
|
||||
/>
|
||||
<addon name="zohocrm"
|
||||
label="Zoho CRM"
|
||||
description="Convert Forms Zoho CRM addon allows you to sync your Submissions with Zoho CRM's modules."
|
||||
image="https://static.tassos.gr/images/integrations/zohocrm.png"
|
||||
docalias="sync-leads-with-zohocrm"
|
||||
/>
|
||||
<addon name="salesforce"
|
||||
label="SalesForce Web-to-Lead"
|
||||
description="Convert Forms SalesForce addon allows you to create SalesForce Web-to-Lead forms in Joomla."
|
||||
image="https://static.tassos.gr/images/integrations/salesforce.png"
|
||||
docalias="sync-leads-with-salesforce"
|
||||
/>
|
||||
<addon name="drip"
|
||||
label="Drip"
|
||||
description="Convert Forms Drip addon allows you to sync form submissions with the Drip Ecommerce CRM."
|
||||
image="https://static.tassos.gr/images/integrations/drip.png"
|
||||
docalias="sync-leads-with-drip"
|
||||
/>
|
||||
<!-- Coming Soon -->
|
||||
<addon name="constantcontact"
|
||||
label="Constant Contact"
|
||||
description="Convert Forms ConstantContact addon allows you to create ConstantContact newsletter signup forms in Joomla."
|
||||
image="https://static.tassos.gr/images/integrations/constant-contact.png"
|
||||
comingsoon="true"
|
||||
docalias="how-to-connect-convert-forms-with-constant-contact"
|
||||
/>
|
||||
</addons>
|
||||
@@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form>
|
||||
<fieldset name="basic">
|
||||
<field name="key" type="hidden"/>
|
||||
<field name="type" type="hidden"/>
|
||||
<field name="name" type="text"
|
||||
label="COM_CONVERTFORMS_FIELD_NAME"
|
||||
description="COM_CONVERTFORMS_FIELD_NAME_DESC"
|
||||
hint="COM_CONVERTFORMS_FIELD_NAME"
|
||||
/>
|
||||
<field name="label" type="text"
|
||||
label="COM_CONVERTFORMS_FIELD_LABEL"
|
||||
description="COM_CONVERTFORMS_FIELD_LABEL_DESC"
|
||||
hint="COM_CONVERTFORMS_FIELD_LABEL"
|
||||
/>
|
||||
<field name="description" type="textarea"
|
||||
label="COM_CONVERTFORMS_FIELD_DESCRIPTION"
|
||||
description="COM_CONVERTFORMS_FIELD_DESCRIPTION_DESC"
|
||||
filter="raw"
|
||||
/>
|
||||
<field name="required" type="nrtoggle"
|
||||
label="COM_CONVERTFORMS_REQUIRED_NAME"
|
||||
description="COM_CONVERTFORMS_REQUIRED_NAME_DESC"
|
||||
checked="true"
|
||||
/>
|
||||
</fieldset>
|
||||
<fieldset name="advanced">
|
||||
<field name="size" type="list"
|
||||
label="COM_CONVERTFORMS_FIELD_SIZE"
|
||||
description="COM_CONVERTFORMS_FIELD_SIZE_DESC"
|
||||
default="">
|
||||
<option value="cf-width-auto">Auto</option>
|
||||
<option value="cf-one-third">Small</option>
|
||||
<option value="cf-one-half">Medium</option>
|
||||
<option value="">Large</option>
|
||||
</field>
|
||||
<field name="value" type="textarea"
|
||||
label="COM_CONVERTFORMS_FIELD_VALUE"
|
||||
description="COM_CONVERTFORMS_FIELD_VALUE_DESC"
|
||||
class="show-smart-tags"
|
||||
/>
|
||||
<field name="placeholder" type="text"
|
||||
label="COM_CONVERTFORMS_PLACEHOLDER_NAME"
|
||||
description="COM_CONVERTFORMS_PLACEHOLDER_NAME_DESC"
|
||||
/>
|
||||
<field name="cssclass" type="cssclasses"
|
||||
label="COM_CONVERTFORMS_CSS_CLASSES"
|
||||
description="COM_CONVERTFORMS_CSS_CLASSES_DESC"
|
||||
/>
|
||||
<field name="inputcssclass" type="text"
|
||||
label="COM_CONVERTFORMS_INPUT_CSS_CLASSES"
|
||||
description="COM_CONVERTFORMS_INPUT_CSS_CLASSES_DESC"
|
||||
/>
|
||||
<field name="hidelabel" type="nrtoggle"
|
||||
label="COM_CONVERTFORMS_HIDE_LABELS"
|
||||
description="COM_CONVERTFORMS_HIDE_LABELS_DESC"
|
||||
/>
|
||||
<field name="browserautocomplete" type="nrtoggle"
|
||||
label="COM_CONVERTFORMS_DISABLE_BROWSER_AUTOCOMPLETE"
|
||||
description="COM_CONVERTFORMS_DISABLE_BROWSER_AUTOCOMPLETE_DESC"
|
||||
/>
|
||||
</fieldset>
|
||||
</form>
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form>
|
||||
<fieldset name="basic">
|
||||
<field name="complexity" type="list"
|
||||
label="COM_CONVERTFORMS_FIELD_CAPTCHA_COMPLEXITY"
|
||||
description="COM_CONVERTFORMS_FIELD_CAPTCHA_COMPLEXITY_DESC">
|
||||
<option value="">NR_LOW</option>
|
||||
<option value="medium">NR_MEDIUM</option>
|
||||
<option value="high">NR_HIGH</option>
|
||||
</field>
|
||||
<field name="wrong_answer_text" type="text"
|
||||
label="COM_CONVERTFORMS_FIELD_CAPTCHA_WRONG_ANSWER"
|
||||
description="COM_CONVERTFORMS_FIELD_CAPTCHA_WRONG_ANSWER_DESC"
|
||||
default="Wrong answer!"
|
||||
hint="Wrong answer!"
|
||||
/>
|
||||
<field name="placeholder" type="text"
|
||||
label="COM_CONVERTFORMS_PLACEHOLDER_NAME"
|
||||
description="COM_CONVERTFORMS_PLACEHOLDER_NAME_DESC"
|
||||
hint="Type in the result of the equation"
|
||||
/>
|
||||
</fieldset>
|
||||
</form>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form>
|
||||
<fieldset name="basic">
|
||||
<field name="choices" type="nr_choices"
|
||||
label="COM_CONVERTFORMS_CHOICES"
|
||||
description="COM_CONVERTFORMS_CHOICES_DESC"
|
||||
/>
|
||||
</fieldset>
|
||||
<fieldset name="advanced">
|
||||
<field name="choicelayout" type="list"
|
||||
label="COM_CONVERTFORMS_CHOICE_LAYOUT"
|
||||
description="COM_CONVERTFORMS_CHOICE_LAYOUT_DESC"
|
||||
default="">
|
||||
<option value="">COM_CONVERTFORMS_CHOICE_1_COLUMN</option>
|
||||
<option value="2">COM_CONVERTFORMS_CHOICE_2_COLUMN</option>
|
||||
<option value="3">COM_CONVERTFORMS_CHOICE_3_COLUMN</option>
|
||||
<option value="auto">COM_CONVERTFORMS_CHOICE_SIDE_BY_SIDE</option>
|
||||
</field>
|
||||
</fieldset>
|
||||
</form>
|
||||
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form>
|
||||
<fieldset name="basic">
|
||||
<field name="border_style" type="list"
|
||||
label="COM_CONVERTFORMS_FIELD_BORDER_STYLE"
|
||||
description="COM_CONVERTFORMS_FIELD_BORDER_STYLE_DESC"
|
||||
default="dashed">
|
||||
<option value="dashed">COM_CONVERTFORMS_FIELD_BORDER_STYLE_DASHED</option>
|
||||
<option value="solid">COM_CONVERTFORMS_FIELD_BORDER_STYLE_SOLID</option>
|
||||
<option value="dotted">COM_CONVERTFORMS_FIELD_BORDER_STYLE_DOTTED</option>
|
||||
</field>
|
||||
<field name="border_width" type="nrnumber"
|
||||
label="COM_CONVERTFORMS_FIELD_BORDER_WIDTH"
|
||||
description="COM_CONVERTFORMS_FIELD_BORDER_WIDTH_DESC"
|
||||
addon="px"
|
||||
min="0"
|
||||
default="1"
|
||||
/>
|
||||
<field name="border_color" type="nrcolor"
|
||||
label="COM_CONVERTFORMS_FIELD_BORDER_COLOR"
|
||||
description="COM_CONVERTFORMS_FIELD_BORDER_COLOR_DESC"
|
||||
default="#ccc"
|
||||
/>
|
||||
<field name="margin_top" type="nrnumber"
|
||||
label="COM_CONVERTFORMS_FIELD_MARGIN_TOP"
|
||||
description="COM_CONVERTFORMS_FIELD_MARGIN_TOP_DESC"
|
||||
addon="px"
|
||||
min="0"
|
||||
default="0"
|
||||
/>
|
||||
<field name="margin_bottom" type="nrnumber"
|
||||
label="COM_CONVERTFORMS_FIELD_MARGIN_BOTTOM"
|
||||
description="COM_CONVERTFORMS_FIELD_MARGIN_BOTTOM_DESC"
|
||||
addon="px"
|
||||
min="0"
|
||||
default="0"
|
||||
/>
|
||||
</fieldset>
|
||||
</form>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form>
|
||||
<fieldset name="basic">
|
||||
<field name="choices" type="nr_choices"
|
||||
label="COM_CONVERTFORMS_CHOICES"
|
||||
description="COM_CONVERTFORMS_CHOICES_DESC"
|
||||
/>
|
||||
</fieldset>
|
||||
</form>
|
||||
@@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form>
|
||||
<fieldset name="basic">
|
||||
<field name="editor" type="plugins"
|
||||
label="COM_CONVERTFORMS_EDITOR_SELECT"
|
||||
description="COM_CONVERTFORMS_EDITOR_SELECT_DESC"
|
||||
folder="editors">
|
||||
<option value="">JOPTION_USE_DEFAULT</option>
|
||||
</field>
|
||||
<field name="height" type="nrnumber"
|
||||
label="COM_CONVERTFORMS_EDITOR_HEIGHT"
|
||||
description="COM_CONVERTFORMS_EDITOR_HEIGHT_DESC"
|
||||
addon="px"
|
||||
default="300"
|
||||
step="50"
|
||||
min="0"
|
||||
/>
|
||||
</fieldset>
|
||||
<fieldset name="restrictions">
|
||||
<field name="readonly" type="nrtoggle"
|
||||
label="COM_CONVERTFORMS_READONLY"
|
||||
description="COM_CONVERTFORMS_READONLY_DESC"
|
||||
/>
|
||||
<field name="minchars" type="nrnumber"
|
||||
label="COM_CONVERTFORMS_MIN_CHARS"
|
||||
description="COM_CONVERTFORMS_MIN_CHARS_DESC"
|
||||
default="0"
|
||||
min="0"
|
||||
/>
|
||||
<field name="maxchars" type="nrnumber"
|
||||
label="COM_CONVERTFORMS_MAX_CHARS"
|
||||
description="COM_CONVERTFORMS_MAX_CHARS_DESC"
|
||||
default="0"
|
||||
min="0"
|
||||
/>
|
||||
<field name="minwords" type="nrnumber"
|
||||
label="COM_CONVERTFORMS_MIN_WORDS"
|
||||
description="COM_CONVERTFORMS_MIN_WORDS_DESC"
|
||||
default="0"
|
||||
min="0"
|
||||
/>
|
||||
<field name="maxwords" type="nrnumber"
|
||||
label="COM_CONVERTFORMS_MAX_WORDS"
|
||||
description="COM_CONVERTFORMS_MAX_WORDS_DESC"
|
||||
default="0"
|
||||
min="0"
|
||||
/>
|
||||
</fieldset>
|
||||
<fieldset name="advanced">
|
||||
<field name="filter" type="list"
|
||||
label="COM_CONVERTFORMS_FIELD_FILTER"
|
||||
description="COM_CONVERTFORMS_FIELD_FILTER_DESC"
|
||||
default="safehtml">
|
||||
<option value="safehtml">JLIB_FILTER_PARAMS_SAFEHTML</option>
|
||||
<option value="raw">JLIB_FILTER_PARAMS_RAW</option>
|
||||
</field>
|
||||
</fieldset>
|
||||
</form>
|
||||
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form>
|
||||
<fieldset name="restrictions">
|
||||
<field name="readonly" type="nrtoggle"
|
||||
label="COM_CONVERTFORMS_READONLY"
|
||||
description="COM_CONVERTFORMS_READONLY_DESC"
|
||||
/>
|
||||
</fieldset>
|
||||
<fieldset name="basic">
|
||||
<field name="dnscheck" type="nrtoggle"
|
||||
label="COM_CONVERTFORMS_FIELD_EMAIL_DNSCHECK"
|
||||
description="COM_CONVERTFORMS_FIELD_EMAIL_DNSCHECK_DESC"
|
||||
/>
|
||||
</fieldset>
|
||||
</form>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form>
|
||||
<fieldset name="basic">
|
||||
<field name="gap" type="nrnumber"
|
||||
label="COM_CONVERTFORMS_FIELD_EMPTY_SPACE_GAP"
|
||||
description="COM_CONVERTFORMS_FIELD_EMPTY_SPACE_GAP_DESC"
|
||||
addon="px"
|
||||
min="0"
|
||||
default="20"
|
||||
/>
|
||||
</fieldset>
|
||||
</form>
|
||||
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form>
|
||||
<fieldset name="basic">
|
||||
<field name="upload_folder_type" type="list"
|
||||
label="COM_CONVERTFORMS_FILEUPLOAD_FOLDER_TYPE"
|
||||
description="COM_CONVERTFORMS_FILEUPLOAD_FOLDER_TYPE_DESC"
|
||||
default="auto">
|
||||
<option value="auto">NR_AUTO</option>
|
||||
<option value="custom">NR_CUSTOM</option>
|
||||
</field>
|
||||
<field name="upload_folder" type="text"
|
||||
label="COM_CONVERTFORMS_FILEUPLOAD_FOLDER"
|
||||
description="COM_CONVERTFORMS_FILEUPLOAD_FOLDER_DESC"
|
||||
hint="media/com_convertforms/uploads"
|
||||
class="show-smart-tags"
|
||||
showon="upload_folder_type:custom"
|
||||
/>
|
||||
|
||||
<field name="max_file_size" type="nrnumber"
|
||||
label="COM_CONVERTFORMS_FILEUPLOAD_MAX_FILE_SIZE"
|
||||
description="COM_CONVERTFORMS_FILEUPLOAD_MAX_FILE_SIZE_DESC"
|
||||
addon="MB"
|
||||
min="0"
|
||||
default="0"
|
||||
/>
|
||||
<field name="upload_types" type="text"
|
||||
label="COM_CONVERTFORMS_FILEUPLOAD_UPLOAD_TYPES"
|
||||
description="COM_CONVERTFORMS_FILEUPLOAD_UPLOAD_TYPES_DESC"
|
||||
hint=".jpg, .png, .gif"
|
||||
default=".jpg, .png, .gif"
|
||||
/>
|
||||
<field name="allow_unsafe" type="nrtoggle"
|
||||
label="COM_CONVERTFORMS_UPLOAD_ALLOW_UNSAFE"
|
||||
description="COM_CONVERTFORMS_UPLOAD_ALLOW_UNSAFE_DESC"
|
||||
/>
|
||||
|
||||
|
||||
<field name="@limit_files" type="nr_pro"
|
||||
label="COM_CONVERTFORMS_FILEUPLOAD_LIMIT_FILES"
|
||||
description="COM_CONVERTFORMS_FILEUPLOAD_LIMIT_FILES_DESC"
|
||||
/>
|
||||
<field name="@auto_delete_files" type="nr_pro"
|
||||
label="COM_CONVERTFORMS_UPLOAD_AUTO_DELETE"
|
||||
description="COM_CONVERTFORMS_UPLOAD_AUTO_DELETE_DESC"
|
||||
/>
|
||||
|
||||
</fieldset>
|
||||
</form>
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form>
|
||||
<fieldset name="basic">
|
||||
<field name="hcaptcha_type" type="list"
|
||||
label="NR_TYPE"
|
||||
description="COM_CONVERTFORMS_FIELD_HCAPTCHA_TYPE_DESC"
|
||||
default="checkbox">
|
||||
<option value="checkbox">NR_CHECKBOX</option>
|
||||
<option value="invisible">NR_INVISIBLE</option>
|
||||
</field>
|
||||
<field name="theme" type="list"
|
||||
label="NR_THEME"
|
||||
description="COM_CONVERTFORMS_FIELD_RECAPTCHA_THEME_DESC"
|
||||
showon="hcaptcha_type:checkbox"
|
||||
default="light">
|
||||
<option value="light">NR_LIGHT</option>
|
||||
<option value="dark">NR_DARK</option>
|
||||
</field>
|
||||
<field name="size" type="list"
|
||||
label="NR_SIZE"
|
||||
description="COM_CONVERTFORMS_FIELD_RECAPTCHA_SIZE_DESC"
|
||||
showon="hcaptcha_type:checkbox"
|
||||
default="normal">
|
||||
<option value="normal">NR_NORMAL</option>
|
||||
<option value="compact">NR_COMPACT</option>
|
||||
</field>
|
||||
</fieldset>
|
||||
</form>
|
||||
@@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form>
|
||||
<fieldset name="basic">
|
||||
<field name="heading_type" type="list"
|
||||
label="COM_CONVERTFORMS_FIELD_HEADING_HEADINGTYPE"
|
||||
description="COM_CONVERTFORMS_FIELD_HEADING_HEADINGTYPE_DESC"
|
||||
default="h2">
|
||||
<option value="h1">H1</option>
|
||||
<option value="h2">H2</option>
|
||||
<option value="h3">H3</option>
|
||||
<option value="h4">H4</option>
|
||||
<option value="h5">H5</option>
|
||||
<option value="h6">H6</option>
|
||||
</field>
|
||||
<field name="font_size" type="nrnumber"
|
||||
label="COM_CONVERTFORMS_INPUT_FONT_SIZE"
|
||||
description="COM_CONVERTFORMS_FIELD_HEADING_FONT_SIZE_DESC"
|
||||
addon="px"
|
||||
min="0"
|
||||
default="22"
|
||||
/>
|
||||
<field name="font_family" type="nrfonts"
|
||||
class="nrfont"
|
||||
label="COM_CONVERTFORMS_BODY_FONT"
|
||||
description="COM_CONVERTFORMS_FIELD_HEADING_FONT_FAMILY_DESC"
|
||||
default="Arial">
|
||||
<option value=" ">JDEFAULT</option>
|
||||
</field>
|
||||
<field name="line_height" type="nrnumber"
|
||||
label="COM_CONVERTFORMS_FIELD_HEADING_LINE_HEIGHT"
|
||||
description="COM_CONVERTFORMS_FIELD_HEADING_LINE_HEIGHT_DESC"
|
||||
addon="px"
|
||||
min="0"
|
||||
default="22"
|
||||
/>
|
||||
<field name="letter_spacing" type="nrnumber"
|
||||
label="COM_CONVERTFORMS_FIELD_HEADING_LETTER_SPACING"
|
||||
description="COM_CONVERTFORMS_FIELD_HEADING_LETTER_SPACING_DESC"
|
||||
addon="px"
|
||||
min="0"
|
||||
default="0"
|
||||
/>
|
||||
<field name="content_alignment" type="list"
|
||||
label="COM_CONVERTFORMS_FIELD_HEADING_CONTENT_ALIGNMENT"
|
||||
description="COM_CONVERTFORMS_FIELD_HEADING_CONTENT_ALIGNMENT_DESC"
|
||||
default="none">
|
||||
<option value="left">NR_LEFT</option>
|
||||
<option value="center">NR_CENTER</option>
|
||||
<option value="right">NR_RIGHT</option>
|
||||
</field>
|
||||
<field name="use_link" type="nrtoggle"
|
||||
label="COM_CONVERTFORMS_FIELD_HEADING_USELINK"
|
||||
description="COM_CONVERTFORMS_FIELD_HEADING_USELINK_DESC"
|
||||
/>
|
||||
<field name="link_url" type="text"
|
||||
label="COM_CONVERTFORMS_FIELD_HEADING_LINK"
|
||||
description="COM_CONVERTFORMS_FIELD_HEADING_LINK_DESC"
|
||||
hint="http://"
|
||||
showon="use_link:1"
|
||||
/>
|
||||
<field name="open_new_tab" type="nrtoggle"
|
||||
label="COM_CONVERTFORMS_FIELD_HEADING_NEWTAB"
|
||||
description="COM_CONVERTFORMS_FIELD_HEADING_NEWTAB_DESC"
|
||||
showon="use_link:1"
|
||||
/>
|
||||
</fieldset>
|
||||
</form>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form>
|
||||
<fieldset name="basic">
|
||||
<field name="required" type="nrtoggle"
|
||||
label="COM_CONVERTFORMS_REQUIRED_NAME"
|
||||
description="COM_CONVERTFORMS_REQUIRED_NAME_DESC"
|
||||
/>
|
||||
</fieldset>
|
||||
</form>
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form>
|
||||
<fieldset name="basic">
|
||||
<field name="step" type="nrnumber"
|
||||
label="COM_CONVERTFORMS_FIELD_NUMBER_STEP"
|
||||
description="COM_CONVERTFORMS_FIELD_NUMBER_STEP_DESC"
|
||||
default="1"
|
||||
min="1"
|
||||
/>
|
||||
<field name="min" type="nrnumber"
|
||||
label="COM_CONVERTFORMS_FIELD_NUMBER_MIN"
|
||||
description="COM_CONVERTFORMS_FIELD_NUMBER_MIN_DESC"
|
||||
/>
|
||||
<field name="max" type="nrnumber"
|
||||
label="COM_CONVERTFORMS_FIELD_NUMBER_MAX"
|
||||
description="COM_CONVERTFORMS_FIELD_NUMBER_MAX_DESC"
|
||||
/>
|
||||
</fieldset>
|
||||
<fieldset name="restrictions">
|
||||
<field name="readonly" type="nrtoggle"
|
||||
label="COM_CONVERTFORMS_READONLY"
|
||||
description="COM_CONVERTFORMS_READONLY_DESC"
|
||||
/>
|
||||
</fieldset>
|
||||
</form>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form>
|
||||
<fieldset name="basic">
|
||||
<field name="choices" type="nr_choices"
|
||||
label="COM_CONVERTFORMS_CHOICES"
|
||||
description="COM_CONVERTFORMS_CHOICES_DESC"
|
||||
/>
|
||||
</fieldset>
|
||||
<fieldset name="advanced">
|
||||
<field name="choicelayout" type="list"
|
||||
label="COM_CONVERTFORMS_CHOICE_LAYOUT"
|
||||
description="COM_CONVERTFORMS_CHOICE_LAYOUT_DESC"
|
||||
default="">
|
||||
<option value="">COM_CONVERTFORMS_CHOICE_1_COLUMN</option>
|
||||
<option value="2">COM_CONVERTFORMS_CHOICE_2_COLUMN</option>
|
||||
<option value="3">COM_CONVERTFORMS_CHOICE_3_COLUMN</option>
|
||||
<option value="auto">COM_CONVERTFORMS_CHOICE_SIDE_BY_SIDE</option>
|
||||
</field>
|
||||
</fieldset>
|
||||
</form>
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form>
|
||||
<fieldset name="basic">
|
||||
<field name="theme" type="list"
|
||||
label="NR_THEME"
|
||||
description="COM_CONVERTFORMS_FIELD_RECAPTCHA_THEME_DESC"
|
||||
default="light">
|
||||
<option value="light">NR_LIGHT</option>
|
||||
<option value="dark">NR_DARK</option>
|
||||
</field>
|
||||
<field name="size" type="list"
|
||||
label="NR_SIZE"
|
||||
description="COM_CONVERTFORMS_FIELD_RECAPTCHA_SIZE_DESC"
|
||||
default="normal">
|
||||
<option value="normal">NR_NORMAL</option>
|
||||
<option value="compact">NR_COMPACT</option>
|
||||
</field>
|
||||
</fieldset>
|
||||
</form>
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form>
|
||||
<fieldset name="basic">
|
||||
<field name="badge" type="list"
|
||||
label="COM_CONVERTFORMS_FIELD_RECAPTCHA_INVIS_BADGE"
|
||||
description="COM_CONVERTFORMS_FIELD_RECAPTCHA_INVIS_BADGE_DESC"
|
||||
default="inline">
|
||||
<option value="inline">COM_CONVERTFORMS_FIELD_RECAPTCHA_INVIS_BADGE_INLINE</option>
|
||||
<option value="bottomright">COM_CONVERTFORMS_FIELD_RECAPTCHA_INVIS_BADGE_BOTTOMRIGHT</option>
|
||||
<option value="bottomleft">COM_CONVERTFORMS_FIELD_RECAPTCHA_INVIS_BADGE_BOTTOMLEFT</option>
|
||||
</field>
|
||||
</fieldset>
|
||||
</form>
|
||||
@@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form>
|
||||
<fieldset name="basic">
|
||||
<field name="text" type="text"
|
||||
default="Sign up"
|
||||
hint="Sign up"
|
||||
label="COM_CONVERTFORMS_BUTTON_TEXT"
|
||||
description="COM_CONVERTFORMS_BUTTON_TEXT_DESC"
|
||||
/>
|
||||
<field name="align" type="list"
|
||||
label="COM_CONVERTFORMS_BUTTON_ALIGN"
|
||||
description="COM_CONVERTFORMS_BUTTON_ALIGN_DESC"
|
||||
showon="size:cf-width-auto,cf-one-third,cf-one-half"
|
||||
default="left">
|
||||
<option value="left">NR_LEFT</option>
|
||||
<option value="center">NR_CENTER</option>
|
||||
<option value="right">NR_RIGHT</option>
|
||||
</field>
|
||||
<field name="btnstyle" type="list"
|
||||
label="COM_CONVERTFORMS_SUBMIT_STYLE"
|
||||
description="COM_CONVERTFORMS_SUBMIT_STYLE_DESC"
|
||||
default="flat">
|
||||
<option value="flat">COM_CONVERTFORMS_FLAT</option>
|
||||
<option value="outline">COM_CONVERTFORMS_OUTLINE</option>
|
||||
<option value="gradient">COM_CONVERTFORMS_GRADIENT</option>
|
||||
</field>
|
||||
<field name="fontsize" type="nrnumber"
|
||||
addon="px"
|
||||
default="15"
|
||||
min="12"
|
||||
step="2"
|
||||
label="COM_CONVERTFORMS_BTN_FONT_SIZE"
|
||||
description="COM_CONVERTFORMS_BTN_FONT_SIZE_DESC"
|
||||
/>
|
||||
<field name="bg" type="color"
|
||||
label="NR_BACKGROUND_COLOR"
|
||||
description="NR_BACKGROUND_COLOR_DESC"
|
||||
default="#444"
|
||||
/>
|
||||
<field name="textcolor" type="color"
|
||||
label="NR_TEXT_COLOR"
|
||||
description="NR_COLOR_DESC"
|
||||
default="#fff"
|
||||
/>
|
||||
<field name="texthovercolor" type="color"
|
||||
label="COM_CONVERTFORMS_SUBMIT_HOVER_COLOR"
|
||||
description="COM_CONVERTFORMS_SUBMIT_HOVER_COLOR_DESC"
|
||||
default="#fff"
|
||||
showon="btnstyle:outline"
|
||||
/>
|
||||
<field name="borderradius" type="nrnumber"
|
||||
addon="px"
|
||||
default="3"
|
||||
min="0"
|
||||
step="1"
|
||||
label="COM_CONVERTFORMS_BORDER_RADIUS"
|
||||
description="COM_CONVERTFORMS_BORDER_RADIUS_DESC"
|
||||
/>
|
||||
<field name="vpadding" type="nrnumber"
|
||||
addon="px"
|
||||
default="11"
|
||||
min="0"
|
||||
label="COM_CONVERTFORMS_VPADDING_SIZE"
|
||||
description="COM_CONVERTFORMS_PADDING_DESC"
|
||||
/>
|
||||
<field name="hpadding" type="nrnumber"
|
||||
addon="px"
|
||||
default="15"
|
||||
min="0"
|
||||
label="COM_CONVERTFORMS_HPADDING_SIZE"
|
||||
description="COM_CONVERTFORMS_PADDING_DESC"
|
||||
/>
|
||||
</fieldset>
|
||||
</form>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form>
|
||||
<fieldset name="restrictions">
|
||||
<field name="inputmask" type="text"
|
||||
label="COM_CONVERTFORMS_INPUT_MASK"
|
||||
description="COM_CONVERTFORMS_INPUT_MASK_DESC"
|
||||
hint="(999) 999-9999"
|
||||
/>
|
||||
<field name="readonly" type="nrtoggle"
|
||||
label="COM_CONVERTFORMS_READONLY"
|
||||
description="COM_CONVERTFORMS_READONLY_DESC"
|
||||
/>
|
||||
</fieldset>
|
||||
</form>
|
||||
@@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form>
|
||||
<fieldset name="restrictions">
|
||||
<field name="inputmask" type="text"
|
||||
label="COM_CONVERTFORMS_INPUT_MASK"
|
||||
description="COM_CONVERTFORMS_INPUT_MASK_DESC"
|
||||
hint="(999) 999-9999"
|
||||
/>
|
||||
<field name="readonly" type="nrtoggle"
|
||||
label="COM_CONVERTFORMS_READONLY"
|
||||
description="COM_CONVERTFORMS_READONLY_DESC"
|
||||
/>
|
||||
<field name="minchars" type="nrnumber"
|
||||
label="COM_CONVERTFORMS_MIN_CHARS"
|
||||
description="COM_CONVERTFORMS_MIN_CHARS_DESC"
|
||||
default="0"
|
||||
min="0"
|
||||
/>
|
||||
<field name="maxchars" type="nrnumber"
|
||||
label="COM_CONVERTFORMS_MAX_CHARS"
|
||||
description="COM_CONVERTFORMS_MAX_CHARS_DESC"
|
||||
default="0"
|
||||
min="0"
|
||||
/>
|
||||
<field name="minwords" type="nrnumber"
|
||||
label="COM_CONVERTFORMS_MIN_WORDS"
|
||||
description="COM_CONVERTFORMS_MIN_WORDS_DESC"
|
||||
default="0"
|
||||
min="0"
|
||||
/>
|
||||
<field name="maxwords" type="nrnumber"
|
||||
label="COM_CONVERTFORMS_MAX_WORDS"
|
||||
description="COM_CONVERTFORMS_MAX_WORDS_DESC"
|
||||
default="0"
|
||||
min="0"
|
||||
/>
|
||||
</fieldset>
|
||||
<fieldset name="advanced">
|
||||
<field name="filter" type="list"
|
||||
label="COM_CONVERTFORMS_FIELD_FILTER"
|
||||
description="COM_CONVERTFORMS_FIELD_FILTER_DESC"
|
||||
default="html">
|
||||
<option value="safehtml">JLIB_FILTER_PARAMS_SAFEHTML</option>
|
||||
<option value="html">JLIB_FILTER_PARAMS_TEXT</option>
|
||||
<option value="raw">JLIB_FILTER_PARAMS_RAW</option>
|
||||
<option value="alnum">JLIB_FILTER_PARAMS_ALNUM</option>
|
||||
<option value="word">Word</option>
|
||||
<option value="integer">JLIB_FILTER_PARAMS_INTEGER</option>
|
||||
<option value="float">JLIB_FILTER_PARAMS_FLOAT</option>
|
||||
</field>
|
||||
</fieldset>
|
||||
</form>
|
||||
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form>
|
||||
<fieldset name="basic">
|
||||
<field name="textareaheight" type="nrnumber"
|
||||
label="COM_CONVERTFORMS_TEXTAREA_HEIGHT"
|
||||
description="COM_CONVERTFORMS_TEXTAREA_HEIGHT_DESC"
|
||||
addon="rows"
|
||||
default="3"
|
||||
min="1"
|
||||
/>
|
||||
</fieldset>
|
||||
<fieldset name="restrictions">
|
||||
<field name="readonly" type="nrtoggle"
|
||||
label="COM_CONVERTFORMS_READONLY"
|
||||
description="COM_CONVERTFORMS_READONLY_DESC"
|
||||
/>
|
||||
<field name="minchars" type="nrnumber"
|
||||
label="COM_CONVERTFORMS_MIN_CHARS"
|
||||
description="COM_CONVERTFORMS_MIN_CHARS_DESC"
|
||||
default="0"
|
||||
min="0"
|
||||
/>
|
||||
<field name="maxchars" type="nrnumber"
|
||||
label="COM_CONVERTFORMS_MAX_CHARS"
|
||||
description="COM_CONVERTFORMS_MAX_CHARS_DESC"
|
||||
default="0"
|
||||
min="0"
|
||||
/>
|
||||
<field name="minwords" type="nrnumber"
|
||||
label="COM_CONVERTFORMS_MIN_WORDS"
|
||||
description="COM_CONVERTFORMS_MIN_WORDS_DESC"
|
||||
default="0"
|
||||
min="0"
|
||||
/>
|
||||
<field name="maxwords" type="nrnumber"
|
||||
label="COM_CONVERTFORMS_MAX_WORDS"
|
||||
description="COM_CONVERTFORMS_MAX_WORDS_DESC"
|
||||
default="0"
|
||||
min="0"
|
||||
/>
|
||||
</fieldset>
|
||||
<fieldset name="advanced">
|
||||
<field name="filter" type="list"
|
||||
label="COM_CONVERTFORMS_FIELD_FILTER"
|
||||
description="COM_CONVERTFORMS_FIELD_FILTER_DESC"
|
||||
default="html">
|
||||
<option value="safehtml">JLIB_FILTER_PARAMS_SAFEHTML</option>
|
||||
<option value="html">JLIB_FILTER_PARAMS_TEXT</option>
|
||||
<option value="raw">JLIB_FILTER_PARAMS_RAW</option>
|
||||
<option value="alnum">JLIB_FILTER_PARAMS_ALNUM</option>
|
||||
<option value="word">Word</option>
|
||||
<option value="integer">JLIB_FILTER_PARAMS_INTEGER</option>
|
||||
<option value="float">JLIB_FILTER_PARAMS_FLOAT</option>
|
||||
</field>
|
||||
</fieldset>
|
||||
</form>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form>
|
||||
<fieldset name="restrictions">
|
||||
<field name="readonly" type="nrtoggle"
|
||||
label="COM_CONVERTFORMS_READONLY"
|
||||
description="COM_CONVERTFORMS_READONLY_DESC"
|
||||
/>
|
||||
</fieldset>
|
||||
<fieldset name="advanced">
|
||||
<field name="placeholder" type="text"
|
||||
label="COM_CONVERTFORMS_PLACEHOLDER_NAME"
|
||||
description="COM_CONVERTFORMS_PLACEHOLDER_NAME_DESC"
|
||||
default="http://www.example.com"
|
||||
/>
|
||||
</fieldset>
|
||||
</form>
|
||||
Reference in New Issue
Block a user