first commit

This commit is contained in:
2026-02-08 21:16:11 +01:00
commit e17b7026fd
8881 changed files with 1160453 additions and 0 deletions

View File

@@ -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);
}
}
?>

View 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';
}
}

View File

@@ -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);
}
}

View 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);
}
}
?>

View File

@@ -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);
}
}
}
?>

View File

@@ -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',
);
}
?>

View File

@@ -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;
}
?>

View File

@@ -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
{
}
?>

View File

@@ -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;
}
}
?>

View File

@@ -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>';
}
}
?>

View File

@@ -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'
];
}
?>

View File

@@ -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;
}
}

View File

@@ -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>.';
}
}

View File

@@ -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'
);
}
?>

View File

@@ -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;
}
}
?>

View File

@@ -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';
}
?>

View File

@@ -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';
}
?>

View File

@@ -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;
}
}
?>

View File

@@ -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>.';
}
}
?>

View File

@@ -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');
}
}
?>

View File

@@ -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;
}
?>

View File

@@ -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>';
}
}
?>

View File

@@ -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);
}
}
?>

View File

@@ -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
{
}
?>

View File

@@ -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>';
}
}
?>

View File

@@ -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;
}
}
?>

View File

@@ -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 . '
}
';
}
}
?>

View 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);
}
}

View File

@@ -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(['{', '}'], ['&#123;', '&#125;'], $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);
}
}

View File

@@ -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;
}
}
?>

View 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;
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();
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
?>

View File

@@ -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;
});
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}
}

View File

@@ -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 : '';
}
}

View File

@@ -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();
}
}

View File

@@ -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'];
}
}

View File

@@ -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);
}
}

View File

@@ -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'] : '';
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>