<?php
/**
* @package RSForm! Pro
* @copyright (C) 2007-2019 www.rsjoomla.com
* @license GPL, http://www.gnu.org/copyleft/gpl.html
*/
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
require_once __DIR__.'/helper.php';
class RSFormProBackupForm
{
// Database instance
protected $db;
// Holds the form.
protected $form;
// Holds an array in the form of field ID => field name.
protected $fields;
// The path to the backup.
protected $path;
// The XML Writer object
protected $xml;
// Holds the Grid Layout so we can process it after fields have been added
protected $GridLayout;
public function __construct($options = array()) {
require_once __DIR__.'/xml.php';
$this->db = Factory::getDbo();
$this->xml = new RSFormProBackupXML;
// Load the form
$query = $this->db->getQuery(true);
$query->select('*')
->from($this->db->qn('#__rsform_forms'))
->where($this->db->qn('FormId').'='.$this->db->q($options['form']));
$this->form = $this->db->setQuery($query)->loadObject();
if (!$this->form) {
throw new Exception(sprintf('Form #%d could not be loaded from the database!', $options['form']));
}
// Assign the path
$this->path = $options['path'];
}
public function store() {
// Add XML header
$this->xml->addHeader();
// Add <form> tag
$this->xml->add('form');
$this->storeStructure();
$this->storeFields();
$this->storeCalculations();
$this->storePost();
$this->storeConditions();
$this->storeDirectory();
$this->storeEmails();
$this->storeMappings();
$this->storeGridLayout();
// Allow plugins to add their own data to the backup.
Factory::getApplication()->triggerEvent('onRsformFormBackup', array($this->form, $this->xml, $this->fields));
// Close <form> tag
$this->xml->add('/form');
$archive = new RSFormProTar($this->path);
$buffer = (string) $this->xml;
$size = strlen($buffer);
$archive->addHeader($size, RSFormProBackupHelper::getHash($this->form->FormId).'.xml');
$archive->add($buffer);
$archive->addPadding($size);
$archive->close();
}
// Form structure
// ==============
protected function storeStructure() {
// Add the form structure #__rsform_forms
$this->xml->add('structure');
foreach ($this->form as $property => $value) {
if ($property == 'GridLayout' && strlen($value))
{
$this->GridLayout = $value;
$value = '';
}
$this->xml->add($property, $value);
}
// Add the form translation
if ($translations = $this->getFormTranslations()) {
$this->xml->add('translations');
foreach ($translations as $language => $properties) {
$this->xml->add($language);
foreach ($properties as $property => $value) {
$this->xml->add($property, $value);
}
$this->xml->add('/'.$language);
}
$this->xml->add('/translations');
}
$this->xml->add('/structure');
}
// Fields
// ======
protected function storeFields() {
// Add fields #__rsform_components
if ($fields = $this->getFields()) {
$this->xml->add('fields');
foreach ($fields as $field) {
$properties = isset($field->properties) ? $field->properties : array();
$translations = isset($field->translations) ? $field->translations : array();
// No need for these.
unset($field->ComponentId, $field->FormId, $field->properties, $field->translations);
$this->xml->add('field');
foreach ($field as $property => $value) {
$this->xml->add($property, $value);
}
// Add field properties
$this->xml->add('properties');
foreach ($properties as $property => $value) {
$this->xml->add($property, $value);
}
$this->xml->add('/properties');
// Add translations
if ($translations) {
$this->xml->add('translations');
foreach ($translations as $language => $properties) {
$this->xml->add($language);
foreach ($properties as $property => $value) {
$this->xml->add($property, $value);
}
$this->xml->add('/'.$language);
}
$this->xml->add('/translations');
}
$this->xml->add('/field');
}
$this->xml->add('/fields');
}
}
protected function getFields() {
$db = &$this->db;
$query = $db->getQuery(true);
$query->select('*')
->from($db->qn('#__rsform_components'))
->where($db->qn('FormId').'='.$db->q($this->form->FormId));
$db->setQuery($query);
$fields = $db->loadObjectList('ComponentId');
// Get properties
if ($fields) {
$query->clear()
->select('*')
->from($db->qn('#__rsform_properties'))
->where($db->qn('ComponentId').' IN ('.RSFormProBackupHelper::qi(array_keys($fields)).')');
$db->setQuery($query);
$properties = $db->loadObjectList();
// Get translations
if ($translations = $this->getTranslations('properties')) {
foreach ($translations as $translation) {
list($componentId, $property) = explode('.', $translation->reference_id, 2);
if (!isset($fields[$componentId]))
{
continue;
}
if (!isset($fields[$componentId]->translations)) {
$fields[$componentId]->translations = array();
}
if (!isset($fields[$componentId]->translations[$translation->lang_code])) {
$fields[$componentId]->translations[$translation->lang_code] = array();
}
$fields[$componentId]->translations[$translation->lang_code][$property] = $translation->value;
}
}
foreach ($properties as $property) {
if (!isset($fields[$property->ComponentId]))
{
continue;
}
if (!isset($fields[$property->ComponentId]->properties)) {
$fields[$property->ComponentId]->properties = array();
}
// exceptions for the calendars (YUI and jQuery)
if ($property->PropertyName == 'VALIDATIONCALENDAR' && !empty($property->PropertyValue)) {
$valueProperty = explode(' ', $property->PropertyValue);
// get the name of the component
$query->clear()
->select('PropertyValue')
->from($db->qn('#__rsform_properties'))
->where($db->qn('ComponentId').' = '.$db->q($valueProperty[1]))
->where($db->qn('PropertyName').' = '.$db->q('NAME'));
$db->setQuery($query);
$componentName = $db->loadResult();
if (!empty($componentName)) {
$valueProperty[1] = $componentName;
$property->PropertyValue = implode(' ', $valueProperty);
}
}
if ($property->PropertyName === 'SELECTFIELD' && !empty($property->PropertyValue))
{
// get the name of the component
$query->clear()
->select('PropertyValue')
->from($db->qn('#__rsform_properties'))
->where($db->qn('ComponentId').' = '.$db->q($property->PropertyValue))
->where($db->qn('PropertyName').' = '.$db->q('NAME'));
$db->setQuery($query);
$componentName = $db->loadResult();
if (!empty($componentName))
{
$property->PropertyValue = $componentName;
}
}
$fields[$property->ComponentId]->properties[$property->PropertyName] = $property->PropertyValue;
}
foreach ($fields as $field) {
$this->fields[$field->ComponentId] = '';
if (isset($field->properties['NAME'])) {
$this->fields[$field->ComponentId] = $field->properties['NAME'];
}
}
}
return $fields;
}
// Calculations
// ============
protected function storeCalculations() {
// Add Calculations #__rsform_calculations
if ($calculations = $this->getCalculations()) {
$this->xml->add('calculations');
foreach ($calculations as $calculation) {
// No need for these.
unset($calculation->id, $calculation->formId);
$this->xml->add('calculation');
foreach ($calculation as $property => $value) {
$this->xml->add($property, $value);
}
$this->xml->add('/calculation');
}
$this->xml->add('/calculations');
}
}
protected function getCalculations() {
$db = &$this->db;
$query = $db->getQuery(true);
$query->select('*')
->from($db->qn('#__rsform_calculations'))
->where($db->qn('formId').'='.$db->q($this->form->FormId));
$db->setQuery($query);
return $db->loadObjectList();
}
// Post
// ====
protected function storePost() {
// Add Post to Location #__rsform_posts
if ($post = $this->getPost()) {
// No need for this
unset($post->form_id);
$this->xml->add('post');
foreach ($post as $property => $value) {
$this->xml->add($property, $value);
}
$this->xml->add('/post');
}
}
protected function getPost() {
$db = &$this->db;
$query = $db->getQuery(true);
$query->select('*')
->from($db->qn('#__rsform_posts'))
->where($db->qn('form_id').'='.$db->q($this->form->FormId));
$db->setQuery($query);
return $db->loadObject();
}
// Conditions
// ==========
protected function storeConditions() {
// Add conditions #__rsform_conditions & #__rsform_condition_details
if ($conditions = $this->getConditions()) {
require_once JPATH_ADMINISTRATOR . '/components/com_rsform/helpers/conditions.php';
$this->xml->add('conditions');
foreach ($conditions as $condition) {
$component_ids = RSFormProConditions::parseComponentIds($condition->component_id);
// No need
unset($condition->id, $condition->form_id, $condition->component_id);
$json_ids = array();
foreach ($component_ids as $component_id)
{
if (isset($this->fields[$component_id]))
{
$json_ids[] = $this->fields[$component_id];
}
}
$condition->component_id = json_encode($json_ids);
$this->xml->add('condition');
foreach ($condition as $property => $value) {
if ($property == 'details') {
$this->xml->add('details');
foreach ($value as $detail) {
$this->xml->add('detail');
foreach ($detail as $property => $value) {
$this->xml->add($property, $value);
}
$this->xml->add('/detail');
}
$this->xml->add('/details');
} else {
$this->xml->add($property, $value);
}
}
$this->xml->add('/condition');
}
$this->xml->add('/conditions');
}
}
protected function getConditions() {
$db = &$this->db;
$query = $db->getQuery(true);
$query->select('*')
->from($db->qn('#__rsform_conditions'))
->where($db->qn('form_id').'='.$db->q($this->form->FormId));
$db->setQuery($query);
if ($conditions = $db->loadObjectList('id')) {
$query->clear()
->select('*')
->from($db->qn('#__rsform_condition_details'))
->where($db->qn('condition_id').' IN ('.RSFormProBackupHelper::qi(array_keys($conditions)).')');
$db->setQuery($query);
if ($details = $db->loadObjectList()) {
foreach ($details as $detail) {
$condition_id = $detail->condition_id;
$component_id = $detail->component_id;
// No need for these.
unset($detail->id, $detail->condition_id, $detail->component_id);
// Add the field's name so that we can restore correctly.
$detail->component_id = isset($this->fields[$component_id]) ? $this->fields[$component_id] : '';
if (!isset($conditions[$condition_id]->details)) {
$conditions[$condition_id]->details = array();
}
$conditions[$condition_id]->details[] = $detail;
}
}
}
return $conditions;
}
// Directory
// =========
protected function storeDirectory() {
// Add directory #__rsform_directory & #__rsform_directory_fields
if ($directory = $this->getDirectory()) {
// No need for these
unset($directory->formId);
$this->xml->add('directory');
foreach ($directory as $property => $value) {
if ($property == 'fields') {
$this->xml->add('fields');
foreach ($value as $field) {
// No need for this.
unset($field->formId);
// Special case - static headers
if ($field->componentId < 0) {
// Do nothing
} else {
$field->componentId = isset($this->fields[$field->componentId]) ? $this->fields[$field->componentId] : '';
}
$this->xml->add('field');
foreach ($field as $property => $value) {
$this->xml->add($property, $value);
}
$this->xml->add('/field');
}
$this->xml->add('/fields');
} else {
$this->xml->add($property, $value);
}
}
$this->xml->add('/directory');
}
}
protected function getDirectory() {
$db = &$this->db;
$query = $db->getQuery(true);
$query->select('*')
->from($db->qn('#__rsform_directory'))
->where($db->qn('formId').'='.$db->q($this->form->FormId));
$db->setQuery($query);
if ($directory = $db->loadObject()) {
$query->clear()
->select('*')
->from($db->qn('#__rsform_directory_fields'))
->where($db->qn('formId').'='.$db->q($this->form->FormId));
$db->setQuery($query);
$directory->fields = $db->loadObjectList();
return $directory;
}
return false;
}
// Emails
// ======
protected function storeEmails() {
// Add Emails #__rsform_emails
if ($emails = $this->getEmails()) {
$this->xml->add('emails');
foreach ($emails as $email) {
$translations = isset($email->translations) ? $email->translations : array();
unset($email->translations, $email->id, $email->formId);
$this->xml->add('email');
foreach ($email as $property => $value) {
$this->xml->add($property, $value);
}
// Add translations
if ($translations) {
$this->xml->add('translations');
foreach ($translations as $language => $properties) {
$this->xml->add($language);
foreach ($properties as $property => $value) {
$this->xml->add($property, $value);
}
$this->xml->add('/'.$language);
}
$this->xml->add('/translations');
}
$this->xml->add('/email');
}
$this->xml->add('/emails');
}
}
protected function getEmails() {
$db = &$this->db;
$query = $db->getQuery(true);
$query->select('*')
->from($db->qn('#__rsform_emails'))
->where($db->qn('formId').'='.$db->q($this->form->FormId));
$db->setQuery($query);
if ($emails = $db->loadObjectList('id')) {
// Get translations
if ($translations = $this->getTranslations('emails')) {
foreach ($translations as $translation) {
@list($id, $property) = explode('.', $translation->reference_id, 2);
if (!isset($emails[$id])) {
continue;
}
if (!isset($emails[$id]->translations)) {
$emails[$id]->translations = array();
}
if (!isset($emails[$id]->translations[$translation->lang_code])) {
$emails[$id]->translations[$translation->lang_code] = array();
}
$emails[$id]->translations[$translation->lang_code][$property] = $translation->value;
}
}
return $emails;
}
}
// Mappings
// ========
protected function storeMappings() {
// Add Mappings #__rsform_mappings
if ($mappings = $this->getMappings()) {
$prefix = Factory::getApplication()->get('dbprefix');
$this->xml->add('mappings');
foreach ($mappings as $mapping) {
unset($mapping->id, $mapping->formId);
$this->xml->add('mapping');
foreach ($mapping as $property => $value) {
// Don't hardcode the prefix so we can restore easier
if ($property === 'table' && strpos($value, $prefix) === 0)
{
$value = substr_replace($value, '#__', 0, strlen($prefix));
}
$this->xml->add($property, $value);
}
$this->xml->add('/mapping');
}
$this->xml->add('/mappings');
}
}
protected function getMappings() {
$db = &$this->db;
$query = $db->getQuery(true);
$query->select('*')
->from($db->qn('#__rsform_mappings'))
->where($db->qn('formId').'='.$db->q($this->form->FormId));
$db->setQuery($query);
return $db->loadObjectList();
}
// Translations
// ============
protected function getFormTranslations() {
$translations = array();
if ($results = $this->getTranslations('forms')) {
foreach ($results as $result) {
if (!isset($translations[$result->lang_code])) {
$translations[$result->lang_code] = array();
}
$translations[$result->lang_code][$result->reference_id] = $result->value;
}
}
return $translations;
}
protected function getTranslations($reference) {
$db = &$this->db;
$query = $db->getQuery(true);
$query->select('*')
->from($db->qn('#__rsform_translations'))
->where($db->qn('form_id').'='.$db->q($this->form->FormId))
->where($db->qn('reference').'='.$db->q($reference));
$db->setQuery($query);
return $db->loadObjectList();
}
// Grid Layout - needs to replace IDs with field names.
// ===========
protected function storeGridLayout()
{
if (empty($this->GridLayout))
{
return false;
}
$data = json_decode($this->GridLayout, true);
$rows = array();
$hidden = array();
// If decoding is successful, we should have $rows and $hidden
if (is_array($data) && isset($data[0], $data[1]))
{
$rows = $data[0];
$hidden = $data[1];
}
if ($rows)
{
foreach ($rows as $row_index => &$row)
{
foreach ($row['columns'] as $column_index => $fields)
{
foreach ($fields as $position => $id)
{
if (isset($this->fields[$id]))
{
$row['columns'][$column_index][$position] = $this->fields[$id];
}
else
{
// Field doesn't exist, remove it from grid
unset($row['columns'][$column_index][$position]);
}
}
}
}
unset($row);
}
if ($hidden)
{
foreach ($hidden as $hidden_index => $id)
{
if (isset($this->fields[$id]))
{
$hidden[$hidden_index] = $this->fields[$id];
}
else
{
// Field doesn't exist, remove it from grid
unset($hidden[$hidden_index]);
}
}
}
$this->xml->replace('GridLayout', json_encode(array($rows, $hidden)));
}
}