| Current Path : /home/jcjack6/transformptyoga.com/administrator/components/com_convertforms/ConvertForms/ |
| Current File : /home/jcjack6/transformptyoga.com/administrator/components/com_convertforms/ConvertForms/Export.php |
<?php
/**
* @package Convert Forms
* @version 5.1.6 Pro
*
* @author Tassos Marinos <info@tassos.gr>
* @link https://www.tassos.gr
* @copyright Copyright © 2024 Tassos 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\CMS\MVC\Model\BaseDatabaseModel;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\Registry\Registry;
use Joomla\Filesystem\Path;
use Joomla\Filesystem\File as JoomlaFile;
use ConvertForms\Helper;
use Joomla\CMS\Factory;
use NRFramework\File;
/**
* 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);
$include_submission_notes = (bool) $options->get('include_submission_notes', 0);
$csv_delimiter = self::sanitizeCSVDelimiter($options->get('csv_delimiter', ','));
// Load submissions model
BaseDatabaseModel::addIncludePath(JPATH_ADMINISTRATOR . '/components/com_convertforms/models');
$model = BaseDatabaseModel::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', ''));
$model->setState('filter.timezone', $options->get('filter_timezone', ''));
// Proceed only if we have submissions
if (!$submissions = $model->getItems())
{
$error = Text::sprintf('COM_CONVERTFORMS_NO_RESULTS_FOUND', strtolower(Text::_('COM_CONVERTFORMS_SUBMISSIONS')));
throw new \Exception($error);
}
foreach ($submissions as &$submission)
{
self::prepareSubmission($submission, $include_submission_notes);
}
unset($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 = Path::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, true, $csv_delimiter);
break;
}
return [
'options' => $options,
'pagination' => $pagination,
'filename' => $filename
];
}
/**
* Get a key value array with submission's submitted data
*
* @param object $submission The submission object
* @param boolean $include_submission_notes Whether to include submission notes in the export payload.
*
* @return array
*/
private static function prepareSubmission(&$submission, $include_submission_notes = false)
{
$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;
}
if ($include_submission_notes)
{
$result['notes'] = isset($submission->params->leadnotes) ? $submission->params->leadnotes : '';
}
$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 = file_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
JoomlaFile::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.
*
* @param string $delimiter The CSV delimiter character.
*
* @return void
*/
private static function toCSV($data, $destination, $append = false, $excel_security = true, $check_for_duplicates = true, $delimiter = ',')
{
$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]), $delimiter);
}
// Get CSV content
$existingRows = [];
if ($append && $check_for_duplicates)
{
rewind($resource);
while (($existingData = fgetcsv($resource, 0, $delimiter)) !== false)
{
$existingRows[(int) $existingData[0]] = $existingData;
}
fseek($resource, 0, SEEK_END);
}
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, $delimiter);
}
fclose($resource);
}
/**
* Allow only supported delimiters in CSV exports.
*
* @param string $delimiter
*
* @return string
*/
private static function sanitizeCSVDelimiter($delimiter)
{
return in_array($delimiter, [',', ';'], true) ? $delimiter : ',';
}
/**
* Redirects to the error layout and displays the given error message
*
* @param string $error_message
*
* @return void
*/
public static function error($error_message)
{
$app = Factory::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 file_exists(File::getTempFolder() . $filename);
}
/**
* Adds the Export popup to the page which can be triggered by toolbar buttons.
*
* @return void
*/
public static function renderModal()
{
HTMLHelper::script('com_convertforms/export.js', ['relative' => true, 'version' => 'auto']);
Factory::getDocument()->addScriptDeclaration('
document.addEventListener("DOMContentLoaded", function() {
new exportModal("' . Factory::getApplication()->input->get('view') . '");
});
');
$html = HTMLHelper::_('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);
}
}