diff --git a/admin/inc/class.admin_import_groups_csv.inc.php b/admin/inc/class.admin_import_groups_csv.inc.php index e098ee0900..654f2a708a 100644 --- a/admin/inc/class.admin_import_groups_csv.inc.php +++ b/admin/inc/class.admin_import_groups_csv.inc.php @@ -15,27 +15,9 @@ use EGroupware\Api; /** * class import_csv for admin (groups) */ -class admin_import_groups_csv implements importexport_iface_import_plugin { +class admin_import_groups_csv extends importexport_basic_import_csv +{ - private static $plugin_options = array( - 'fieldsep', // char - 'charset', // string - 'num_header_lines', // int number of header lines - 'field_conversion', // array( $csv_col_num => conversion) - 'field_mapping', // array( $csv_col_num => adb_filed) - 'conditions', /* => array containing condition arrays: - 'type' => exists, // exists - 'string' => '#kundennummer', - 'true' => array( - 'action' => update, - 'last' => true, - ), - 'false' => array( - 'action' => insert, - 'last' => true, - ),*/ - - ); /** * actions which could be done to data entries @@ -49,128 +31,80 @@ class admin_import_groups_csv implements importexport_iface_import_plugin { */ protected static $conditions = array( 'exists' ); - /** - * @var definition - */ - private $definition; - /** - * @var bool - */ - private $dry_run = false; + protected function import_record(importexport_iface_egw_record &$record, &$import_csv) + { + if($this->definition->plugin_options['conditions']) + { + foreach($this->definition->plugin_options['conditions'] as $condition) + { + switch($condition['type']) + { + // exists + case 'exists' : + $accounts = array(); - /** - * List of import warnings - */ - protected $warnings = array(); + // Skip the search if the field is empty + if($record[$condition['string']] !== '') + { - /** - * List of import errors - */ - protected $errors = array(); - - /** - * List of actions, and how many times that action was taken - */ - protected $results = array(); - - /** - * imports entries according to given definition object. - * @param resource $_stream - * @param string $_charset - * @param definition $_definition - */ - public function import( $_stream, importexport_definition $_definition ) { - $import_csv = new importexport_import_csv( $_stream, array( - 'fieldsep' => $_definition->plugin_options['fieldsep'], - 'charset' => $_definition->plugin_options['charset'], - )); - - $this->definition = $_definition; - - // dry run? - $this->dry_run = isset( $_definition->plugin_options['dry_run'] ) ? $_definition->plugin_options['dry_run'] : false; - - // set FieldMapping. - $import_csv->mapping = $_definition->plugin_options['field_mapping']; - - // set FieldConversion - $import_csv->conversion = $_definition->plugin_options['field_conversion']; - - //check if file has a header lines - if ( isset( $_definition->plugin_options['num_header_lines'] ) && $_definition->plugin_options['num_header_lines'] > 0) { - $import_csv->skip_records($_definition->plugin_options['num_header_lines']); - } elseif(isset($_definition->plugin_options['has_header_line']) && $_definition->plugin_options['has_header_line']) { - // First method is preferred - $import_csv->skip_records(1); - } - - // Start counting successes - $count = 0; - $this->results = array(); - - // Failures - $this->errors = array(); - - while ( $record = $import_csv->get_record() ) { - $success = false; - // don't import empty records - if( count( array_unique( $record ) ) < 2 ) continue; - - importexport_import_csv::convert($record, admin_egw_group_record::$types, 'admin'); - - if ( $_definition->plugin_options['conditions'] ) { - foreach ( $_definition->plugin_options['conditions'] as $condition ) { - switch ( $condition['type'] ) { - // exists - case 'exists' : - $accounts = array(); - - // Skip the search if the field is empty - if($record[$condition['string']] !== '') { - - $accounts = $GLOBALS['egw']->accounts->search(array( - 'type' => 'groups', - 'query' => $record[$condition['string']], - 'query_type' => $condition['string'] - )); - } - // Search looks in the given field, but doesn't do an exact match - foreach ( (array)$accounts as $key => $account ) + $accounts = $GLOBALS['egw']->accounts->search(array( + 'type' => 'groups', + 'query' => $record[$condition['string']], + 'query_type' => $condition['string'] + )); + } + // Search looks in the given field, but doesn't do an exact match + foreach((array)$accounts as $key => $account) + { + if($account[$condition['string']] != $record[$condition['string']]) { - if($account[$condition['string']] != $record[$condition['string']]) unset($accounts[$key]); + unset($accounts[$key]); } - if ( is_array( $accounts ) && count( $accounts ) >= 1 ) { - $account = current($accounts); - // apply action to all contacts matching this exists condition - $action = $condition['true']; - foreach ( (array)$accounts as $account ) { - // Read full account, and copy needed info for accounts->save() - $account = $GLOBALS['egw']->accounts->read($account['account_id']); - $record['account_id'] = $account['account_id']; - $record['account_firstname'] = $account['account_firstname']; - $record['account_lastname'] = $account['account_lastname']; - $success = $this->action( $action['action'], $record, $import_csv->get_current_position() ); - } - } else { - $action = $condition['false']; - $success = ($this->action( $action['action'], $record, $import_csv->get_current_position() )); + } + if(is_array($accounts) && count($accounts) >= 1) + { + $account = current($accounts); + // apply action to all contacts matching this exists condition + $action = $condition['true']; + foreach((array)$accounts as $account) + { + // Read full account, and copy needed info for accounts->save() + $account = $GLOBALS['egw']->accounts->read($account['account_id']); + $record['account_id'] = $account['account_id']; + $record['account_firstname'] = $account['account_firstname']; + $record['account_lastname'] = $account['account_lastname']; + $success = $this->admin_action($action['action'], $record, $import_csv->get_current_position()); } - break; + } + else + { + $action = $condition['false']; + $success = ($this->admin_action($action['action'], $record, $import_csv->get_current_position())); + } + break; - // not supported action - default : - die('condition / action not supported!!!'); - } - if ($action['last']) break; + // not supported action + default : + die('condition / action not supported!!!'); + } + if($action['last']) + { + break; } - } else { - // unconditional insert - $success = $this->action( 'insert', $record, $import_csv->get_current_position() ); } - if($success) $count++; } - return $count; + else + { + // unconditional insert + $success = $this->admin_action('insert', $record, $import_csv->get_current_position()); + } + return $success; + } + + protected function action($_action, importexport_iface_egw_record &$record, $record_num = 0) + { + // Not used, see admin_action() } /** @@ -180,7 +114,7 @@ class admin_import_groups_csv implements importexport_iface_import_plugin { * @param array $_data contact data for the action * @return bool success or not */ - private function action ( $_action, $_data, $record_num = 0 ) { + private function admin_action($_action, $_data, $record_num = 0 ) { switch ($_action) { case 'none' : return true; diff --git a/admin/inc/class.admin_import_users_csv.inc.php b/admin/inc/class.admin_import_users_csv.inc.php index 88457587e7..43d97c9ae9 100644 --- a/admin/inc/class.admin_import_users_csv.inc.php +++ b/admin/inc/class.admin_import_users_csv.inc.php @@ -15,28 +15,8 @@ use EGroupware\Api; /** * class import_csv for admin (users) */ -class admin_import_users_csv implements importexport_iface_import_plugin { - - private static $plugin_options = array( - 'fieldsep', // char - 'charset', // string - 'num_header_lines', // int number of header lines - 'field_conversion', // array( $csv_col_num => conversion) - 'field_mapping', // array( $csv_col_num => adb_filed) - 'conditions', /* => array containing condition arrays: - 'type' => exists, // exists - 'string' => '#kundennummer', - 'true' => array( - 'action' => update, - 'last' => true, - ), - 'false' => array( - 'action' => insert, - 'last' => true, - ),*/ - - ); - +class admin_import_users_csv extends importexport_basic_import_csv +{ /** * actions which could be done to data entries */ @@ -49,142 +29,99 @@ class admin_import_users_csv implements importexport_iface_import_plugin { */ protected static $conditions = array( 'exists' ); - /** - * @var definition - */ - private $definition; - - /** - * @var bool - */ - private $dry_run = false; - - /** - * @var bool is current user admin? - */ - private $is_admin = false; - - /** - * @var int - */ - private $user = null; - - /** - * List of import warnings - */ - protected $warnings = array(); - - /** - * List of import errors - */ - protected $errors = array(); - - /** - * List of actions, and how many times that action was taken - */ - protected $results = array(); - /** * imports entries according to given definition object. * @param resource $_stream * @param string $_charset * @param definition $_definition */ - public function import( $_stream, importexport_definition $_definition ) { - $import_csv = new importexport_import_csv( $_stream, array( - 'fieldsep' => $_definition->plugin_options['fieldsep'], - 'charset' => $_definition->plugin_options['charset'], - )); + public function init(importexport_definition $definition, importexport_import_csv $import_csv = null) + { + parent::init($definition, $import_csv); // TODO: Change the autogenerated stub - $this->definition = $_definition; + $this->lookups['account_status'] = array('A' => lang('Active'), '' => lang('Disabled'), + 'D' => lang('Disabled')); + } - // user, is admin ? - $this->is_admin = isset( $GLOBALS['egw_info']['user']['apps']['admin'] ) && $GLOBALS['egw_info']['user']['apps']['admin']; - $this->user = $GLOBALS['egw_info']['user']['account_id']; - - // dry run? - $this->dry_run = isset( $_definition->plugin_options['dry_run'] ) ? $_definition->plugin_options['dry_run'] : false; - - // set FieldMapping. - $import_csv->mapping = $_definition->plugin_options['field_mapping']; - - // set FieldConversion - $import_csv->conversion = $_definition->plugin_options['field_conversion']; - - //check if file has a header lines - if ( isset( $_definition->plugin_options['num_header_lines'] ) && $_definition->plugin_options['num_header_lines'] > 0) { - $import_csv->skip_records($_definition->plugin_options['num_header_lines']); - } elseif(isset($_definition->plugin_options['has_header_line']) && $_definition->plugin_options['has_header_line']) { - // First method is preferred - $import_csv->skip_records(1); + protected function import_record(importexport_iface_egw_record &$record, &$import_csv) + { + $success = false; + // don't import empty records + if(count(array_unique($record)) < 2) + { + return $success; } - $admin_cmd = $_definition->plugin_options['admin_cmd']; - // Start counting successes - $count = 0; - $this->results = array(); + if(strtolower($record['account_expires']) == 'never') + { + $record['account_expires'] = -1; + } - // Failures - $this->errors = array(); - - $lookups = array( - 'account_status' => array('A' => lang('Active'), '' => lang('Disabled'), 'D' => lang('Disabled')), - ); - - while ( $record = $import_csv->get_record() ) { - $success = false; - // don't import empty records - if( count( array_unique( $record ) ) < 2 ) continue; - - if(strtolower($record['account_expires']) == 'never') $record['account_expires'] = -1; - importexport_import_csv::convert($record, admin_egw_user_record::$types, 'admin', $lookups); - - if ( $_definition->plugin_options['conditions'] ) { - foreach ( $_definition->plugin_options['conditions'] as $condition ) { - switch ( $condition['type'] ) { - // exists - case 'exists' : - $accounts = array(); - // Skip the search if the field is empty - if($record[$condition['string']] !== '') + if($this->definition->plugin_options['conditions']) + { + foreach($this->definition->plugin_options['conditions'] as $condition) + { + switch($condition['type']) + { + // exists + case 'exists' : + $accounts = array(); + // Skip the search if the field is empty + if($record[$condition['string']] !== '') + { + $accounts = $GLOBALS['egw']->accounts->search(array( + 'type' => 'accounts', + 'query' => $record[$condition['string']], + 'query_type' => $condition['string'] + )); + } + // Search looks in the given field, but doesn't do an exact match + foreach((array)$accounts as $key => $account) + { + if($account[$condition['string']] != $record[$condition['string']]) { - $accounts = $GLOBALS['egw']->accounts->search(array( - 'type' => 'accounts', - 'query' => $record[$condition['string']], - 'query_type' => $condition['string'] - )); + unset($accounts[$key]); } - // Search looks in the given field, but doesn't do an exact match - foreach ( (array)$accounts as $key => $account ) + } + if(is_array($accounts) && count($accounts) >= 1) + { + // apply action to all contacts matching this exists condition + $action = $condition['true']; + foreach((array)$accounts as $account) { - if($account[$condition['string']] != $record[$condition['string']]) unset($accounts[$key]); + $record['account_id'] = $account['account_id']; + $success = $this->admin_action($action['action'], $record, $import_csv->get_current_position(), $admin_cmd); } - if ( is_array( $accounts ) && count( $accounts ) >= 1 ) { - // apply action to all contacts matching this exists condition - $action = $condition['true']; - foreach ( (array)$accounts as $account ) { - $record['account_id'] = $account['account_id']; - $success = $this->action( $action['action'], $record, $import_csv->get_current_position(), $admin_cmd ); - } - } else { - $action = $condition['false']; - $success = ($this->action( $action['action'], $record, $import_csv->get_current_position(), $admin_cmd )); - } - break; + } + else + { + $action = $condition['false']; + $success = ($this->admin_action($action['action'], $record, $import_csv->get_current_position(), $admin_cmd)); + } + break; - // not supported action - default : - die('condition / action not supported!!!'); - } - if ($action['last']) break; + // not supported action + default : + die('condition / action not supported!!!'); + } + if($action['last']) + { + break; } - } else { - // unconditional insert - $success = $this->action( 'create', $record, $import_csv->get_current_position(), $admin_cmd ); } - if($success) $count++; } - return $count; + else + { + // unconditional insert + $success = $this->admin_action('create', $record, $import_csv->get_current_position(), $admin_cmd); + } + + return $success; + } + + protected function action($_action, importexport_iface_egw_record &$record, $record_num = 0) + { + // Not used, see admin_action() } /** @@ -194,7 +131,8 @@ class admin_import_users_csv implements importexport_iface_import_plugin { * @param array $_data contact data for the action * @return bool success or not */ - private function action ( $_action, $_data, $record_num = 0, $admin_cmd ) { + private function admin_action($_action, $_data, $record_num = 0, $admin_cmd) + { switch ($_action) { case 'none' : return true; diff --git a/calendar/inc/class.calendar_import_ical.inc.php b/calendar/inc/class.calendar_import_ical.inc.php index 6aca01a610..65723eb582 100644 --- a/calendar/inc/class.calendar_import_ical.inc.php +++ b/calendar/inc/class.calendar_import_ical.inc.php @@ -99,6 +99,8 @@ class calendar_import_ical implements importexport_iface_import_plugin { */ protected $results = array(); + protected $record_count = 0; + /** * imports entries according to given definition object. * @param resource $_stream @@ -132,6 +134,15 @@ class calendar_import_ical implements importexport_iface_import_plugin { echo lang("No preview for iCal"); return; } + else + { + // Estimate event count + while(($line = fgets($_stream)) !== false) + { + $this->record_count += preg_match_all("/^BEGIN:VEVENT/i", $line, $matches); + } + rewind($_stream); + } // switch off notifications by default $plugin_options = $_definition->plugin_options; if(!array_key_exists('no_notification', $_definition->plugin_options)) @@ -180,6 +191,8 @@ class calendar_import_ical implements importexport_iface_import_plugin { $this->results['imported'] += $calendar_ical->events_imported; } + importexport_import_ui::sendUpdate(100, $this->results['imported'], print_r($this->results, true)); + return $calendar_ical->events_imported; } @@ -188,11 +201,16 @@ class calendar_import_ical implements importexport_iface_import_plugin { */ public function event_callback(&$event) { + static $event_count = 0; + // Check & apply value overrides foreach((array)$this->definition->plugin_options['override_values'] as $field => $settings) { $event[$field] = $settings['value']; } + // Update UI with progress + $progress = $this->record_count ? (int)(100 * (++$event_count / $this->record_count)) : false; + importexport_import_ui::sendUpdate($progress, $this->bo->link_title($event), $this->bo->link_title($event)); return true; } diff --git a/importexport/inc/class.importexport_basic_import_csv.inc.php b/importexport/inc/class.importexport_basic_import_csv.inc.php index 37d15fd83e..d92fb04e7d 100644 --- a/importexport/inc/class.importexport_basic_import_csv.inc.php +++ b/importexport/inc/class.importexport_basic_import_csv.inc.php @@ -148,6 +148,12 @@ abstract class importexport_basic_import_csv implements importexport_iface_impor // set FieldConversion $import_csv->conversion = $_definition->plugin_options['field_conversion']; + if(!$this->dry_run) + { + // This needs to scan the whole file, so it can take a while + $record_count = $import_csv->get_num_of_records(); + } + //check if file has a header lines if ( isset( $_definition->plugin_options['num_header_lines'] ) && $_definition->plugin_options['num_header_lines'] > 0) { $import_csv->skip_records($_definition->plugin_options['num_header_lines']); @@ -190,7 +196,19 @@ abstract class importexport_basic_import_csv implements importexport_iface_impor { $this->set_overrides($_definition, $egw_record); } - $success = $this->import_record($egw_record, $import_csv); + try + { + $success = $this->import_record($egw_record, $import_csv); + } + catch (Exception $e) + { + $this->errors[] = $e->getMessage(); + $success = false; + if(!$this->dry_run) + { + importexport_import_ui::sendUpdate("", get_class($e), $e->getMessage()); + } + } if($success) { @@ -204,6 +222,13 @@ abstract class importexport_basic_import_csv implements importexport_iface_impor set_time_limit(10); } + // Send an update to client + if(!$this->dry_run) + { + $complete = $record_count ? (int)(100 * ($import_csv->get_current_position() / $record_count)) : false; + $this->sendUpdate($complete, $egw_record); + } + // Keep a few records for preview, but process the whole file if($this->dry_run && $import_csv->get_current_position() < $GLOBALS['egw_info']['user']['preferences']['common']['maxmatchs']) { @@ -734,5 +759,26 @@ error_log("Searching for $custom_field = $value"); public function get_results() { return $this->results; } + + /** + * Send some progress so the UI doesn't look frozen + * + * @param integer $complete 0-100 or false for indeterminate + * @param importexport_iface_egw_record $record + * @return void + * @throws Api\Json\Exception + */ + protected function sendUpdate($complete, $record) + { + $label = ""; + try + { + $label = $record->get_title() ?: ""; + } + catch (Exception $e) + { + } + importexport_import_ui::sendUpdate($complete, $label, substr(array2string($record->get_record_array()), 0, 120) . '...'); + } } // end of iface_export_plugin ?> diff --git a/importexport/inc/class.importexport_import_csv.inc.php b/importexport/inc/class.importexport_import_csv.inc.php index 8c4ec19f91..8452d55743 100755 --- a/importexport/inc/class.importexport_import_csv.inc.php +++ b/importexport/inc/class.importexport_import_csv.inc.php @@ -175,7 +175,9 @@ class importexport_import_csv implements importexport_iface_import_record { //, break; case 'last' : - while ($this->get_raw_record()) {} + while($this->get_raw_record() !== false) + { + } break; default: //somenumber @@ -207,7 +209,7 @@ class importexport_import_csv implements importexport_iface_import_record { //, return $this->num_of_records; } $current_position = $this->current_position; - while ($this->get_raw_record()) {} + $this->get_raw_record('last'); $this->num_of_records = $this->current_position; $this->get_record($current_position); return $this->num_of_records; diff --git a/importexport/inc/class.importexport_import_ui.inc.php b/importexport/inc/class.importexport_import_ui.inc.php index 0a687499c8..77c2a713f7 100644 --- a/importexport/inc/class.importexport_import_ui.inc.php +++ b/importexport/inc/class.importexport_import_ui.inc.php @@ -137,19 +137,21 @@ use EGroupware\Api\Etemplate; { $this->message .= implode("
\n", $check_message) . "
\n"; } + // Clear first, to prevent request from being collected if the result is the same + $template->setElementAttribute('preview', 'value', ''); if($content['dry-run']) { $preview = $this->preview($plugin, $file, $definition_obj); $template->setElementAttribute('message', 'value', $this->message); - - // Clear first, to prevent request from being collected if the result is the same - $template->setElementAttribute('preview', 'value', ''); $template->setElementAttribute('preview', 'value', $preview); return; } else { + // Set up feedback area + $this->feedback($template, $content['definition'] . ': ' . $content['file']['name']); + // Disable push so we don't overload or spend time notifying EGroupware\Swoolepush\Hooks::pushDisabled(true); @@ -157,9 +159,9 @@ use EGroupware\Api\Etemplate; $count = $plugin->import($file, $definition_obj); EGroupware\Swoolepush\Hooks::pushDisabled(false); - - // Close preview - EGroupware\Api\Json\Response::get()->call('app.importexport.closePreview'); + + // Don't close progress, leave it open so they can see + //static::sendUpdate(null); } } else @@ -180,21 +182,24 @@ use EGroupware\Api\Etemplate; } $total_processed = 0; foreach($plugin->get_results() as $action => $a_count) { - $this->message .= "
\n" . lang($action) . ": $a_count"; + $this->message .= "\n" . lang($action) . ": $a_count"; $total_processed += $a_count; } if(count($plugin->get_warnings())) { - $this->message .= "
\n".lang('Warnings').':'; + $this->message .= "\n" . lang('Warnings') . ':'; foreach($plugin->get_warnings() as $record => $message) { - $this->message .= "
\n$record: $message"; + $this->message .= "\n$record: $message"; } } if(count($plugin->get_errors())) { - $this->message .= "
\n".lang('Problems during import:'); + $this->message .= "\n" . lang('Problems during import:'); foreach($plugin->get_errors() as $record => $message) { - $this->message .= "
\n$record: $message"; + $this->message .= "\n$record: $message"; + } + if($count != $total_processed) + { + $this->message .= "\n" . lang('Some records may not have been imported'); } - if($count != $total_processed) $this->message .= "
\n".lang('Some records may not have been imported'); } if ($dst_file && $content['file']['tmp_name'] == $dst_file) { // Remove file @@ -207,6 +212,11 @@ use EGroupware\Api\Etemplate; $this->message .= lang('Database error'); } catch (Exception $e) { $this->message .= $e->getMessage(); + $this->sendUpdate(false, get_class($e), $e->getMessage()); + } + if($file && !$content['dry-run'] && $count) + { + static::sendUpdate(100, lang('%1 records processed', $count), $this->message); } } elseif($content['cancel']) @@ -379,6 +389,52 @@ use EGroupware\Api\Etemplate; return '
' . lang('Preview') . ' - ' . $plugin->get_name() . '
' . $preview; } + /** + * Setup progress feedback area + * This includes sending the response, but not returning. Progress is sent via Push. + */ + protected function feedback($template, $title) + { + $template->setElementAttribute('progress_title', 'value', $title); + EGroupware\Api\Json\Response::get()->call('app.importexport.progressUpdate', false); + // Send response + EGroupware\Api\Json\Response::sendResult(); + @ob_flush(); + flush(); + fastcgi_finish_request(); + } + + /** + * Send some feedback to the client about how the import is going + * + * @param $complete numeric | null Send null to close the progress + * @param $label + * @param $log + * @return void + * @throws Api\Json\Exception + */ + public static function sendUpdate($complete, $label = '', $log = '') + { // No real push, no updates + if(EGroupware\Api\Json\Push::onlyFallback()) + { + error_log($complete . "% $label\t" . $log); + return; + } + $update = [ + 'progress' => $complete, + 'label' => $label, + 'log' => $log + ]; + // Close the progress + if($complete === null) + { + $update = null; + } + + $p = new Api\Json\Push(); + $p->call('app.importexport.progressUpdate', $update); + } + /** * Simple check to see if the file at least matches the definition * diff --git a/importexport/js/app.ts b/importexport/js/app.ts index 598e77d3bc..b57d740ee1 100644 --- a/importexport/js/app.ts +++ b/importexport/js/app.ts @@ -74,6 +74,13 @@ class ImportExportApp extends EgwApp jQuery('div.filters').hide(); } } + else if(_name == "importexport.import_dialog") + { + // Store popup so we can find it from parent + // Using _name only allows one import (at a time) to be updated + this.egw.window.name = _name; + this.egw.window.opener.egw.storeWindow(this.appname, this.egw.window); + } } /** @@ -149,6 +156,70 @@ class ImportExportApp extends EgwApp preview.parent().hide(); } + progressUpdate(progress : ProgressUpdate) + { + const dialog = window.open('', "importexport.import_dialog"); + + if(!dialog || !dialog.app?.importexport?.et2) + { + this.egw.message(this.egw.lang("Lost the dialog, no progress updates"), "warning"); + if(dialog) + { + dialog.close(); + } + return; + } + // Find the template in the dialog and do the update there + const et2 = dialog.app.importexport.et2; + if(progress !== null) + { + dialog.app.importexport._doProgressUpdate(progress); + } + else + { + dialog.app.importexport._closeProgress(); + } + } + + _doProgressUpdate(progress : ProgressUpdate) + { + const progress_box = this.et2.getDOMWidgetById("progress_box") + progress_box.classList.remove("hideme"); + // Get the td too + progress_box.parentNode.classList.remove("hideme"); + const preview_box = this.et2.getDOMWidgetById("preview_box").parentNode + preview_box.classList.add("hideme"); + + const record = progress_box.getWidgetById("progress_record"); + record.value = progress.label || ""; + + // sl-progress-bar is not an etemplate widget and chokes the server processing if we put it in the xet + let bar = progress_box.querySelector("sl-progress-bar"); + if(!bar) + { + bar = document.createElement("sl-progress-bar"); + progress_box.insertBefore(bar, record.nextSibling); + } + + bar.indeterminate = !Number.isInteger(progress.progress); + bar.value = progress.progress || 0; + + if(progress.log) + { + const log = this.et2.getDOMWidgetById("import_log") + log.value = log.value + "\n" + progress.log; + // Try to scroll to bottom + const text = log.shadowRoot.querySelector("textarea"); + text.scrollTop = text.scrollHeight + 200; + } + } + + _closeProgress() + { + const progress_box = this.et2.getDOMWidgetById("progress_box") + progress_box.parentNode.classList.add("hideme"); + } + /** * Open a popup to run a given definition * @@ -242,3 +313,13 @@ class ImportExportApp extends EgwApp } app.classes.importexport = ImportExportApp; + +interface ProgressUpdate +{ + // Update the progress bar + progress : number | false; + // Set label in progress bar + label? : string; + // Add something to the log + log? : string; +} diff --git a/importexport/templates/default/app.css b/importexport/templates/default/app.css index 883202e0a6..1ac0614934 100644 --- a/importexport/templates/default/app.css +++ b/importexport/templates/default/app.css @@ -1,21 +1,34 @@ -.preview { - position: absolute; - top: 0px; - left: 0px; - width: 97%; - height: 95%; - overflow: hidden; - background-color: white; - z-index: 999; - display: none; - border: 1px solid black; - margin: 1.5%; +.preview, .import_progress { + position: absolute; + top: 0px; + left: 0px; + width: 97%; + height: 95%; + overflow: hidden; + background-color: white; + z-index: 999; + display: none; + border: 1px solid black; + margin: 1.5%; } -.preview .content { +.import_progress { + display: initial; +} + +/* Not sure why these are missing */ +.import_progress sl-progress-bar { + --track-color: hsl(240 5.9% 90%); + --indicator-color: hsl(200.4 98% 39.4%); + --label-color: var(--label-color); + --sl-border-radius-pill: 9999px; +} + +.preview .content, .import_progress .log { overflow: auto; max-height: 99%; display: block; + flex: 1 1 auto; } .preview_box .header, .preview .header { @@ -30,7 +43,6 @@ } .preview [id$='buttons'] { - margin-left: 50%; } .wizard_content > div > table { diff --git a/importexport/templates/default/import_dialog.xet b/importexport/templates/default/import_dialog.xet index b3de42579e..97b08538ca 100644 --- a/importexport/templates/default/import_dialog.xet +++ b/importexport/templates/default/import_dialog.xet @@ -52,7 +52,17 @@ - + + + + + + + + + + + diff --git a/importexport/templates/pixelegg/app.css b/importexport/templates/pixelegg/app.css index d51857440c..bcca65c3bc 100755 --- a/importexport/templates/pixelegg/app.css +++ b/importexport/templates/pixelegg/app.css @@ -11,7 +11,8 @@ * @package projectmanager * @version $Id$ */ -.preview { +.preview, +.import_progress { position: absolute; top: 0px; left: 0px; @@ -24,10 +25,22 @@ border: 1px solid black; margin: 1.5%; } -.preview .content { +.import_progress { + display: initial; +} +/* Not sure why these are missing */ +.import_progress sl-progress-bar { + --track-color: hsl(240, 5.9%, 90%); + --indicator-color: hsl(200.4, 98%, 39.4%); + --label-color: var(--label-color); + --sl-border-radius-pill: 9999px; +} +.preview .content, +.import_progress .log { overflow: auto; max-height: 99%; display: block; + flex: 1 1 auto; } .preview_box .header, .preview .header { @@ -39,9 +52,6 @@ .preview tr { vertical-align: top; } -.preview [id$='buttons'] { - margin-left: 50%; -} .wizard_content > div > table { width: 100%; }