getDataAttributesAsString() . $idParam . '>';
foreach ($this->cells as $cell) {
$return = array_merge($return, $cell->generateHTML($module, $input, $values, $restricted, $tabindex, $scope));
}
@@ -4288,6 +4420,15 @@ class htmlResponsiveRadio extends htmlRadio {
return $row->generateHTML($module, $input, $values, $restricted, $tabindex, $scope);
}
+ /**
+ * Returns the selector to use to find the show/hide elements.
+ *
+ * @return string selector
+ */
+ protected function getShowHideSelector() {
+ return '.row';
+ }
+
}
/**
diff --git a/lam/lib/import.inc b/lam/lib/import.inc
new file mode 100644
index 00000000..6288eece
--- /dev/null
+++ b/lam/lib/import.inc
@@ -0,0 +1,777 @@
+extractImportChunks($lines);
+ $tasks = $this->convertToTasks($chunks);
+ return $tasks;
+ }
+
+ /**
+ * Processes the import data stored in session.
+ */
+ public function doImport() {
+ $data = '';
+ $tasks = &$_SESSION[Importer::SESSION_KEY_TASKS];
+ $stopOnError = $_SESSION[Importer::SESSION_KEY_STOP_ON_ERROR];
+ // check if any actions are needed at all
+ if (empty($tasks)) {
+ return $this->getStatus($data);
+ }
+ $endTime = $this->getEndTime();
+ while ((!empty($tasks)) && ($endTime > time())) {
+ $task = array_shift($tasks);
+ try {
+ $data .= $task->run();
+ }
+ catch (LAMException $e) {
+ if ($stopOnError) {
+ return $this->stopImport($data, $e);
+ }
+ else {
+ $data .= Importer::formatMessage('ERROR', $e->getTitle(), $e->getMessage());
+ }
+ }
+ }
+ return $this->getStatus($data);
+ }
+
+ /**
+ * Stops the import process because of an exception.
+ *
+ * @param string $data HTML output
+ * @param LAMException $e exception
+ * @return string JSON status
+ */
+ private function stopImport($data, LAMException $e) {
+ $data .= Importer::formatMessage('ERROR', $e->getTitle(), $e->getMessage());
+ if (isset($_SESSION[Importer::SESSION_KEY_TASKS])) {
+ unset($_SESSION[Importer::SESSION_KEY_TASKS]);
+ }
+ $status = array(
+ Importer::STATUS => 'failed',
+ Importer::DATA => $data
+ );
+ return json_encode($status);
+ }
+
+ /**
+ * Returns the current status as JSON.
+ *
+ * @param string $data HTML output to display
+ * @return string JSON status
+ */
+ private function getStatus($data) {
+ if (empty($_SESSION[Importer::SESSION_KEY_TASKS])) {
+ if (isset($_SESSION[Importer::SESSION_KEY_TASKS])) {
+ unset($_SESSION[Importer::SESSION_KEY_TASKS]);
+ }
+ $status = array(
+ Importer::STATUS => 'done',
+ Importer::DATA => $data
+ );
+ return json_encode($status);
+ }
+ $progress = (sizeof($_SESSION[Importer::SESSION_KEY_TASKS]) / $_SESSION[Importer::SESSION_KEY_COUNT]) * 100.0;
+ $progress = floor(100 - $progress);
+ $status = array(
+ Importer::STATUS => 'inProgress',
+ Importer::PROGRESS => $progress,
+ Importer::DATA => $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
+ * @return ImporterTask[] tasks
+ */
+ private function convertToTasks($entries) {
+ $tasks = array();
+ $count = sizeof($entries);
+ for ($i = 0; $i < $count; $i++) {
+ $entry = $entries[$i];
+ $firstParts = explode(':', $entry[0], 2);
+ if ($firstParts[Importer::KEY] == 'version') {
+ if ($i > 0) {
+ // allow version only as first chunk
+ throw new LAMException(_('Invalid data'), _('Duplicate version entry found.'));
+ }
+ $this->processVersion($entry);
+ }
+ elseif ($firstParts[Importer::KEY] == 'dn') {
+ $tasks[] = $this->processDnEntry($entry);
+ }
+ else {
+ throw new LAMException(_('A valid dn line is required'), htmlspecialchars($entry[0]));
+ }
+ }
+ return $tasks;
+ }
+
+ /**
+ * Checks a version entry.
+ *
+ * @param string[] $entry entry
+ * @throws LAMException if version is invalid
+ */
+ private function processVersion($entry) {
+ $keyValue = $this->getLineKeyValue($entry[0]);
+ if (($keyValue[Importer::VALUE] != '1') || (sizeof($entry) > 1)) {
+ $escapedLines = array_map('htmlspecialchars', $entry);
+ throw new LAMException(_('LDIF import only supports version 1.'), implode('
', $escapedLines));
+ }
+ }
+
+ /**
+ * Checks a dn entry.
+ *
+ * @param string[] $entry entry
+ * @return ImporterTask task
+ * @throws LAMException if invalid format
+ */
+ private function processDnEntry($entry) {
+ $dnLine = array_shift($entry);
+ $keyValue = $this->getLineKeyValue($dnLine);
+ $dn = $keyValue[Importer::VALUE];
+ if (empty($entry)) {
+ throw new LAMException(_('Invalid data'), htmlspecialchars($dnLine));
+ }
+ $firstAttributeLine = array_shift($entry);
+ $firstAttribute = $this->getLineKeyValue($firstAttributeLine);
+ if ($firstAttribute[Importer::KEY] != Importer::CHANGETYPE) {
+ // complete DN
+ $attributes = array(
+ $firstAttribute[Importer::KEY] => array($firstAttribute[Importer::VALUE])
+ );
+ foreach ($entry as $attributeLine) {
+ $attribute = $this->getLineKeyValue($attributeLine);
+ $attributes[$attribute[Importer::KEY]][] = $attribute[Importer::VALUE];
+ }
+ return new AddEntryTask($dn, $attributes);
+ }
+ else {
+ $type = $firstAttribute[Importer::VALUE];
+ if ($type === 'add') {
+ $attributes = array();
+ foreach ($entry as $line) {
+ $lineData = $this->getLineKeyValue($line);
+ $attributes[$lineData[Importer::KEY]][] = $lineData[Importer::VALUE];
+ }
+ return new AddEntryTask($dn, $attributes);
+ }
+ elseif ($type === 'modrdn') {
+ return $this->createModRdnTask($dn, $entry);
+ }
+ elseif ($type === 'delete') {
+ if (!empty($entry)) {
+ throw new LAMException(_('Invalid data'), htmlspecialchars($dn));
+ }
+ return new DeleteEntryTask($dn);
+ }
+ elseif ($type !== 'modify') {
+ throw new LAMException(_('Invalid data'), htmlspecialchars($dn) . ' - changeType: ' . htmlspecialchars($type));
+ }
+ $changes = array();
+ $subtasks = array();
+ $currentLines = array();
+ $linesCount = sizeof($entry);
+ for ($i = 0; $i < $linesCount; $i++) {
+ $line = $entry[$i];
+ if ($line === '-') {
+ $subtasks[] = $this->getChangeTypeTask($dn, $currentLines);
+ $currentLines = array();
+ }
+ else {
+ $currentLines[] = $line;
+ }
+ }
+ $subtasks[] = $this->getChangeTypeTask($dn, $currentLines);
+ return new MultiTask($subtasks, $dn);
+ }
+ }
+
+ /**
+ * Returns a modrdn task.
+ *
+ * @param string $dn DN
+ * @param string[] $entry entry lines
+ * @return
+ * @throws LAMException syntax error
+ */
+ private function createModRdnTask($dn, $entry) {
+ if (sizeof($entry) !== 2) {
+ throw new LAMException(_('Invalid data'), htmlspecialchars($dn));
+ }
+ $newRdnData = $this->getLineKeyValue($entry[0]);
+ if ($newRdnData[Importer::KEY] !== 'newrdn') {
+ throw new LAMException(_('Invalid data'), htmlspecialchars($dn) . '
' . $newRdnData);
+ }
+ $newRdn = $newRdnData[Importer::VALUE];
+ $delOldRdnData = $this->getLineKeyValue($entry[1]);
+ if (($delOldRdnData[Importer::KEY] !== 'deleteoldrdn') || !in_array($delOldRdnData[Importer::VALUE], array('0', '1'), true)) {
+ throw new LAMException(_('Invalid data'), htmlspecialchars($dn) . '
' . $entry[1]);
+ }
+ $delOldRdn = ($delOldRdnData[Importer::VALUE] === '0') ? false : true;
+ return new RenameEntryTask($dn, $newRdn, $delOldRdn);
+ }
+
+ /**
+ * Returns a task for LDIF changeType entry.
+ *
+ * @param string $dn DN
+ * @param string $lines lines
+ * @return ImporterTask task
+ */
+ private function getChangeTypeTask($dn, $lines) {
+ $firstLine = array_shift($lines);
+ $firstLineData = $this->getLineKeyValue($firstLine);
+ $type = $firstLineData[Importer::KEY];
+ $attributeName = $firstLineData[Importer::VALUE];
+ $attributes = array();
+ foreach ($lines as $line) {
+ $lineData = $this->getLineKeyValue($line);
+ if ($lineData[Importer::KEY] !== $attributeName) {
+ throw new LAMException(_('Invalid data'), htmlspecialchars($dn) . ' - ' . htmlspecialchars($type));
+ }
+ $attributes[$attributeName][] = $lineData[Importer::VALUE];
+ }
+ if ($type === 'add') {
+ return new AddAttributesTask($dn, $attributes);
+ }
+ elseif ($type === 'delete') {
+ return new DeleteAttributesTask($dn, $attributeName, $attributes);
+ }
+ elseif ($type === 'replace') {
+ return new ReplaceAttributesTask($dn, $attributes);
+ }
+ throw new LAMException(_('Invalid data'), htmlspecialchars($dn) . ' - ' . htmlspecialchars($type));
+ }
+
+ /**
+ * Returns the HTML for an error message.
+ *
+ * @param string $type message type (e.g. INFO)
+ * @param string $title title
+ * @param string $message message
+ * @return string HTML
+ */
+ public static function formatMessage($type, $title, $message) {
+ $msg = new htmlStatusMessage($type, $title, $message);
+ $tabindex = 0;
+ ob_start();
+ $msg->generateHTML(null, array($msg), array(), true, $tabindex, 'user');
+ $data = ob_get_contents();
+ ob_clean();
+ return $data;
+ }
+
+ /**
+ * Returns the key and value part of the line.
+ *
+ * @param string $line line
+ * @return string[] array(key, value)
+ */
+ private function getLineKeyValue($line) {
+ $parts = explode(':', $line, 2);
+ if (sizeof($parts) !== 2) {
+ throw new LAMException(_('Invalid data'), htmlspecialchars($line));
+ }
+ if (substr($parts[Importer::VALUE], 0, 1) == ':') {
+ $value = base64_decode(trim(substr($parts[Importer::VALUE], 1)));
+ }
+ else {
+ $value = trim($parts[Importer::VALUE]);
+ }
+ return array($parts[Importer::KEY], $value);
+ }
+
+}
+
+/**
+ * A single import task.
+ *
+ * @author Roland Gruber
+ */
+interface ImporterTask {
+
+ /**
+ * Runs the task.
+ *
+ * @return string HTML output or LAMException if error occured
+ */
+ public function run();
+
+}
+
+/**
+ * Adds a complete LDAP entry.
+ *
+ * @author Roland Gruber
+ */
+class AddEntryTask implements ImporterTask {
+
+ private $dn = '';
+ private $attributes = array();
+
+ /**
+ * Constructor
+ *
+ * @param string $dn DN
+ * @param array[string[]] $attributes list of attributes
+ */
+ public function __construct($dn, $attributes) {
+ $this->dn = $dn;
+ $this->attributes = $attributes;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @see \LAM\TOOLS\IMPORT_EXPORT\ImporterTask::run()
+ */
+ public function run() {
+ $ldap = $_SESSION['ldap']->server();
+ $success = @ldap_add($ldap, $this->dn, $this->attributes);
+ if ($success) {
+ return Importer::formatMessage('INFO', _('Entry created'), htmlspecialchars($this->dn));
+ }
+ throw new LAMException(sprintf(_('Was unable to create DN: %s.'), $this->dn), getExtendedLDAPErrorMessage($ldap));
+ }
+
+}
+
+/**
+ * Renames an LDAP entry.
+ *
+ * @author Roland Gruber
+ */
+class RenameEntryTask implements ImporterTask {
+
+ private $dn = '';
+ private $newRdn = '';
+ private $deleteOldRdn = true;
+
+ /**
+ * Constructor
+ *
+ * @param string $dn DN
+ * @param string $newRdn new RDN value
+ * @param bool $deleteOldRdn delete old RDN value
+ */
+ public function __construct($dn, $newRdn, $deleteOldRdn) {
+ $this->dn = $dn;
+ $this->newRdn = $newRdn;
+ $this->deleteOldRdn = $deleteOldRdn;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @see \LAM\TOOLS\IMPORT_EXPORT\ImporterTask::run()
+ */
+ public function run() {
+ $ldap = $_SESSION['ldap']->server();
+ $success = @ldap_rename($ldap, $this->dn, $this->newRdn, null, $this->deleteOldRdn);
+ if ($success) {
+ return Importer::formatMessage('INFO', _('Rename successful!'), htmlspecialchars($this->dn));
+ }
+ throw new LAMException(_('Could not rename the entry.') . '
' . $this->dn, getExtendedLDAPErrorMessage($ldap));
+ }
+
+}
+
+/**
+ * Deletes an LDAP entry.
+ *
+ * @author Roland Gruber
+ */
+class DeleteEntryTask implements ImporterTask {
+
+ private $dn = '';
+
+ /**
+ * Constructor
+ *
+ * @param string $dn DN
+ */
+ public function __construct($dn) {
+ $this->dn = $dn;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @see \LAM\TOOLS\IMPORT_EXPORT\ImporterTask::run()
+ */
+ public function run() {
+ $ldap = $_SESSION['ldap']->server();
+ $success = @ldap_delete($ldap, $this->dn);
+ if ($success) {
+ return Importer::formatMessage('INFO', sprintf(_('Successfully deleted DN %s'), $this->dn), '');
+ }
+ throw new LAMException(_('Could not delete the entry.') . '
' . $this->dn, getExtendedLDAPErrorMessage($ldap));
+ }
+
+}
+
+/**
+ * Combines multiple import tasks.
+ *
+ * @author Roland Gruber
+ */
+class MultiTask implements ImporterTask {
+
+ /**
+ * @var ImporterTask[] tasks
+ */
+ private $tasks = array();
+
+ /**
+ * @var string DN
+ */
+ private $dn = null;
+
+ /**
+ * Constructor
+ *
+ * @param ImporterTask[] $tasks tasks
+ */
+ public function __construct($tasks, $dn) {
+ $this->tasks = $tasks;
+ $this->dn = $dn;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @see \LAM\TOOLS\IMPORT_EXPORT\ImporterTask::run()
+ */
+ public function run() {
+ foreach ($this->tasks as $task) {
+ $task->run();
+ }
+ return Importer::formatMessage('INFO', _('LDAP operation successful.'), htmlspecialchars($this->dn));
+ }
+
+ /**
+ * Returns the list of subtasks.
+ *
+ * @return ImporterTask[]
+ */
+ public function getTasks() {
+ return $this->tasks;
+ }
+
+}
+
+/**
+ * Adds attributes to an existing LDAP entry.
+ *
+ * @author Roland Gruber
+ */
+class AddAttributesTask implements ImporterTask {
+
+ private $dn = '';
+ private $attributes = array();
+
+ /**
+ * Constructor
+ *
+ * @param string $dn DN
+ * @param array[string[]] $attributes list of attributes
+ */
+ public function __construct($dn, $attributes) {
+ $this->dn = $dn;
+ $this->attributes = $attributes;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @see \LAM\TOOLS\IMPORT_EXPORT\ImporterTask::run()
+ */
+ public function run() {
+ $ldap = $_SESSION['ldap']->server();
+ $success = @ldap_mod_add($ldap, $this->dn, $this->attributes);
+ if ($success) {
+ return '';
+ }
+ throw new LAMException(sprintf(_('Was unable to create DN: %s.'), $this->dn), getExtendedLDAPErrorMessage($ldap));
+ }
+
+ /**
+ * Returns the DN.
+ *
+ * @return string DN
+ */
+ public function getDn() {
+ return $this->dn;
+ }
+
+ /**
+ * Returns the attributes to add.
+ *
+ * @return string[] attributes (array('attr' => array('val1', 'val2')))
+ */
+ public function getAttributes() {
+ return $this->attributes;
+ }
+
+}
+
+/**
+ * Deletes attributes from an existing LDAP entry.
+ *
+ * @author Roland Gruber
+ */
+class DeleteAttributesTask implements ImporterTask {
+
+ private $dn = '';
+ private $attributes = array();
+ private $attributeName = null;
+
+ /**
+ * Constructor
+ *
+ * @param string $dn DN
+ * @param string $attributeName attribute name
+ * @param array[string[]] $attributes list of attributes
+ */
+ public function __construct($dn, $attributeName, $attributes) {
+ $this->dn = $dn;
+ $this->attributeName = $attributeName;
+ $this->attributes = $attributes;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @see \LAM\TOOLS\IMPORT_EXPORT\ImporterTask::run()
+ */
+ public function run() {
+ $ldap = $_SESSION['ldap']->server();
+ if (!empty($this->attributes)) {
+ $success = @ldap_mod_del($ldap, $this->dn, $this->attributes);
+ }
+ else {
+ $success = @ldap_modify($ldap, $this->dn, array($this->attributeName => array()));
+ }
+ if ($success) {
+ return '';
+ }
+ throw new LAMException(sprintf(_('Was unable to create DN: %s.'), $this->dn), getExtendedLDAPErrorMessage($ldap));
+ }
+
+ /**
+ * Returns the DN.
+ *
+ * @return string DN
+ */
+ public function getDn() {
+ return $this->dn;
+ }
+
+ /**
+ * Returns the attributes to add.
+ *
+ * @return string[] attributes (array('attr' => array('val1', 'val2')))
+ */
+ public function getAttributes() {
+ return $this->attributes;
+ }
+
+ /**
+ * Returns the attributes name.
+ *
+ * @return string name
+ */
+ public function getAttributeName() {
+ return $this->attributeName;
+ }
+
+}
+
+/**
+ * Replaces attributes in an existing LDAP entry.
+ *
+ * @author Roland Gruber
+ */
+class ReplaceAttributesTask implements ImporterTask {
+
+ private $dn = '';
+ private $attributes = array();
+
+ /**
+ * Constructor
+ *
+ * @param string $dn DN
+ * @param array[string[]] $attributes list of attributes
+ */
+ public function __construct($dn, $attributes) {
+ $this->dn = $dn;
+ $this->attributes = $attributes;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @see \LAM\TOOLS\IMPORT_EXPORT\ImporterTask::run()
+ */
+ public function run() {
+ $ldap = $_SESSION['ldap']->server();
+ $success = @ldap_modify($ldap, $this->dn, $this->attributes);
+ if ($success) {
+ return '';
+ }
+ throw new LAMException(sprintf(_('Was unable to create DN: %s.'), $this->dn), getExtendedLDAPErrorMessage($ldap));
+ }
+
+ /**
+ * Returns the DN.
+ *
+ * @return string DN
+ */
+ public function getDn() {
+ return $this->dn;
+ }
+
+ /**
+ * Returns the attributes to add.
+ *
+ * @return string[] attributes (array('attr' => array('val1', 'val2')))
+ */
+ public function getAttributes() {
+ return $this->attributes;
+ }
+
+ /**
+ * Returns the attributes name.
+ *
+ * @return string name
+ */
+ public function getAttributeName() {
+ return $this->attributeName;
+ }
+
+}
+
+?>
diff --git a/lam/lib/tools/importexport.inc b/lam/lib/tools/importexport.inc
new file mode 100644
index 00000000..c15d69f0
--- /dev/null
+++ b/lam/lib/tools/importexport.inc
@@ -0,0 +1,131 @@
+
\ No newline at end of file
diff --git a/lam/style/500_layout.css b/lam/style/500_layout.css
index 870e9b75..d2fbd317 100644
--- a/lam/style/500_layout.css
+++ b/lam/style/500_layout.css
@@ -380,6 +380,12 @@ table.collapse {
background-position: 0px 0px !important;
}
+.okButton {
+ background-image: url(../graphics/pass.png) !important;
+ background-size: 16px 16px;
+ background-position: 0px 0px !important;
+}
+
.smallPadding span {
padding: 0.1em 0.4em !important;
}
diff --git a/lam/templates/3rdParty/pla/config/config.php b/lam/templates/3rdParty/pla/config/config.php
index c0082362..bf182f7f 100644
--- a/lam/templates/3rdParty/pla/config/config.php
+++ b/lam/templates/3rdParty/pla/config/config.php
@@ -39,10 +39,10 @@ $config->custom->commands['script'] = array(
'delete_form' => true,
'draw_tree_node' => true,
'expand' => true,
- 'export' => true,
- 'export_form' => true,
- 'import' => true,
- 'import_form' => true,
+ 'export' => false,
+ 'export_form' => false,
+ 'import' => false,
+ 'import_form' => false,
'login' => true,
'logout' => true,
'login_form' => true,
diff --git a/lam/templates/lib/500_lam.js b/lam/templates/lib/500_lam.js
index 862bedb3..9a1ed75f 100644
--- a/lam/templates/lib/500_lam.js
+++ b/lam/templates/lib/500_lam.js
@@ -901,6 +901,210 @@ window.lam.tools.schema.select = function() {
});
};
+window.lam.importexport = window.lam.importexport || {};
+
+/**
+ * Starts the import process.
+ *
+ * @param tokenName name of CSRF token
+ * @param tokenValue value of CSRF token
+ */
+window.lam.importexport.startImport = function(tokenName, tokenValue) {
+ jQuery(document).ready(function() {
+ jQuery('#progressbarImport').progressbar();
+ 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.data && (jsonData.data != '')) {
+ output.append(jsonData.data);
+ }
+ if (jsonData.status == 'done') {
+ jQuery('#progressbarImport').hide();
+ jQuery('#btn_submitImportCancel').hide();
+ jQuery('#statusImportInprogress').hide();
+ jQuery('#statusImportDone').show();
+ jQuery('.newimport').show();
+ }
+ else if (jsonData.status == 'failed') {
+ jQuery('#btn_submitImportCancel').hide();
+ jQuery('#statusImportInprogress').hide();
+ jQuery('#statusImportFailed').show();
+ jQuery('.newimport').show();
+ }
+ else {
+ jQuery('#progressbarImport').progressbar({
+ value: jsonData.progress
+ });
+ window.lam.import.startImport(tokenName, tokenValue);
+ }
+ });
+ });
+};
+
+/**
+ * Starts the export process.
+ *
+ * @param tokenName name of CSRF token
+ * @param tokenValue value of CSRF token
+ */
+window.lam.importexport.startExport = function(tokenName, tokenValue) {
+ jQuery(document).ready(function() {
+ jQuery('#progressbarExport').progressbar({value: 50});
+ var output = jQuery('#exportResults');
+ var data = {
+ jsonInput: ''
+ };
+ data[tokenName] = tokenValue;
+ data['baseDn'] = jQuery('#baseDn').val();
+ data['searchScope'] = jQuery('#searchScope').val();
+ data['filter'] = jQuery('#filter').val();
+ data['attributes'] = jQuery('#attributes').val();
+ data['format'] = jQuery('#format').val();
+ data['ending'] = jQuery('#ending').val();
+ data['includeSystem'] = jQuery('#includeSystem').val();
+ data['saveAsFile'] = jQuery('#saveAsFile').val();
+ jQuery.ajax({
+ url: '../misc/ajax.php?function=export',
+ method: 'POST',
+ data: data
+ })
+ .done(function(jsonData){
+ if (jsonData.data && (jsonData.data != '')) {
+ output.append(jsonData.data);
+ }
+ if (jsonData.status == 'done') {
+ jQuery('#progressbarExport').hide();
+ jQuery('#btn_submitExportCancel').hide();
+ jQuery('#statusExportInprogress').hide();
+ jQuery('#statusExportDone').show();
+ jQuery('.newexport').show();
+ if (jsonData.output) {
+ jQuery('#exportResults > pre').text(jsonData.output);
+ }
+ else if (jsonData.file) {
+ window.open(jsonData.file, '_blank');
+ }
+ }
+ else {
+ jQuery('#progressbarExport').hide();
+ jQuery('#btn_submitExportCancel').hide();
+ jQuery('#statusExportInprogress').hide();
+ jQuery('#statusExportFailed').show();
+ jQuery('.newexport').show();
+ }
+ })
+ .fail(function() {
+ jQuery('#progressbarExport').hide();
+ jQuery('#btn_submitExportCancel').hide();
+ jQuery('#statusExportInprogress').hide();
+ jQuery('#statusExportFailed').show();
+ jQuery('.newexport').show();
+ });
+ });
+};
+
+window.lam.html = window.lam.html || {};
+
+/**
+ * Shows a DN selection for the given input field.
+ *
+ * @param fieldId id of input field
+ * @param title title of dialog
+ * @param okText ok button text
+ * @param cancelText cancel button text
+ * @param tokenName CSRF token name
+ * @param tokenValue CSRF token value
+ */
+window.lam.html.showDnSelection = function(fieldId, title, okText, cancelText, tokenName, tokenValue) {
+ var field = jQuery('#' + fieldId);
+ var fieldDiv = jQuery('#dlg_' + fieldId);
+ if (!fieldDiv.length > 0) {
+ jQuery('body').append(jQuery('
'));
+ }
+ var dnValue = field.val();
+ var data = {
+ jsonInput: ''
+ };
+ data[tokenName] = tokenValue;
+ data['fieldId'] = fieldId;
+ data['dn'] = dnValue;
+ jQuery.ajax({
+ url: '../misc/ajax.php?function=dnselection',
+ method: 'POST',
+ data: data
+ })
+ .done(function(jsonData) {
+ jQuery('#dlg_' + fieldId).html(jsonData.dialogData);
+ var buttonList = {};
+ buttonList[cancelText] = function() { jQuery(this).dialog("destroy"); };
+ jQuery('#dlg_' + fieldId).dialog({
+ modal: true,
+ title: title,
+ dialogClass: 'defaultBackground',
+ buttons: buttonList,
+ width: 'auto',
+ maxHeight: 600,
+ position: {my: 'center', at: 'center', of: window}
+ });
+ });
+};
+
+/**
+ * Selects the DN from dialog.
+ *
+ * @param el ok button in dialog
+ * @param fieldId field id of input field
+ * @returns false
+ */
+window.lam.html.selectDn = function(el, fieldId) {
+ var field = jQuery('#' + fieldId);
+ var dn = jQuery(el).parents('.row').data('dn');
+ field.val(dn);
+ jQuery('#dlg_' + fieldId).dialog("destroy");
+ return false;
+}
+
+/**
+ * Updates the DN selection.
+ *
+ * @param el element
+ * @param fieldId field id of dialog
+ * @param tokenName CSRF token name
+ * @param tokenValue CSRF token value
+ */
+window.lam.html.updateDnSelection = function(el, fieldId, tokenName, tokenValue) {
+ var fieldDiv = jQuery('#dlg_' + fieldId);
+ var dn = jQuery(el).parents('.row').data('dn');
+ var data = {
+ jsonInput: ''
+ };
+ data[tokenName] = tokenValue;
+ data['fieldId'] = fieldId;
+ data['dn'] = dn;
+ jQuery.ajax({
+ url: '../misc/ajax.php?function=dnselection',
+ method: 'POST',
+ data: data
+ })
+ .done(function(jsonData) {
+ jQuery('#dlg_' + fieldId).html(jsonData.dialogData);
+ jQuery(fieldDiv).dialog({
+ position: {my: 'center', at: 'center', of: window}
+ });
+ })
+ .fail(function() {
+ jQuery(fieldDiv).dialog("close");
+ });
+}
+
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..5c6c718d 100644
--- a/lam/templates/misc/ajax.php
+++ b/lam/templates/misc/ajax.php
@@ -1,5 +1,12 @@
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 === 'export') {
+ include_once('../../lib/export.inc');
+ $attributes = $_POST['attributes'];
+ $baseDn = $_POST['baseDn'];
+ $ending = $_POST['ending'];
+ $filter = $_POST['filter'];
+ $format = $_POST['format'];
+ $includeSystem = ($_POST['includeSystem'] === 'true');
+ $saveAsFile = ($_POST['saveAsFile'] === 'true');
+ $searchScope = $_POST['searchScope'];
+ $exporter = new Exporter($baseDn, $searchScope, $filter, $attributes, $includeSystem, $saveAsFile, $format, $ending);
+ ob_start();
+ $jsonOut = $exporter->doExport();
+ 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']));
@@ -100,6 +133,12 @@ class Ajax {
ob_end_clean();
echo $jsonOut;
}
+ elseif ($function === 'dnselection') {
+ ob_start();
+ $jsonOut = $this->dnSelection();
+ ob_end_clean();
+ echo $jsonOut;
+ }
}
/**
@@ -132,6 +171,128 @@ class Ajax {
echo json_encode(array("result" => $result));
}
+ /**
+ * Handles DN selection fields.
+ *
+ * @return string JSON output
+ */
+ private function dnSelection() {
+ $dn = trim($_POST['dn']);
+ if (empty($dn) || !get_preg($dn, 'dn')) {
+ $dnList = $this->getDefaultDns();
+ $dn = null;
+ }
+ else {
+ $dnList = $this->getSubDns($dn);
+ }
+ $html = $this->buildDnSelectionHtml($dnList, $dn);
+ return json_encode(array('dialogData' => $html));
+ }
+
+ /**
+ * Returns a list of default DNs from account types + tree suffix.
+ *
+ * @return string[] default DNs
+ */
+ private function getDefaultDns() {
+ $typeManager = new TypeManager();
+ $baseDnList = array();
+ foreach ($typeManager->getConfiguredTypes() as $type) {
+ $suffix = $type->getSuffix();
+ if (!empty($suffix)) {
+ $baseDnList[] = $suffix;
+ }
+ }
+ $treeSuffix = $_SESSION['config']->get_Suffix('tree');
+ if (!empty($treeSuffix)) {
+ $baseDnList[] = $suffix;
+ }
+ $baseDnList = array_unique($baseDnList);
+ usort($baseDnList, 'compareDN');
+ return $baseDnList;
+ }
+
+ /**
+ * Returns the HTML to build the DN selection list.
+ *
+ * @param string[] $dnList DN list
+ * @param string $currentDn current DN
+ */
+ private function buildDnSelectionHtml($dnList, $currentDn) {
+ $fieldId = trim($_POST['fieldId']);
+ $mainRow = new htmlResponsiveRow();
+ $onclickUp = 'window.lam.html.updateDnSelection(this, \''
+ . htmlspecialchars($fieldId) . '\', \'' . getSecurityTokenName() . '\', \''
+ . getSecurityTokenValue() . '\')';
+ if (!empty($currentDn)) {
+ $row = new htmlResponsiveRow();
+ $row->addDataAttribute('dn', $currentDn);
+ $text = new htmlOutputText($currentDn);
+ $text->setIsBold(true);
+ $row->add($text, 12, 9);
+ $row->setCSSClasses(array('text-right'));
+ $buttonId = base64_encode($currentDn);
+ $buttonId = str_replace('=', '', $buttonId);
+ $button = new htmlButton($buttonId, _('Ok'));
+ $button->setIconClass('okButton');
+ $button->setOnClick('window.lam.html.selectDn(this, \'' . htmlspecialchars($fieldId) . '\')');
+ $row->add($button, 12, 3);
+ $mainRow->add($row, 12);
+ // back up
+ $row = new htmlResponsiveRow();
+ $row->addDataAttribute('dn', extractDNSuffix($currentDn));
+ $text = new htmlLink('..', '#');
+ $text->setCSSClasses(array('bold'));
+ $text->setOnClick($onclickUp);
+ $row->add($text, 12, 9);
+ $row->setCSSClasses(array('text-right'));
+ $buttonId = base64_encode('..');
+ $buttonId = str_replace('=', '', $buttonId);
+ $button = new htmlButton($buttonId, _('Up'));
+ $button->setIconClass('upButton');
+ $button->setOnClick($onclickUp);
+ $row->add($button, 12, 3);
+ $mainRow->add($row, 12);
+ }
+ foreach ($dnList as $dn) {
+ $row = new htmlResponsiveRow();
+ $row->addDataAttribute('dn', $dn);
+ $link = new htmlLink($dn, '#');
+ $link->setOnClick($onclickUp);
+ $row->add($link, 12, 9);
+ $row->setCSSClasses(array('text-right'));
+ $buttonId = base64_encode($dn);
+ $buttonId = str_replace('=', '', $buttonId);
+ $button = new htmlButton($buttonId, _('Ok'));
+ $button->setIconClass('okButton');
+ $button->setOnClick('window.lam.html.selectDn(this, \'' . htmlspecialchars($fieldId) . '\')');
+ $row->add($button, 12, 3);
+ $mainRow->add($row, 12);
+ }
+ $tabindex = 1000;
+ ob_start();
+ parseHtml(null, $mainRow, array(), false, $tabindex, 'user');
+ $out = ob_get_contents();
+ ob_end_clean();
+ return $out;
+ }
+
+ /**
+ * Returns the sub DNs of given DN.
+ *
+ * @param string $dn DN
+ * @return string[] sub DNs
+ */
+ private function getSubDns($dn) {
+ $dnEntries = ldapListDN($dn);
+ $dnList = array();
+ foreach ($dnEntries as $entry) {
+ $dnList[] = $entry['dn'];
+ }
+ usort($dnList, 'compareDN');
+ return $dnList;
+ }
+
}
diff --git a/lam/templates/tools/importexport.php b/lam/templates/tools/importexport.php
new file mode 100644
index 00000000..22dc8bc7
--- /dev/null
+++ b/lam/templates/tools/importexport.php
@@ -0,0 +1,397 @@
+
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+\n";
+ $container = new htmlResponsiveRow();
+ $container->add(new htmlTitle(_("Import")), 12);
+ $sources = array(
+ _('Text input') => 'text',
+ _('File') => 'file',
+ );
+ $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->add(new htmlResponsiveInputCheckbox('noStop', false, _('Don\'t stop on errors')), 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) {
+ try {
+ checkImportData();
+ }
+ catch (LAMException $e) {
+ $container = new htmlResponsiveRow();
+ $container->add(new htmlStatusMessage('ERROR', $e->getTitle(), $e->getMessage()), 12);
+ parseHtml(null, $container, array(), false, $tabindex, 'user');
+ printImportTabContent($tabindex);
+ return;
+ }
+ echo "
\n");
+}
+
+/**
+ * Checks if the import data is ok.
+ *
+ * @throws LAMException 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)) {
+ throw new LAMException(_('You must either upload a file or provide an import in the text box.'));
+ }
+ $lines = preg_split("/\n|\r\n|\r/", $ldif);
+ $importer = new Importer();
+ $tasks = $importer->getTasks($lines);
+ $_SESSION[Importer::SESSION_KEY_TASKS] = $tasks;
+ $_SESSION[Importer::SESSION_KEY_COUNT] = sizeof($tasks);
+ $_SESSION[Importer::SESSION_KEY_STOP_ON_ERROR] = (!isset($_POST['noStop']) || ($_POST['noStop'] != 'on'));
+}
+
+/**
+ * Prints the content area for the export tab.
+ *
+ * @param int $tabindex tabindex
+ */
+function printExportTabContent(&$tabindex) {
+ echo "
\n");
+}
+
+/**
+ * Returns the default base DN.
+ *
+ * @return string base DN
+ */
+function getDefaultBaseDn() {
+ $typeManager = new TypeManager();
+ $baseDn = '';
+ foreach ($typeManager->getConfiguredTypes() as $type) {
+ $suffix = $type->getSuffix();
+ if (empty($baseDn) || (!empty($suffix) && (strlen($suffix) < strlen($baseDn)))) {
+ $baseDn = $suffix;
+ }
+ }
+ $treeSuffix = $_SESSION['config']->get_Suffix('tree');
+ if (empty($baseDn) || (!empty($treeSuffix) && (strlen($treeSuffix) < strlen($baseDn)))) {
+ $baseDn = $treeSuffix;
+ }
+ return $baseDn;
+}
+
+/**
+ * Prints the content area for the export tab during processing state.
+ *
+ * @param int $tabindex tabindex
+ */
+function printExportTabProcessing(&$tabindex) {
+ try {
+ checkExportData();
+ }
+ catch (LAMException $e) {
+ $container = new htmlResponsiveRow();
+ $container->add(new htmlStatusMessage('ERROR', $e->getTitle(), $e->getMessage()), 12);
+ parseHtml(null, $container, array(), false, $tabindex, 'user');
+ printExportTabContent($tabindex);
+ return;
+ }
+ echo "
\n");
+}
+
+/**
+ * Checks if the export data is ok.
+ *
+ * @throws LAMException error message if not valid
+ */
+function checkExportData() {
+ if (empty($_POST['baseDn'])) {
+ throw new LAMException(_('This field is required.'), _('Base DN'));
+ }
+}
+
+include '../../lib/adminFooter.inc';
diff --git a/lam/tests/lib/importTest.php b/lam/tests/lib/importTest.php
new file mode 100644
index 00000000..b60528fa
--- /dev/null
+++ b/lam/tests/lib/importTest.php
@@ -0,0 +1,462 @@
+setExpectedException(LAMException::class, 'this is no LDIF');
+
+ $importer = new Importer();
+ $importer->getTasks($lines);
+ }
+
+ /**
+ * Wrong format version.
+ */
+ public function testWrongVersion() {
+ $lines = array(
+ "version: 3"
+ );
+
+ $this->setExpectedException(LAMException::class, 'version: 3');
+
+ $importer = new Importer();
+ $importer->getTasks($lines);
+ }
+
+ /**
+ * Multiple versions.
+ */
+ public function testMultipleVersions() {
+ $lines = array(
+ "version: 1",
+ "",
+ "version: 1"
+ );
+
+ $this->setExpectedException(LAMException::class);
+
+ $importer = new Importer();
+ $importer->getTasks($lines);
+ }
+
+ /**
+ * Data after version.
+ */
+ public function testDataAfterVersion() {
+ $lines = array(
+ "version: 1",
+ "some: data"
+ );
+
+ $this->setExpectedException(LAMException::class);
+
+ $importer = new Importer();
+ $importer->getTasks($lines);
+ }
+
+ /**
+ * DN line without any data.
+ */
+ public function testDnNoData() {
+ $lines = array(
+ "version: 1",
+ "",
+ "dn: uid=test,dc=example,dc=com"
+ );
+
+ $this->setExpectedException(LAMException::class, 'dn: uid=test,dc=example,dc=com');
+
+ $importer = new Importer();
+ $importer->getTasks($lines);
+ }
+
+ /**
+ * One complete entry.
+ */
+ public function testSingleFullEntry() {
+ $lines = array(
+ "version: 1",
+ "",
+ "dn: uid=test,dc=example,dc=com",
+ "objectClass: inetOrgPerson",
+ "uid: test",
+ );
+
+ $importer = new Importer();
+ $tasks = $importer->getTasks($lines);
+ $this->assertEquals(1, sizeof($tasks));
+ }
+
+ /**
+ * Change entry with invalid changetype.
+ */
+ public function testChangeInvalidType() {
+ $lines = array(
+ "version: 1",
+ "",
+ "dn: uid=test,dc=example,dc=com",
+ "changeType: invalid",
+ "uid: test",
+ );
+
+ $this->setExpectedException(LAMException::class, 'uid=test,dc=example,dc=com - changeType: invalid');
+
+ $importer = new Importer();
+ $tasks = $importer->getTasks($lines);
+ }
+
+ /**
+ * Change entry with add changetype.
+ */
+ public function testChangeAdd() {
+ $lines = array(
+ "version: 1",
+ "",
+ "dn: uid=test,dc=example,dc=com",
+ "changeType: add",
+ "uid: test",
+ );
+
+ $importer = new Importer();
+ $tasks = $importer->getTasks($lines);
+ $this->assertEquals(1, sizeof($tasks));
+ $task = $tasks[0];
+ $this->assertEquals(AddEntryTask::class, get_class($task));
+ }
+
+ /**
+ * Change entry with modrdn changetype and invalid options.
+ */
+ public function testChangeModRdnInvalidData() {
+ $lines = array(
+ "version: 1",
+ "",
+ "dn: uid=test,dc=example,dc=com",
+ "changeType: modrdn",
+ "uid: test",
+ );
+
+ $this->setExpectedException(LAMException::class, 'uid=test,dc=example,dc=com');
+
+ $importer = new Importer();
+ $tasks = $importer->getTasks($lines);
+ }
+
+ /**
+ * Change entry with modrdn changetype and invalid deleteoldrdn.
+ */
+ public function testChangeModRdnInvalidDeleteoldrdn() {
+ $lines = array(
+ "version: 1",
+ "",
+ "dn: uid=test,dc=example,dc=com",
+ "changeType: modrdn",
+ "newrdn: uid1=test",
+ "deleteoldrdn: x",
+ );
+
+ $this->setExpectedException(LAMException::class, 'uid=test,dc=example,dc=com');
+
+ $importer = new Importer();
+ $tasks = $importer->getTasks($lines);
+ }
+
+ /**
+ * Change entry with modrdn changetype.
+ */
+ public function testChangeModRdn() {
+ $lines = array(
+ "version: 1",
+ "",
+ "dn: uid=test,dc=example,dc=com",
+ "changeType: modrdn",
+ "newrdn: uid1=test",
+ "deleteoldrdn: 0",
+ );
+
+ $importer = new Importer();
+ $tasks = $importer->getTasks($lines);
+ $this->assertEquals(1, sizeof($tasks));
+ $task = $tasks[0];
+ $this->assertEquals(RenameEntryTask::class, get_class($task));
+ }
+
+ /**
+ * Change entry with delete changetype with extra line.
+ */
+ public function testChangeDeleteInvalid() {
+ $lines = array(
+ "version: 1",
+ "",
+ "dn: uid=test,dc=example,dc=com",
+ "changeType: delete",
+ "uid: test",
+ );
+
+ $this->setExpectedException(LAMException::class, 'uid=test,dc=example,dc=com');
+
+ $importer = new Importer();
+ $tasks = $importer->getTasks($lines);
+ }
+
+ /**
+ * Change entry with delete changetype.
+ */
+ public function testChangeDelete() {
+ $lines = array(
+ "version: 1",
+ "",
+ "dn: uid=test,dc=example,dc=com",
+ "changeType: delete",
+ );
+
+ $importer = new Importer();
+ $tasks = $importer->getTasks($lines);
+ $this->assertEquals(1, sizeof($tasks));
+ $task = $tasks[0];
+ $this->assertEquals(DeleteEntryTask::class, get_class($task));
+ }
+
+ /**
+ * Change entry with modify changetype with invalid operation.
+ */
+ public function testChangeModifyInvalid() {
+ $lines = array(
+ "version: 1",
+ "",
+ "dn: uid=test,dc=example,dc=com",
+ "changeType: modify",
+ "invalid: test",
+ );
+
+ $this->setExpectedException(LAMException::class, 'uid=test,dc=example,dc=com');
+
+ $importer = new Importer();
+ $tasks = $importer->getTasks($lines);
+ }
+
+ /**
+ * Change entry with modify changetype and add operation.
+ */
+ public function testChangeModifyAddInvalid() {
+ $lines = array(
+ "version: 1",
+ "",
+ "dn: uid=test,dc=example,dc=com",
+ "changeType: modify",
+ "add: uid",
+ "uid: uid1",
+ "invalid: uid2"
+ );
+
+ $this->setExpectedException(LAMException::class, 'uid=test,dc=example,dc=com');
+
+ $importer = new Importer();
+ $tasks = $importer->getTasks($lines);
+ }
+
+ /**
+ * Change entry with modify changetype and add operation.
+ */
+ public function testChangeModifyAdd() {
+ $lines = array(
+ "version: 1",
+ "",
+ "dn: uid=test,dc=example,dc=com",
+ "changeType: modify",
+ "add: uid",
+ "uid: uid1",
+ "uid: uid2"
+ );
+
+ $importer = new Importer();
+ $tasks = $importer->getTasks($lines);
+ $this->assertEquals(1, sizeof($tasks));
+ $task = $tasks[0];
+ $this->assertEquals(MultiTask::class, get_class($task));
+ $subtasks = $task->getTasks();
+ $this->assertEquals(1, sizeof($subtasks));
+ $subTask = $subtasks[0];
+ $this->assertEquals(AddAttributesTask::class, get_class($subTask));
+ $this->assertEquals($subTask->getDn(), 'uid=test,dc=example,dc=com');
+ $attributes = $subTask->getAttributes();
+ $this->assertEquals(1, sizeof($attributes));
+ $this->assertEquals(2, sizeof($attributes['uid']));
+ $this->assertTrue(in_array('uid1', $attributes['uid']));
+ $this->assertTrue(in_array('uid2', $attributes['uid']));
+ }
+
+ /**
+ * Change entry with modify changetype and two add operations.
+ */
+ public function testChangeModifyAddTwice() {
+ $lines = array(
+ "version: 1",
+ "",
+ "dn: uid=test,dc=example,dc=com",
+ "changeType: modify",
+ "add: uid",
+ "uid: uid1",
+ "uid: uid2",
+ "-",
+ "add: gn",
+ "gn: name1",
+ "gn: name2"
+ );
+
+ $importer = new Importer();
+ $tasks = $importer->getTasks($lines);
+ $this->assertEquals(1, sizeof($tasks));
+ $task = $tasks[0];
+ $this->assertEquals(MultiTask::class, get_class($task));
+ $subtasks = $task->getTasks();
+ $this->assertEquals(2, sizeof($subtasks));
+ $subTask = $subtasks[0];
+ $this->assertEquals(AddAttributesTask::class, get_class($subTask));
+ $this->assertEquals($subTask->getDn(), 'uid=test,dc=example,dc=com');
+ $attributes = $subTask->getAttributes();
+ $this->assertEquals(1, sizeof($attributes));
+ $this->assertEquals(2, sizeof($attributes['uid']));
+ $this->assertTrue(in_array('uid1', $attributes['uid']));
+ $this->assertTrue(in_array('uid2', $attributes['uid']));
+ $subTask = $subtasks[1];
+ $this->assertEquals(AddAttributesTask::class, get_class($subTask));
+ $this->assertEquals($subTask->getDn(), 'uid=test,dc=example,dc=com');
+ $attributes = $subTask->getAttributes();
+ $this->assertEquals(1, sizeof($attributes));
+ $this->assertEquals(2, sizeof($attributes['gn']));
+ $this->assertTrue(in_array('name1', $attributes['gn']));
+ $this->assertTrue(in_array('name2', $attributes['gn']));
+ }
+
+ /**
+ * Change entry with modify changetype and delete operation.
+ */
+ public function testChangeModifyDelete() {
+ $lines = array(
+ "version: 1",
+ "",
+ "dn: uid=test,dc=example,dc=com",
+ "changeType: modify",
+ "delete: uid",
+ "uid: uid1",
+ "uid: uid2"
+ );
+
+ $importer = new Importer();
+ $tasks = $importer->getTasks($lines);
+ $this->assertEquals(1, sizeof($tasks));
+ $task = $tasks[0];
+ $this->assertEquals(MultiTask::class, get_class($task));
+ $subtasks = $task->getTasks();
+ $this->assertEquals(1, sizeof($subtasks));
+ $subTask = $subtasks[0];
+ $this->assertEquals(DeleteAttributesTask::class, get_class($subTask));
+ $this->assertEquals($subTask->getDn(), 'uid=test,dc=example,dc=com');
+ $attributes = $subTask->getAttributes();
+ $this->assertEquals(1, sizeof($attributes));
+ $this->assertEquals(2, sizeof($attributes['uid']));
+ $this->assertTrue(in_array('uid1', $attributes['uid']));
+ $this->assertTrue(in_array('uid2', $attributes['uid']));
+ }
+
+ /**
+ * Change entry with modify changetype and delete operation.
+ */
+ public function testChangeModifyDeleteAll() {
+ $lines = array(
+ "version: 1",
+ "",
+ "dn: uid=test,dc=example,dc=com",
+ "changeType: modify",
+ "delete: uid",
+ );
+
+ $importer = new Importer();
+ $tasks = $importer->getTasks($lines);
+ $this->assertEquals(1, sizeof($tasks));
+ $task = $tasks[0];
+ $this->assertEquals(MultiTask::class, get_class($task));
+ $subtasks = $task->getTasks();
+ $this->assertEquals(1, sizeof($subtasks));
+ $subTask = $subtasks[0];
+ $this->assertEquals(DeleteAttributesTask::class, get_class($subTask));
+ $this->assertEquals($subTask->getDn(), 'uid=test,dc=example,dc=com');
+ $attributes = $subTask->getAttributes();
+ $this->assertTrue(empty($attributes));
+ }
+
+ /**
+ * Change entry with modify changetype and replace operation.
+ */
+ public function testChangeModifyReplace() {
+ $lines = array(
+ "version: 1",
+ "",
+ "dn: uid=test,dc=example,dc=com",
+ "changeType: modify",
+ "replace: uid",
+ "uid: uid1",
+ "uid: uid2",
+ );
+
+ $importer = new Importer();
+ $tasks = $importer->getTasks($lines);
+ $this->assertEquals(1, sizeof($tasks));
+ $task = $tasks[0];
+ $this->assertEquals(MultiTask::class, get_class($task));
+ $subtasks = $task->getTasks();
+ $this->assertEquals(1, sizeof($subtasks));
+ $subTask = $subtasks[0];
+ $this->assertEquals(ReplaceAttributesTask::class, get_class($subTask));
+ $this->assertEquals($subTask->getDn(), 'uid=test,dc=example,dc=com');
+ $attributes = $subTask->getAttributes();
+ $this->assertEquals(1, sizeof($attributes));
+ $this->assertEquals(2, sizeof($attributes['uid']));
+ $this->assertTrue(in_array('uid1', $attributes['uid']));
+ $this->assertTrue(in_array('uid2', $attributes['uid']));
+ }
+
+}