From c9cff54937725c558e7ef09c6555408a45b434fb Mon Sep 17 00:00:00 2001 From: Roland Gruber Date: Fri, 31 Aug 2018 20:59:05 +0200 Subject: [PATCH] import tool --- lam/help/help.inc | 6 +- lam/lib/import.inc | 118 ++++++++++++ lam/templates/lib/500_lam.js | 37 ++++ lam/templates/misc/ajax.php | 11 +- lam/templates/tools/importexport.php | 263 +++++++++++++++++++++++++++ 5 files changed, 433 insertions(+), 2 deletions(-) create mode 100644 lam/lib/import.inc create mode 100644 lam/templates/tools/importexport.php diff --git a/lam/help/help.inc b/lam/help/help.inc index c248c9c1..f3cc2d19 100644 --- a/lam/help/help.inc +++ b/lam/help/help.inc @@ -330,7 +330,7 @@ $helpArray = array ( "Text" => _("This will create a new organisational unit under the selected one.")), "602" => array ("Headline" => _("OU-Editor") . " - " . _("Delete organisational unit"), "Text" => _("This will delete the selected organisational unit. The OU has to be empty.")), - // 700 - 799 + // 700 - 749 // multi edit tool "700" => array ("Headline" => _('LDAP suffix'), "Text" => _('Please select the suffix where changes should be done.')), @@ -338,6 +338,10 @@ $helpArray = array ( "Text" => _('Use this to enter an additional LDAP filter (e.g. "(cn!=admin)") to reduce the number of entries to modify.')), "702" => array ("Headline" => _('Operations'), "Text" => _('Please specify which attributes should be changed. The modify operation will also add an value if the attribute does not yet exist. To delete all values of an attribute please leave the value field empty.')), + // 750 - 799 + // import/export + "750" => array ("Headline" => _('LDIF data'), + "Text" => _('The input data must be formatted in LDIF format.')), // 800 - 899 // jobs '800' => array( diff --git a/lam/lib/import.inc b/lam/lib/import.inc new file mode 100644 index 00000000..e22603c8 --- /dev/null +++ b/lam/lib/import.inc @@ -0,0 +1,118 @@ +getStatus(); + } + $endTime = $this->getEndTime(); + while ((!empty($entries)) && ($endTime > time())) { + $this->continueImport($entries); + } + return $this->getStatus(); + } + + /** + * Returns the current status as JSON. + * + * @return string JSON status + */ + private function getStatus() { + if (empty($entries)) { + if (isset($_SESSION[Importer::SESSION_KEY_ENTRIES])) { + unset($_SESSION[Importer::SESSION_KEY_ENTRIES]); + } + $status = array( + Importer::STATUS => 'done' + ); + return json_encode($status); + } + $progress = (sizeof($_SESSION[Importer::SESSION_KEY_ENTRIES]) / $_SESSION[Importer::SESSION_KEY_COUNT]) * 100.0; + $progress = floor(100 - $progress); + $status = array( + Importer::STATUS => 'inProgress', + Importer::PROGRESS => $progress, + Importer::DATA => '' + ); + return json_encode($status); + } + + /** + * Returns the time when processing should end. + * + * @return number end time as Unix timestamp + */ + private function getEndTime() { + $startTime = time(); + $maxTime = get_cfg_var('max_execution_time') - 10; + if ($maxTime > Importer::TIME_LIMIT) { + $maxTime = Importer::TIME_LIMIT; + } + if ($maxTime <= 0) { + $maxTime = Importer::TIME_LIMIT; + } + return $startTime + $maxTime; + } + + /** + * Continues the import with processing of a single entry. + * + * @param array[] $entries import entries + */ + private function continueImport(&$entries) { + $entry = array_shift($entries); + } + +} + +?> diff --git a/lam/templates/lib/500_lam.js b/lam/templates/lib/500_lam.js index 862bedb3..be320477 100644 --- a/lam/templates/lib/500_lam.js +++ b/lam/templates/lib/500_lam.js @@ -901,6 +901,43 @@ window.lam.tools.schema.select = function() { }); }; +window.lam.import = window.lam.import || {}; + +/** + * Starts the import process. + * + * @param tokenName name of CSRF token + * @param tokenValue value of CSRF token + */ +window.lam.import.startImport = function(tokenName, tokenValue) { + jQuery(document).ready(function() { + var output = jQuery('#importResults'); + var data = { + jsonInput: '' + }; + data[tokenName] = tokenValue; + jQuery.ajax({ + url: '../misc/ajax.php?function=import', + method: 'POST', + data: data + }) + .done(function(jsonData){ + if (jsonData.status == 'done') { + jQuery('#progressbarImport').hide(); + jQuery('#btn_submitImportCancel').hide(); + jQuery('#statusImportInprogress').hide(); + jQuery('#statusImportDone').show(); + } + else { + jQuery('#progressbarImport').progressbar({ + value: jsonData.progress + }); + window.lam.import.startImport(tokenName, tokenValue); + } + }); + }); +}; + jQuery(document).ready(function() { window.lam.gui.equalHeight(); window.lam.form.autoTrim(); diff --git a/lam/templates/misc/ajax.php b/lam/templates/misc/ajax.php index 0e45907f..46b2c458 100644 --- a/lam/templates/misc/ajax.php +++ b/lam/templates/misc/ajax.php @@ -1,5 +1,6 @@ managePasswordChange($jsonInput); } - elseif ($function == 'upload') { + elseif ($function === 'import') { + include_once('../../lib/import.inc'); + $importer = new Importer(); + ob_start(); + $jsonOut = $importer->doImport(); + ob_end_clean(); + echo $jsonOut; + } + elseif ($function === 'upload') { include_once('../../lib/upload.inc'); $typeManager = new \LAM\TYPES\TypeManager(); $uploader = new \LAM\UPLOAD\Uploader($typeManager->getConfiguredType($_GET['typeId'])); diff --git a/lam/templates/tools/importexport.php b/lam/templates/tools/importexport.php new file mode 100644 index 00000000..b970c794 --- /dev/null +++ b/lam/templates/tools/importexport.php @@ -0,0 +1,263 @@ + + + + +
+
+
    +
  • + import +
  • +
  • + export +
  • +
