<?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; use Joomla\CMS\Table\Table; class RSFormProRestoreForm { // Database instance protected $db; // Holds the form's structure as a Table object protected $form; // Holds an array of the XML data. protected $xml; // Holds an array in the form of field ID => field name. protected $fields; // Holds the setting for keeping the form's ids from the backup protected $keepId; // Meta data information protected $metaData; public function __construct($options = array()) { $this->keepId = !empty($options['keepId']); $path = &$options['path']; $this->db = Factory::getDbo(); // Check if the form's xml exists if (!file_exists($path)) { throw new Exception(sprintf('The file %s does not exist!', $path)); } if (!is_readable($path)) { throw new Exception(sprintf('File %s is not readable!', $path)); } // Attempt to load the XML data libxml_use_internal_errors(true); $contents = file_get_contents($path); if (strpos($contents, '<typeAlias>') !== false) { $contents = str_replace(array('<typeAlias></typeAlias>', '<typeAlias>', '</typeAlias>'), '', $contents); } if (class_exists('DOMDocument')) { $dom = new DOMDocument('1.0', 'UTF-8'); $dom->strictErrorChecking = false; $dom->validateOnParse = false; $dom->recover = true; $dom->loadXML($contents); $this->xml = simplexml_import_dom($dom); } else { $this->xml = simplexml_load_string($contents); } if ($this->xml === false) { $errors = array(); foreach (libxml_get_errors() as $error) { $errors[] = 'Message: '.$error->message.'; Line: '.$error->line.'; Column: '.$error->column; } throw new Exception(sprintf('Error while parsing XML: %s<br/>', implode('<br />', $errors))); } $this->metaData = $options['metaData']; } public function restore() { $this->restoreStructure(); $this->restoreFields(); $this->restoreCalculations(); $this->restorePost(); $this->restoreConditions(); $this->restoreDirectory(); $this->restoreEmails(); $this->restoreMappings(); $this->rebuildCalendarsValidationRules(); $this->rebuildPreviewSelectFields(); $this->rebuildGridLayout(); // Allow plugins to restore their own data from the backup. Factory::getApplication()->triggerEvent('onRsformFormRestore', array($this->form, $this->xml, $this->fields)); } public function getFormId() { return $this->form->FormId; } // Form structure // ============== protected function restoreStructure() { // Restore the form structure #__rsform_forms $data = array(); $oldFormId = false; foreach ($this->xml->structure->children() as $property => $value) { // Skip translations for now // Skip ThemeParams, no longer exists if ($property == 'translations' || $property == 'ThemeParams') { continue; } if ($property == 'FormId') { if ($this->keepId) { $oldFormId = (string) $value; } continue; } $data[$property] = (string) $value; } $this->form = Table::getInstance('RSForm_Forms', 'Table'); // Responsive layout needs its own CSS to be loaded, make sure old forms still load it when restored. if (version_compare($this->metaData['version'], '1.51.12', '<') && $data['FormLayoutName'] == 'responsive') { $data['LoadFormLayoutFramework'] = 1; } if (!$this->form->save($data)) { throw new Exception(sprintf('Form %s could not be saved. Error reported is: %s', $this->form->FormTitle, $this->form->getError())); } if (!empty($oldFormId) && $oldFormId != $this->form->FormId) { // Is it free? $query = $this->db->getQuery(true) ->select($this->db->qn('FormId')) ->from($this->db->qn('#__rsform_forms')) ->where($this->db->qn('FormId') . ' = ' . $this->db->q($oldFormId)); // Requested form ID doesn't exist, update current form if (!$this->db->setQuery($query)->loadResult()) { $query->clear() ->update($this->db->qn('#__rsform_forms')) ->set($this->db->qn('FormId') . ' = ' . $this->db->q($oldFormId)) ->where($this->db->qn('FormId') . ' = ' . $this->db->q($this->form->FormId)); $this->db->setQuery($query)->execute(); $this->form->FormId = $oldFormId; } } // Restore form translations if ($this->xml->structure->translations) { foreach ($this->xml->structure->translations->children() as $lang_code => $properties) { foreach ($properties->children() as $property => $value) { $data = array( 'form_id' => $this->form->FormId, 'lang_code' => (string) $lang_code, 'reference' => 'forms', 'reference_id' => (string) $property, 'value' => (string) $value ); $data = (object) $data; $this->db->insertObject('#__rsform_translations', $data); } } } } // Fields // ====== protected function restoreFields() { // Restore the form fields #__rsform_components if (isset($this->xml->fields)) { foreach ($this->xml->fields->children() as $field) { $componentTypeId = (string) $field->ComponentTypeId; // change fieldType if needed $changedField = ''; if ($componentTypeId == '12') { $componentTypeId = '13'; $changedField = 'imageButton'; } $data = array( 'FormId' => $this->form->FormId, 'ComponentTypeId' => $componentTypeId, 'Order' => (string) $field->Order, 'Published' => (string) $field->Published ); $data = (object) $data; $this->db->insertObject('#__rsform_components', $data, 'ComponentId'); $componentId = $data->ComponentId; // we use the switch statement for further field types changes - at the moment we only need it for the image button $referenceProperties = array(); if (!empty($changedField)) { switch ($changedField) { case 'imageButton': $query = $this->db->getQuery(true); $query->select($this->db->qn('FieldName')) ->from($this->db->qn('#__rsform_component_type_fields')) ->where($this->db->qn('ComponentTypeId') . ' = ' . $this->db->q(13)); $this->db->setQuery($query); $referenceProperties = $this->db->loadColumn(); break; } } if (isset($field->properties)) { $newProperties = array(); foreach ($field->properties->children() as $property => $value) { $property = (string) $property; $value = (string) $value; if (!isset($newProperties[$componentId])) { $newProperties[$componentId] = array(); } if (!empty($changedField)) { switch ($changedField) { case 'imageButton': if (in_array($property, $referenceProperties)) { if ($property == 'ADDITIONALATTRIBUTES' && isset($newProperties[$componentId]['ADDITIONALATTRIBUTES'])) { $newProperties[$componentId]['ADDITIONALATTRIBUTES'] = $value."\r\n".$newProperties[$componentId]['ADDITIONALATTRIBUTES']; } else { $newProperties[$componentId][$property] = $value; } } elseif ($property == 'IMAGEBUTTON' && !empty($value)) { $additional = 'type="image"'."\r\n".'src="'.$value.'"'; if (isset($newProperties[$componentId]['ADDITIONALATTRIBUTES']) && !empty($newProperties[$componentId]['ADDITIONALATTRIBUTES'])) { $additional = $newProperties[$componentId]['ADDITIONALATTRIBUTES']."\r\n".$additional; } $newProperties[$componentId]['ADDITIONALATTRIBUTES'] = $additional; } break; } } else { $newProperties[$componentId][$property] = $value; } } // add the submit button extra properties if (!empty($changedField)) { switch ($changedField) { case 'imageButton': foreach ($newProperties as $CompId => $property) { foreach ($referenceProperties as $referenceProperty) { $value = ''; switch ($referenceProperty) { case 'DISPLAYPROGRESS': $value = 'NO'; break; case 'DISPLAYPROGRESSMSG': $value = '<div>'."\r\n".' <p><em>Page <strong>{page}</strong> of {total}</em></p>'."\r\n".' <div class="rsformProgressContainer">'."\r\n".' <div class="rsformProgressBar" style="width: {percent}%;"></div>'."\r\n".' </div>'."\r\n".'</div>'; break; } if (!empty($value)) { $newProperties[$CompId][$referenceProperty] = $value; } } } break; } } foreach ($newProperties as $CompId => $property) { foreach ($property as $propertyName => $propertyValue) { $data = array( 'ComponentId' => $CompId, 'PropertyName' => $propertyName, 'PropertyValue' => $propertyValue ); $data = (object) $data; $this->db->insertObject('#__rsform_properties', $data); // store the ComponentId if ((string) $propertyName == 'NAME') { $this->fields[(string) $propertyValue] = $CompId; } } } } if (isset($field->translations)) { foreach ($field->translations->children() as $lang_code => $properties) { foreach ($properties->children() as $property => $value) { $data = array( 'form_id' => $this->form->FormId, 'lang_code' => (string) $lang_code, 'reference' => 'properties', 'reference_id' => $componentId . '.' . (string) $property, 'value' => (string) $value ); $data = (object) $data; $this->db->insertObject('#__rsform_translations', $data); } } } } } } protected function rebuildCalendarsValidationRules() { $db = &$this->db; $query = $db->getQuery(true); $query->clear() ->select('c.ComponentId') ->select('p.PropertyName') ->select('p.PropertyValue') ->from($db->qn('#__rsform_components', 'c')) ->join('LEFT', $db->qn('#__rsform_properties', 'p') . ' ON (' . $db->qn('c.ComponentId') . ' = ' . $db->qn('p.ComponentId') . ')') ->where($db->qn('c.FormId').' = '.$db->q($this->form->FormId)) ->where('('.$db->qn('p.PropertyName').' = '.$db->q('NAME').' OR '.$db->qn('p.PropertyName').' = '.$db->q('VALIDATIONCALENDAR').')'); $db->setQuery($query); $formCalendarsComponents = $db->loadObjectList(); $componentsNames = array(); $componentsValidations = array(); foreach ($formCalendarsComponents as $calendar) { if ($calendar->PropertyName == 'NAME') { $componentsNames[$calendar->PropertyValue] = $calendar->ComponentId; } if ($calendar->PropertyName == 'VALIDATIONCALENDAR') { $componentsValidations[$calendar->ComponentId] = $calendar->PropertyValue; } } foreach ($componentsValidations as $componentId => $value) { if (!empty($value)) { $ruleParts = explode(' ', $value, 2); $otherComponentName = $ruleParts[1]; $idOtherComponent = $componentsNames[$otherComponentName]; $ruleParts[1] = $idOtherComponent; // replace the name with the id $data = array( 'ComponentId' => $componentId, 'PropertyName' => 'VALIDATIONCALENDAR', 'PropertyValue' => implode(' ', $ruleParts) ); $data = (object) $data; $this->db->updateObject('#__rsform_properties', $data, array('ComponentId', 'PropertyName')); } } } protected function rebuildPreviewSelectFields() { $db = &$this->db; $query = $db->getQuery(true); $query->clear() ->select('c.ComponentId') ->select('p.PropertyName') ->select('p.PropertyValue') ->from($db->qn('#__rsform_components', 'c')) ->join('LEFT', $db->qn('#__rsform_properties', 'p') . ' ON (' . $db->qn('c.ComponentId') . ' = ' . $db->qn('p.ComponentId') . ')') ->where($db->qn('c.FormId').' = '.$db->q($this->form->FormId)) ->where('('.$db->qn('p.PropertyName').' = '.$db->q('NAME').' OR '.$db->qn('p.PropertyName').' = '.$db->q('SELECTFIELD').')'); $db->setQuery($query); $formComponents = $db->loadObjectList(); $componentsNames = array(); $componentsSelectFields = array(); foreach ($formComponents as $formComponent) { if ($formComponent->PropertyName == 'NAME') { $componentsNames[$formComponent->PropertyValue] = $formComponent->ComponentId; } if ($formComponent->PropertyName == 'SELECTFIELD') { $componentsSelectFields[$formComponent->ComponentId] = $formComponent->PropertyValue; } } foreach ($componentsSelectFields as $componentId => $value) { if (!empty($value)) { $idOtherComponent = $componentsNames[$value]; $data = array( 'ComponentId' => $componentId, 'PropertyName' => 'SELECTFIELD', 'PropertyValue' => $idOtherComponent ); $data = (object) $data; $this->db->updateObject('#__rsform_properties', $data, array('ComponentId', 'PropertyName')); } } } // Calculations // ============ protected function restoreCalculations() { // Restore Calculations #__rsform_calculations if (isset($this->xml->calculations)) { foreach ($this->xml->calculations->children() as $calculation) { $data = array( 'formId' => $this->form->FormId, 'total' => (string) $calculation->total, 'expression' => (string) $calculation->expression, 'ordering' => (string) $calculation->ordering ); $data = (object) $data; $this->db->insertObject('#__rsform_calculations', $data); } } } // Post // ==== protected function restorePost() { // Restore Post to Location #__rsform_posts if (isset($this->xml->post)) { foreach ($this->xml->post as $post) { // Some older versions might have left some data here due to a bug, must delete it first. $query = $this->db->getQuery(true); $query->delete('#__rsform_posts') ->where($this->db->qn('form_id').' = '.$this->db->q($this->form->FormId)); $this->db->setQuery($query)->execute(); $data = array( 'form_id' => $this->form->FormId, 'enabled' => (string) $post->enabled, 'method' => (string) $post->method, 'fields' => (string) $post->fields, 'headers' => (string) $post->headers, 'silent' => (string) $post->silent, 'url' => (string) $post->url ); $data = (object) $data; $this->db->insertObject('#__rsform_posts', $data); } } } // Conditions // ========== protected function restoreConditions() { // Restore conditions #__rsform_conditions & #__rsform_condition_details if (isset($this->xml->conditions)) { require_once JPATH_ADMINISTRATOR . '/components/com_rsform/helpers/conditions.php'; foreach ($this->xml->conditions->children() as $condition) { $component_ids = (string) $condition->component_id; $tmp_ids = json_decode($component_ids); if (is_array($tmp_ids)) { $component_ids = $tmp_ids; } else { $component_ids = array($component_ids); } $json_ids = array(); foreach ($component_ids as $component_id) { if (isset($this->fields[$component_id])) { $json_ids[] = $this->fields[$component_id]; } } $json_ids = json_encode($json_ids); $data = array( 'form_id' => $this->form->FormId, 'action' => (string) $condition->action, 'block' => (string) $condition->block, 'component_id' => $json_ids, 'condition' => (string) $condition->condition, 'lang_code' => (string) $condition->lang_code, ); $data = (object) $data; $this->db->insertObject('#__rsform_conditions', $data, 'id'); $conditionId = $data->id; if (isset($condition->details)) { foreach ($condition->details->children() as $detail) { if (!isset($this->fields[(string) $detail->component_id])) { continue; } $data = array( 'condition_id' => $conditionId, 'component_id' => $this->fields[(string) $detail->component_id], 'operator' => (string) $detail->operator, 'value' => (string) $detail->value ); $data = (object) $data; $this->db->insertObject('#__rsform_condition_details', $data); } } } } } // Directory // ========= protected function restoreDirectory() { // Restore directory #__rsform_directory & #__rsform_directory_fields if (isset($this->xml->directory)) { foreach ($this->xml->directory as $directory) { $data = array( 'formId' => $this->form->FormId, 'filename' => (string) $directory->filename, 'csvfilename' => (string) $directory->csvfilename, 'enablepdf' => (int) $directory->enablepdf, 'enablecsv' => (int) $directory->enablecsv, 'AllowCSVFullDownload' => (int) $directory->AllowCSVFullDownload, 'HideEmptyValues' => (int) $directory->HideEmptyValues, 'ShowGoogleMap' => (int) $directory->ShowGoogleMap, 'ViewLayout' => (string) $directory->ViewLayout, 'ViewLayoutName' => (string) $directory->ViewLayoutName, 'ViewLayoutAutogenerate' => (int) $directory->ViewLayoutAutogenerate, 'CSS' => (string) $directory->CSS, 'JS' => (string) $directory->JS, 'ListScript' => (string) $directory->ListScript, 'DetailsScript' => (string) $directory->DetailsScript, 'EditScript' => (string) $directory->EditScript, 'SaveScript' => (string) $directory->SaveScript, 'EmailsScript' => (string) $directory->EmailsScript, 'EmailsCreatedScript' => (string) $directory->EmailsCreatedScript, 'groups' => (string) $directory->groups, 'DeletionGroups' => (string) $directory->DeletionGroups ); $data = (object) $data; $this->db->insertObject('#__rsform_directory', $data); if (isset($directory->fields)) { foreach ($directory->fields->children() as $field) { // check for the component ID $componentId = (string) $field->componentId; if (isset($this->fields[$componentId])) { $componentId = $this->fields[$componentId]; } $componentId = (int) $componentId; if (is_int($componentId) && $componentId !== 0) { $data = array( 'formId' => $this->form->FormId, 'componentId' => $componentId, 'viewable' => (int) $field->viewable, 'searchable' => (int) $field->searchable, 'editable' => (int) $field->editable, 'indetails' => (int) $field->indetails, 'incsv' => (int) $field->incsv, 'sort' => (int) $field->sort, 'ordering' => (int) $field->ordering, ); $data = (object) $data; $this->db->insertObject('#__rsform_directory_fields', $data); } } } } } } // Emails // ====== protected function restoreEmails() { // Restore Emails #__rsform_emails if (isset($this->xml->emails)) { foreach ($this->xml->emails->children() as $email) { $data = array( 'formId' => $this->form->FormId, 'type' => (string) $email->type, 'from' => (string) $email->from, 'fromname' => (string) $email->fromname, 'replyto' => (string) $email->replyto, 'replytoname' => (string) $email->replytoname, 'to' => (string) $email->to, 'cc' => (string) $email->cc, 'bcc' => (string) $email->bcc, 'subject' => (string) $email->subject, 'mode' => (string) $email->mode, 'message' => (string) $email->message ); $data = (object) $data; $this->db->insertObject('#__rsform_emails', $data, 'id'); $emailId = $data->id; if (isset($email->translations)) { foreach ($email->translations->children() as $lang_code => $properties) { foreach ($properties->children() as $property => $value) { $data = array( 'form_id' => $this->form->FormId, 'lang_code' => (string) $lang_code, 'reference' => 'emails', 'reference_id' => $emailId . '.' . (string) $property, 'value' => (string) $value ); $data = (object) $data; $this->db->insertObject('#__rsform_translations', $data); } } } } } } // Mappings // ======== protected function restoreMappings() { // Restore Mappings #__rsform_mappings if (isset($this->xml->mappings)) { $defaultDriver = Factory::getApplication()->get('dbtype'); $prefix = Factory::getApplication()->get('dbprefix'); foreach ($this->xml->mappings->children() as $mapping) { $driver = (string) $mapping->driver; if (empty($driver)) { $driver = $defaultDriver; } $table = (string) $mapping->table; if (strpos($table, '#__') === 0) { $table = substr_replace($table, $prefix, 0, strlen('#__')); } $data = array( 'formId' => $this->form->FormId, 'connection' => (string) $mapping->connection, 'host' => (string) $mapping->host, 'port' => (string) $mapping->port, 'driver' => $driver, 'username' => (string) $mapping->username, 'password' => (string) $mapping->password, 'database' => (string) $mapping->database, 'method' => (string) $mapping->method, 'table' => $table, 'data' => (string) $mapping->data, 'wheredata' => (string) $mapping->wheredata, 'extra' => (string) $mapping->extra, 'andor' => (string) $mapping->andor, 'ordering' => (string) $mapping->ordering ); $data = (object) $data; $this->db->insertObject('#__rsform_mappings', $data); } } } // Grid Layout // =========== protected function rebuildGridLayout() { if (empty($this->form->GridLayout)) { return false; } $data = json_decode($this->form->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]); } } } $data = array( 'FormId' => $this->form->FormId, 'GridLayout' => json_encode(array($rows, $hidden)) ); $data = (object) $data; $this->db->updateObject('#__rsform_forms', $data, array('FormId')); } }