File Manager

Current Directory: /home/astoriaah/www/administrator/components/com_sh404sef/classes
Viewing File: /home/astoriaah/www/administrator/components/com_sh404sef/classes/importgeneric.php
<?php /** * sh404SEF - SEO extension for Joomla! * * @author Yannick Gaultier * @copyright (c) Yannick Gaultier - Weeblr llc - 2022 * @package sh404SEF * @license http://www.gnu.org/copyleft/gpl.html GNU/GPL * @version 4.24.1.4308 * @date 2022-05-31 */ use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\Filesystem\File; use Aspera\Spreadsheet\XLSX\Reader; use Aspera\Spreadsheet\XLSX\SharedStringsConfiguration; // Security check to ensure this file is being included by a parent file. defined('_JEXEC') || die; /** * Implement wizard based exportation of generic data * * @author shumisha * */ class Sh404sefClassImportgeneric extends JObject { /** * An array holding each step details * A step is defined as a task, a view and a layout * By default, task can be 'display', but still need * to be defined in array * @var array */ public $_stepsMap = array( -2 => array('task' => 'doTerminate', 'view' => 'wizard', 'layout' => 'default') , -1 => array('task' => 'doCancel', 'view' => 'wizard', 'layout' => 'default') , 0 => array('task' => 'doStart', 'view' => 'wizard', 'layout' => 'default') , 1 => array('task' => 'doUpload', 'view' => 'wizard', 'layout' => 'default') , 2 => array('task' => 'doValidate', 'view' => 'wizard', 'layout' => 'default') , 3 => array('task' => 'doImport', 'view' => 'wizard', 'layout' => 'default') ); public $_stepsCount = 0; public $_steps = array('next' => 0, 'previous' => 0, 'cancel' => -1, 'terminate' => -2); public $_button = ''; public $_buttonsList = array('next', 'previous', 'terminate', 'cancel'); // visible buttons are displayed as toolbar pressbutton // buttons not on that list are passed as 'hidden' post data public $_visibleButtonsList = array('previous', 'next', 'terminate', 'cancel'); protected $_context = ''; protected $_total = 0; protected $_parentController = null; protected $_filename = ''; protected $_result = array(); protected $_inputData = array(); const MAX_PAGEIDS_PER_STEP = 20; /** * Constructor, keep reference to controller * which called the adapter * * @param unknown_type $parentController */ public function __construct($parentController) { $this->_parentController = $parentController; } /** * Parameters for current adapter, to be used by parent controller * */ public function setup() { $this->_stepsCount = count($this->_stepsMap); // prepare data for controller $properties = array(); $properties['_defaultController'] = 'wizard'; $properties['_defaultTask'] = ''; $properties['_defaultModel'] = ''; $properties['_defaultView'] = 'wizard'; $properties['_defaultLayout'] = 'default'; $properties['_returnController'] = 'default'; $properties['_returnTask'] = ''; $properties['_returnView'] = 'default'; $properties['_returnLayout'] = 'default'; $properties['_pageTitle'] = Text::_('COM_SH404SEF_IMPORTING_TITLE'); return $properties; } /** * First step, by default a message * and a Terminate button * */ public function doStart() { // which button should be displayed ? $this->_visibleButtonsList = array('next', 'cancel'); // next steps definition $this->_steps = array('next' => 1, 'previous' => 0, 'cancel' => -1, 'terminate' => -2); // return results $this->_result['mainText'] = Text::_('COM_SH404SEF_IMPORT_' . strtoupper($this->_context) . '_START'); return $this->_result; } /** * Second step, let user upload file * */ public function doUpload() { // which button should be displayed ? $this->_visibleButtonsList = array('next', 'cancel'); // next steps definition $this->_steps = array('next' => 2, 'previous' => 0, 'cancel' => -1, 'terminate' => -2); // return results // make sure we can upload, ie set the correct encoding type for the form $this->_result['setFormEncType'] = 'multipart/form-data'; // prepare display $this->_result['mainText'] = Text::sprintf('COM_SH404SEF_IMPORT_UPLOAD_FILE', Sh404sefHelperFiles::getMaxUploadSize()); // add a file browse button $this->_result['mainText'] .= '<div style="text-align:center;width:100%;" ><input type="file" name="wizardfile" size="70" /></div>'; return $this->_result; } /** * Second step, read file content, validate and display for user go ahead * */ public function doValidate() { // get file name $fileRecord = Factory::getApplication()->input->files->get('wizardfile', null); // move uploaded file, to get access to it $this->_filename = Sh404sefHelperFiles::createFileName($this->_filename, 'sh404sef_import_' . $this->_context); try { if (!move_uploaded_file($fileRecord['tmp_name'], $this->_filename)) { // could not write to web space temp dir throw new \Exception(Text::_('COM_SH404SEF_WRITE_FAILED')); } // which button should be displayed ? $this->_visibleButtonsList = array('previous', 'next', 'cancel'); // next steps definition $this->_steps = array('next' => 3, 'previous' => 1, 'cancel' => -1, 'terminate' => -2); // analyse file content, returning itemsCount $importType = $this->_analyzeImportFileContent($this->_filename); // we may have to change the opSubject and related data. If user asked for instance to import // aliases, from the aliases page, but actually loaded an import file // containing urls or pageids if ($this->_context != $importType) { $this->_result['opSubject'] = $importType; // update filename $oldFileName = $this->_filename; $this->_filename = str_replace($this->_context, $importType, $this->_filename); // and rename the temp file File::move($oldFileName, $this->_filename); // tell parent controller that we should go to new target afer this import $this->_parentController->set('_returnController', $importType); $this->_parentController->set('_returnView', $importType); } // save current file for next steps $this->_result['mainText'] = Text::sprintf('COM_SH404SEF_IMPORT_VALIDATE_IMPORT', $importType, $this->_total); $this->_result['hiddenText'] = '<input type="hidden" name="filename" value="' . $this->_filename . '" />'; } catch (\Exception $e) { $this->_handleException($e); } return $this->_result; } /** * Last step, actually perform importation * */ public function doImport() { // collect file and import type information $this->_filename = Factory::getApplication()->input->getString('filename'); try { $this->loadFile($this->_filename); // extract header line $header = array_shift($this->_inputData); if ( empty($header) || !is_array($header) ) { throw new \Exception('COM_SH404SEF_ERROR_IMPORT'); } // count items $this->_total = count($this->_inputData); // iterate through file content and create each record foreach ($this->_inputData as $line) { $this->_createRecord($header, $line); } // delete temporary uploaded file File::delete($this->_filename); // which button should be displayed ? $this->_visibleButtonsList = array('terminate'); // next steps definition $this->_steps = array('next' => 3, 'previous' => 0, 'cancel' => -1, 'terminate' => -2); // setup display of wizard last page $this->_result['hiddenText'] = ''; $this->_result['mainText'] = Text::sprintf('COM_SH404SEF_IMPORT_DONE', $this->_total, $this->_context); $this->_result['mainText'] .= $this->_getTerminateOptions(); } catch (\Exception $e) { $this->_handleException($e); } return $this->_result; } /** * Close the wizard window and redirect to default page * */ public function doTerminate() { // now go back to main page $this->_result = array('redirectTo' => true); return $this->_result; } /** * Close the wizard window and redirect to default page * */ public function doCancel() { $this->_result['redirectTo'] = true; $this->_result['redirectOptions'] = array(); return $this->_result; } /** * Load an xls/xlx file from disk into memory. * * @param string $filename * * @throws Exception */ protected function loadFile($filename) { /********************************************** * cf https://github.com/AsperaGmbH/xlsx-reader * /**********************************************/ include_once JPATH_ADMINISTRATOR . '/components/com_sh404sef/vendor/autoload.php'; $options = array( 'TempDir' => Factory::getApplication()->get('tmp_path'), 'SkipEmptyCells' => false, 'ReturnDateTimeObjects' => true, 'SharedStringsConfiguration' => new SharedStringsConfiguration(), 'CustomFormats' => array(), 'OutputColumnNames' => false ); $reader = new Reader($options); $reader->open($filename); // Data must be on the 1st sheet $reader->changeSheet(0); $this->_inputData = array(); foreach ($reader as $index => $row) { $this->_inputData[] = $row; } if ( empty($this->_inputData) || !is_array($this->_inputData) || count($this->_inputData) < 2 ) { throw new \RuntimeException(Text::_('COM_SH404SEF_IMPORT_UNRECOGNIZED_CONTENT')); } } /** * Analyze the content of a potential import file * to try recognize its content. Also find the * number of records in the file * * @param string $filename fully pathed file name * * @return string * @throws Exception */ protected function _analyzeImportFileContent($filename) { $this->loadFile($filename); // extract header line $header = array_shift($this->_inputData); if (empty($header)) { throw new \Exception(Text::_('COM_SH404SEF_IMPORT_UNRECOGNIZED_CONTENT')); } // check against known headers $headers = Sh404sefHelperGeneral::getExportHeaders(); foreach ($headers as $headerName => $headerValue) { if ($header === $headerValue) { $importType = $headerName; break; } } // have we found something ? if (empty($importType)) { throw new \Exception(Text::_('COM_SH404SEF_IMPORT_UNRECOGNIZED_CONTENT')); } $this->_total = count($this->_inputData); return $importType; } /** * Return html for any option that could * be presented to the user on the last * page of the wizard (like clean temp files) * for instance. This will be displayed just after * the mainText text, as prepared by the main * part of this controller */ protected function _getTerminateOptions() { $options = ''; return $options; } /** * Handle an exception by returning to step 2, where * user can select a file * * @param \Exception $e */ protected function _handleException(Exception $e) { // unable to get the file, display error and go back to step 2, "doUpload" $this->_parentController->setError($e->getMessage()); // try delete the uploaded file if (File::exists($this->_filename)) { File::delete($this->_filename); } // go back to previous step $this->doUpload(); } /** * Creates a record in the database, based * on data read from import file * * @param array $header an array of fields, as built from the header line * @param string $line raw record obtained from import file * * @return bool */ protected function _createRecord($header, $line) { return true; } }