+
+ +
+
+
+
+
+ +\n"; + $container = new htmlResponsiveRow(); + $container->add(new htmlTitle(_("Import")), 12); + $sources = array( + _('File') => 'file', + _('Text input') => 'text' + ); + $sourceRadio = new htmlResponsiveRadio(_('Source'), 'source', $sources, 'text'); + $sourceRadio->setTableRowsToHide( + array( + 'file' => array('text'), + 'text' => array('file') + ) + ); + $sourceRadio->setTableRowsToShow( + array( + 'text' => array('text'), + 'file' => array('file') + ) + ); + $container->add($sourceRadio, 12); + $container->addVerticalSpacer('1rem'); + $container->add(new htmlResponsiveInputFileUpload('file', _('File'), '750'), 12); + $container->add(new htmlResponsiveInputTextarea('text', '', '60', '20', _('LDIF data'), '750'), 12); + + $container->addVerticalSpacer('3rem'); + $button = new htmlButton('submitImport', _('Submit')); + $container->add($button, 12, 12, 12, 'text-center'); + + addSecurityTokenToMetaHTML($container); + + parseHtml(null, $container, array(), false, $tabindex, 'user'); + echo ("\n"); +} + +/** + * Prints the content area for the import tab during processing state. + * + * @param int $tabindex tabindex + */ +function printImportTabProcessing(&$tabindex) { + $message = checkImportData(); + if (!empty($message)) { + $container = new htmlResponsiveRow(); + $container->add(new htmlStatusMessage('ERROR', $message), 12); + parseHtml(null, $container, array(), false, $tabindex, 'user'); + printImportTabContent($tabindex); + return; + } + echo "
\n"; + $container = new htmlResponsiveRow(); + $container->add(new htmlTitle(_("Import")), 12); + + $container->add(new htmlDiv('statusImportInprogress', new htmlOutputText(_('Status') . ': ' . _('in progress'))), 12); + $container->add(new htmlDiv('statusImportDone', new htmlOutputText(_('Status') . ': ' . _('done')), array('hidden')), 12); + $container->add(new htmlDiv('progressbarImport', new htmlOutputText('')), 12); + $container->add(new htmlDiv('importResults', new htmlOutputText('')), 12); + $container->add(new htmlJavaScript( + 'window.lam.import.startImport(\'' . getSecurityTokenName() . '\', \'' . getSecurityTokenValue() . '\');' + ), 12); + + $container->addVerticalSpacer('3rem'); + + $button = new htmlButton('submitImportCancel', _('Cancel')); + $container->add($button, 12, 12, 12, 'text-center'); + + addSecurityTokenToMetaHTML($container); + + parseHtml(null, $container, array(), false, $tabindex, 'user'); + echo ("
\n"); +} + +/** + * Checks if the import data is ok. + * + * @return string error message if not valid + */ +function checkImportData() { + $source = $_POST['source']; + $ldif = ''; + if ($source == 'text') { + $ldif = $_POST['text']; + } + else { + $handle = fopen($_FILES['file']['tmp_name'], "r"); + $ldif = fread($handle, 100000000); + fclose($handle); + } + if (empty($ldif)) { + return _('You must either upload a file or provide an import in the text box.'); + } + $lines = preg_split("/\n|\r\n|\r/", $ldif); + $entriesData = extractImportEntries($lines); + if (!is_array($entriesData)) { + return $entriesData; + } + $_SESSION[Importer::SESSION_KEY_ENTRIES] = $entriesData; + $_SESSION[Importer::SESSION_KEY_COUNT] = sizeof($entriesData); +} + +/** + * Extracts the single entries in the file. + * + * @param string[] $lines LDIF lines + * @return string|array array of string[] + */ +function extractImportEntries($lines) { + $entries = array(); + $currentEntry = array(); + foreach ($lines as $line) { + if (substr(trim($line), 0, 1) === '#') { + // skip comments + continue; + } + if (empty(trim($line))) { + // end of entry + if (!empty($currentEntry)) { + $entries[] = $currentEntry; + $currentEntry = array(); + } + } + elseif (substr($line, 0, 1) === ' ') { + // append to last line if starting with a space + if (empty($currentEntry)) { + return _('Invalid data:') . ' ' . htmlspecialchars($line); + } + else { + $currentEntry[sizeof($currentEntry) - 1] .= substr($line, 1); + } + } + else { + $parts = explode(':', $line, 2); + if (sizeof($parts) < 2) { + return _('Invalid data:') . ' ' . htmlspecialchars($line); + } + $currentEntry[] = $line; + } + } + if (!empty($currentEntry)) { + $entries[] = $currentEntry; + } + return $entries; +} + +include '../../lib/adminFooter.inc';