first commit

This commit is contained in:
2024-11-11 18:46:54 +01:00
commit a630d17338
25634 changed files with 4923715 additions and 0 deletions

View File

@@ -0,0 +1,88 @@
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
const $ = window.$;
export default class EntityFieldsValidator {
/**
* Validates entity fields
*
* @returns {boolean}
*/
static validate() {
$('.js-validation-error').addClass('d-none');
return this._checkDuplicateSelectedValues() && this._checkRequiredFields();
}
/**
* Checks if there are no duplicate selected values.
*
* @returns {boolean}
* @private
*/
static _checkDuplicateSelectedValues() {
const uniqueFields = [];
let valid = true;
$('.js-entity-field select').each(function () {
let value = $(this).val();
if (value === 'no') {
return;
}
if ($.inArray(value, uniqueFields) !== -1) {
valid = false;
$('.js-duplicate-columns-warning').removeClass('d-none');
return;
}
uniqueFields.push(value);
});
return valid;
}
/**
* Checks if all required fields are selected.
*
* @returns {boolean}
* @private
*/
static _checkRequiredFields() {
let requiredImportFields = $('.js-import-data-table').data('required-fields');
for (let key in requiredImportFields) {
if (0 === $(`option[value="${requiredImportFields[key]}"]:selected`).length) {
$('.js-missing-column-warning').removeClass('d-none');
$('.js-missing-column').text($(`option[value="${requiredImportFields[key]}"]:first`).text());
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,100 @@
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class ImportBatchSizeCalculator calculates the import batch size.
* Import batch size is the maximum number of records that
* the import should handle in one batch.
*/
export default class ImportBatchSizeCalculator {
constructor() {
// Target execution time in milliseconds.
this._targetExecutionTime = 5000;
// Maximum batch size increase multiplier.
this._maxAcceleration = 4;
// Minimum and maximum import batch sizes.
this._minBatchSize = 5;
this._maxBatchSize = 100;
}
/**
* Marks the start of the import operation.
* Must be executed before starting the import,
* to be able to calculate the import batch size later on.
*/
markImportStart() {
this._importStartTime = new Date().getTime();
}
/**
* Marks the end of the import operation.
* Must be executed after the import operation finishes,
* to be able to calculate the import batch size later on.
*/
markImportEnd() {
this._actualExecutionTime = new Date().getTime() - this._importStartTime;
}
/**
* Calculates how much the import execution time can be increased to still be acceptable.
*
* @returns {number}
* @private
*/
_calculateAcceleration() {
return Math.min(this._maxAcceleration, this._targetExecutionTime / this._actualExecutionTime);
}
/**
* Calculates the recommended import batch size.
*
* @param {number} currentBatchSize current import batch size
* @param {number} maxBatchSize greater than zero, the batch size that shouldn't be exceeded
*
* @returns {number} recommended import batch size
*/
calculateBatchSize(currentBatchSize, maxBatchSize = 0) {
if (!this._importStartTime) {
throw 'Import start is not marked.';
}
if (!this._actualExecutionTime) {
throw 'Import end is not marked.';
}
let candidates = [
this._maxBatchSize,
Math.max(this._minBatchSize, Math.floor(currentBatchSize * this._calculateAcceleration()))
];
if (maxBatchSize > 0) {
candidates.push(maxBatchSize);
}
return Math.min(...candidates);
}
}

View File

@@ -0,0 +1,69 @@
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
import ImportMatchConfiguration from './ImportMatchConfiguration';
import ImportDataTable from './ImportDataTable';
import EntityFieldsValidator from './EntityFieldsValidator';
import Importer from './Importer';
export default class ImportDataPage {
constructor() {
new ImportMatchConfiguration();
new ImportDataTable();
this.importer = new Importer();
$(document).on('click', '.js-process-import', (e) => this.importHandler(e));
$(document).on('click', '.js-abort-import', () => this.importer.requestCancelImport());
$(document).on('click', '.js-close-modal', () => this.importer.progressModal.hide());
$(document).on('click', '.js-continue-import', () => this.importer.continueImport());
}
/**
* Import process event handler
*/
importHandler(e) {
e.preventDefault();
if (!EntityFieldsValidator.validate()) {
return;
}
let configuration = {};
// Collect the configuration from the form into an array.
$('.import-data-configuration-form').find(
'#skip, select[name^=type_value], #csv, #iso_lang, #entity,' +
'#truncate, #match_ref, #regenerate, #forceIDs, #sendemail,' +
'#separator, #multiple_value_separator'
).each((index, $input) => {
configuration[$($input).attr('name')] = $($input).val();
});
this.importer.import(
$('.js-import-process-button').data('import_url'),
configuration
);
}
}

View File

@@ -0,0 +1,173 @@
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
const $ = window.$;
const $importDataTable = $('.js-import-data-table');
/**
* Pagination directions - forward and backward.
*/
const FORWARD = 'forward';
const BACKWARD = 'backward';
export default class ImportDataTable {
constructor() {
this.numberOfColumnsPerPage = this._getNumberOfVisibleColumns();
this.totalNumberOfColumns = this._getTotalNumberOfColumns();
$('.js-import-next-page').on('click', () => this.importNextPageHandler());
$('.js-import-previous-page').on('click', () => this.importPreviousPageHandler());
}
/**
* Handle the next page action in import data table.
*/
importNextPageHandler() {
this._importPaginationHandler(FORWARD);
}
/**
* Handle the previous page action in import data table.
*/
importPreviousPageHandler() {
this._importPaginationHandler(BACKWARD);
}
/**
* Handle the forward and back buttons actions in the import table.
*
* @param {string} direction
* @private
*/
_importPaginationHandler(direction) {
const $currentPageElements = $importDataTable.find('th:visible,td:visible');
const $oppositePaginationButton = direction === FORWARD ? $('.js-import-next-page') : $('.js-import-previous-page');
let lastVisibleColumnFound = false;
let numberOfVisibleColumns = 0;
let $tableColumns = $importDataTable.find('th');
if (direction === BACKWARD) {
// If going backward - reverse the table columns array and use the same logic as forward
$tableColumns = $($tableColumns.toArray().reverse());
}
for (let index in $tableColumns) {
if (isNaN(index)) {
// Reached the last column - hide the opposite pagination button
this._hide($oppositePaginationButton);
break;
}
// Searching for last visible column
if ($($tableColumns[index]).is(':visible')) {
lastVisibleColumnFound = true;
continue;
}
// If last visible column was found - show the column after it
if (lastVisibleColumnFound) {
// If going backward, the column index must be counted from the last element
let showColumnIndex = direction === BACKWARD ? this.totalNumberOfColumns - 1 - index : index;
this._showTableColumnByIndex(showColumnIndex);
numberOfVisibleColumns++;
// If number of visible columns per page is already reached - break the loop
if (numberOfVisibleColumns >= this.numberOfColumnsPerPage) {
this._hide($oppositePaginationButton);
break;
}
}
}
// Hide all the columns from previous page
this._hide($currentPageElements);
// If the first column in the table is not visible - show the "previous" pagination arrow
if (!$importDataTable.find('th:first').is(':visible')) {
this._show($('.js-import-previous-page'));
}
// If the last column in the table is not visible - show the "next" pagination arrow
if (!$importDataTable.find('th:last').is(':visible')) {
this._show($('.js-import-next-page'));
}
}
/**
* Gets the number of currently visible columns in the import data table.
*
* @returns {number}
* @private
*/
_getNumberOfVisibleColumns() {
return $importDataTable.find('th:visible').length;
}
/**
* Gets the total number of columns in the import data table.
*
* @returns {number}
* @private
*/
_getTotalNumberOfColumns() {
return $importDataTable.find('th').length;
}
/**
* Hide the elements.
*
* @param $elements
* @private
*/
_hide($elements) {
$elements.addClass('d-none');
}
/**
* Show the elements.
*
* @param $elements
* @private
*/
_show($elements) {
$elements.removeClass('d-none');
}
/**
* Shows a column from import data table by given index
*
* @param columnIndex
* @private
*/
_showTableColumnByIndex(columnIndex) {
// Increasing the index because nth-child calculates from 1 and index starts from 0
columnIndex++;
this._show($importDataTable.find('th:nth-child(' + columnIndex + ')'));
this._show($importDataTable.find('tbody > tr').find('td:nth-child(' + columnIndex + ')'));
}
}

View File

@@ -0,0 +1,178 @@
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
const $ = window.$;
/**
* Class is responsible for import match configuration
* in Advanced parameters -> Import -> step 2 form.
*/
export default class ImportMatchConfiguration
{
/**
* Initializes all the processes related with import matches.
*/
constructor() {
this.loadEvents();
}
/**
* Loads all events for data match configuration.
*/
loadEvents() {
$(document).on('click', '.js-save-import-match', (event) => this.save(event));
$(document).on('click', '.js-load-import-match', (event) => this.load(event));
$(document).on('click', '.js-delete-import-match', (event) => this.delete(event));
}
/**
* Save the import match configuration.
*/
save(event) {
event.preventDefault();
const ajaxUrl = $('.js-save-import-match').attr('data-url');
const formData = $('.import-data-configuration-form').serialize();
$.ajax({
type: 'POST',
url: ajaxUrl,
data: formData,
}).then(response => {
if (typeof response.errors !== 'undefined' && response.errors.length) {
this._showErrorPopUp(response.errors);
} else if (response.matches.length > 0){
let $dataMatchesDropdown = this.matchesDropdown;
for (let key in response.matches) {
let $existingMatch = $dataMatchesDropdown.find(`option[value=${response.matches[key].id_import_match}]`);
// If match already exists with same id - do nothing
if ($existingMatch.length > 0) {
continue;
}
// Append the new option to the matches dropdown
this._appendOptionToDropdown(
$dataMatchesDropdown,
response.matches[key].name,
response.matches[key].id_import_match
);
}
}
});
}
/**
* Load the import match.
*/
load(event) {
event.preventDefault();
const ajaxUrl = $('.js-load-import-match').attr('data-url');
$.ajax({
type: 'GET',
url: ajaxUrl,
data: {
import_match_id: this.matchesDropdown.val()
},
}).then(response => {
if (response) {
this.rowsSkipInput.val(response.skip);
let entityFields = response.match.split('|');
for (let i in entityFields) {
$(`#type_value_${i}`).val(entityFields[i]);
}
}
});
}
/**
* Delete the import match.
*/
delete(event) {
event.preventDefault();
const ajaxUrl = $('.js-delete-import-match').attr('data-url');
const $dataMatchesDropdown = this.matchesDropdown;
const selectedMatchId = $dataMatchesDropdown.val();
$.ajax({
type: 'DELETE',
url: ajaxUrl,
data: {
import_match_id: selectedMatchId
},
}).then(() => {
// Delete the match option from matches dropdown
$dataMatchesDropdown.find(`option[value=${selectedMatchId}]`).remove();
});
}
/**
* Appends a new option to given dropdown.
*
* @param {jQuery} $dropdown
* @param {String} optionText
* @param {String} optionValue
* @private
*/
_appendOptionToDropdown($dropdown, optionText, optionValue) {
const $newOption = $('<option>');
$newOption.attr('value', optionValue);
$newOption.text(optionText);
$dropdown.append($newOption);
}
/**
* Shows error messages in the native error pop-up.
*
* @param {Array} errors
* @private
*/
_showErrorPopUp(errors) {
alert(errors);
}
/**
* Get the matches dropdown.
*
* @returns {*|HTMLElement}
*/
get matchesDropdown() {
return $('#matches');
}
/**
* Get the "rows to skip" input.
*
* @returns {*|HTMLElement}
*/
get rowsSkipInput() {
return $('#skip');
}
}

View File

@@ -0,0 +1,319 @@
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
export default class ImportProgressModal {
/**
* Show the import progress modal window.
*/
show() {
this.progressModal.modal('show');
}
/**
* Hide the import progress modal window.
*/
hide() {
this.progressModal.modal('hide');
}
/**
* Updates the import progressbar.
*
* @param {number} completed number of completed items.
* @param {number} total number of items in total.
*/
updateProgress(completed, total) {
completed = parseInt(completed);
total = parseInt(total);
let $progressBar = this.progressBar,
percentage = completed / total * 100;
$progressBar.css('width', `${percentage}%`);
$progressBar.find('> span').text(`${completed}/${total}`);
}
/**
* Updates the progress bar label.
*
* @param {String} label if not provided - will use the default label
*/
updateProgressLabel(label) {
this.progressLabel.text(label);
}
/**
* Sets the progress label to "importing"
*/
setImportingProgressLabel() {
this.updateProgressLabel(this.progressModal.find('.modal-body').data('importing-label'));
}
/**
* Sets the progress label to "imported"
*/
setImportedProgressLabel() {
this.updateProgressLabel(this.progressModal.find('.modal-body').data('imported-label'));
}
/**
* Shows information messages in the import modal.
*
* @param {Array} messages
*/
showInfoMessages(messages) {
this._showMessages(this.infoMessageBlock, messages);
}
/**
* Shows warning messages in the import modal.
*
* @param {Array} messages
*/
showWarningMessages(messages) {
this._showMessages(this.warningMessageBlock, messages);
}
/**
* Shows error messages in the import modal.
*
* @param {Array} messages
*/
showErrorMessages(messages) {
this._showMessages(this.errorMessageBlock, messages);
}
/**
* Shows the import success message.
*/
showSuccessMessage() {
this.successMessageBlock.removeClass('d-none');
}
/**
* Shows the post size limit warning message.
*
* @param {number} postSizeValue to be shown in the warning
*/
showPostLimitMessage(postSizeValue) {
this.postLimitMessage.find('#post_limit_value').text(postSizeValue);
this.postLimitMessage.removeClass('d-none');
}
/**
* Show messages in given message block.
*
* @param {jQuery} $messageBlock
* @param {Array} messages
* @private
*/
_showMessages($messageBlock, messages) {
let showMessagesBlock = false;
for (let key in messages) {
// Indicate that the messages block should be displayed
showMessagesBlock = true;
let message = $('<div>');
message.text(messages[key]);
message.addClass('message');
$messageBlock.append(message);
}
if (showMessagesBlock) {
$messageBlock.removeClass('d-none');
}
}
/**
* Show the "Ignore warnings and continue" button.
*/
showContinueImportButton() {
this.continueImportButton.removeClass('d-none')
}
/**
* Hide the "Ignore warnings and continue" button.
*/
hideContinueImportButton() {
this.continueImportButton.addClass('d-none');
}
/**
* Show the "Abort import" button.
*/
showAbortImportButton() {
this.abortImportButton.removeClass('d-none');
}
/**
* Hide the "Abort import" button.
*/
hideAbortImportButton() {
this.abortImportButton.addClass('d-none');
}
/**
* Show the "Close" button of the modal.
*/
showCloseModalButton() {
this.closeModalButton.removeClass('d-none');
}
/**
* Hide the "Close" button.
*/
hideCloseModalButton() {
this.closeModalButton.addClass('d-none');
}
/**
* Clears all warning messages from the modal.
*/
clearWarningMessages() {
this.warningMessageBlock.addClass('d-none').find('.message').remove();
}
/**
* Reset the modal - resets progress bar and removes messages.
*/
reset() {
// Reset the progress bar
this.updateProgress(0, 0);
this.updateProgressLabel(this.progressLabel.attr('default-value'));
// Hide action buttons
this.continueImportButton.addClass('d-none');
this.abortImportButton.addClass('d-none');
this.closeModalButton.addClass('d-none');
// Remove messages
this.successMessageBlock.addClass('d-none');
this.infoMessageBlock.addClass('d-none').find('.message').remove();
this.errorMessageBlock.addClass('d-none').find('.message').remove();
this.postLimitMessage.addClass('d-none');
this.clearWarningMessages();
}
/**
* Gets import progress modal.
*
* @returns {jQuery}
*/
get progressModal() {
return $('#import_progress_modal');
}
/**
* Gets import progress bar.
*
* @returns {jQuery}
*/
get progressBar() {
return this.progressModal.find('.progress-bar');
}
/**
* Gets information messages block.
*
* @returns {jQuery|HTMLElement}
*/
get infoMessageBlock() {
return $('.js-import-info');
}
/**
* Gets error messages block.
*
* @returns {jQuery|HTMLElement}
*/
get errorMessageBlock() {
return $('.js-import-errors');
}
/**
* Gets warning messages block.
*
* @returns {jQuery|HTMLElement}
*/
get warningMessageBlock() {
return $('.js-import-warnings');
}
/**
* Gets success messages block.
*
* @returns {jQuery|HTMLElement}
*/
get successMessageBlock() {
return $('.js-import-success');
}
/**
* Gets post limit message.
*
* @returns {jQuery|HTMLElement}
*/
get postLimitMessage() {
return $('.js-post-limit-warning');
}
/**
* Gets "Ignore warnings and continue" button.
*
* @returns {jQuery|HTMLElement}
*/
get continueImportButton() {
return $('.js-continue-import');
}
/**
* Gets "Abort import" button.
*
* @returns {jQuery|HTMLElement}
*/
get abortImportButton() {
return $('.js-abort-import');
}
/**
* Gets "Close" button of the modal.
*
* @returns {jQuery|HTMLElement}
*/
get closeModalButton() {
return $('.js-close-modal');
}
/**
* Gets progress bar label.
*
* @returns {jQuery|HTMLElement}
*/
get progressLabel() {
return $('#import_progress_bar').find('.progress-details-text');
}
}

View File

@@ -0,0 +1,302 @@
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
import ImportProgressModal from './ImportProgressModal';
import ImportBatchSizeCalculator from './ImportBatchSizeCalculator';
import PostSizeChecker from './PostSizeChecker';
export default class Importer {
constructor() {
this.configuration = {};
this.progressModal = new ImportProgressModal;
this.batchSizeCalculator = new ImportBatchSizeCalculator;
this.postSizeChecker = new PostSizeChecker();
// Default number of rows in one batch of the import.
this.defaultBatchSize = 5;
}
/**
* Process the import.
*
* @param {String} importUrl url of the controller, processing the import.
* @param {Object} configuration import configuration.
*/
import(importUrl, configuration) {
this._mergeConfiguration(configuration);
this.importUrl = importUrl;
// Total number of rows to be imported.
this.totalRowsCount = 0;
// Flags that mark that there were warnings or errors during import.
this.hasWarnings = false;
this.hasErrors = false;
// Resetting the import progress modal and showing it.
this.progressModal.reset();
this.progressModal.show();
// Starting the import with default batch size, which is adjusted for next iterations.
this._ajaxImport(0, this.defaultBatchSize);
}
/**
* Process the ajax import request.
*
* @param {number} offset row number, from which the import job will start processing data.
* @param {number} batchSize batch size of this import job.
* @param {boolean} validateOnly whether the data should be only validated, if false - the data will be imported.
* @param {Object} recurringVariables variables which are recurring between import batch jobs.
* @private
*/
_ajaxImport(offset, batchSize, validateOnly = true, recurringVariables = {}) {
this._mergeConfiguration({
offset: offset,
limit: batchSize,
validateOnly: validateOnly ? 1 : 0,
crossStepsVars: JSON.stringify(recurringVariables)
});
this._onImportStart();
$.post({
url: this.importUrl,
dataType: 'json',
data: this.configuration,
success: (response) => {
if (this._importCancelRequested) {
this._cancelImport();
return false;
}
let hasErrors = response.errors && response.errors.length,
hasWarnings = response.warnings && response.warnings.length,
hasNotices = response.notices && response.notices.length;
if (response.totalCount !== undefined && response.totalCount) {
// The total rows count is retrieved only in the first batch response.
this.totalRowsCount = response.totalCount;
}
// Update import progress.
this.progressModal.updateProgress(response.doneCount, this.totalRowsCount);
if (!validateOnly) {
// Set the progress label to "Importing".
this.progressModal.setImportingProgressLabel();
}
// Information messages are not shown during validation.
if (!validateOnly && hasNotices) {
this.progressModal.showInfoMessages(response.notices);
}
if (hasErrors) {
this.hasErrors = true;
this.progressModal.showErrorMessages(response.errors);
// If there are errors and it's not validation step - stop the import.
// If it's validation step - we will show all errors once it finishes.
if (!validateOnly) {
this._onImportStop();
return false;
}
} else if (hasWarnings) {
this.hasWarnings = true;
this.progressModal.showWarningMessages(response.warnings);
}
if (!response.isFinished) {
// Marking the end of import operation.
this.batchSizeCalculator.markImportEnd();
// Calculate next import batch size and offset.
let nextOffset = offset + batchSize;
let nextBatchSize = this.batchSizeCalculator.calculateBatchSize(batchSize, this.totalRowsCount);
// Showing a warning if post size limit is about to be reached.
if (this.postSizeChecker.isReachingPostSizeLimit(response.postSizeLimit, response.nextPostSize)) {
this.progressModal.showPostLimitMessage(
this.postSizeChecker.getRequiredPostSizeInMegabytes(response.nextPostSize)
);
}
// Run the import again for the next batch.
return this._ajaxImport(
nextOffset,
nextBatchSize,
validateOnly,
response.crossStepsVariables
);
}
// All import batches are finished successfully.
// If it was only validating the import data until this point,
// we have to run the data import now.
if (validateOnly) {
// If errors occurred during validation - stop the import.
if (this.hasErrors) {
this._onImportStop();
return false;
}
if (this.hasWarnings) {
// Show the button to ignore warnings.
this.progressModal.showContinueImportButton();
this._onImportStop();
return false;
}
// Update the progress bar to 100%.
this.progressModal.updateProgress(this.totalRowsCount, this.totalRowsCount);
// Continue with the data import.
return this._ajaxImport(0, this.defaultBatchSize, false);
}
// Import is completely finished.
this._onImportFinish();
},
error: (XMLHttpRequest, textStatus, errorCode) => {
if (textStatus === 'parsererror') {
textStatus = 'Technical error: Unexpected response returned by server. Import stopped.';
}
this._onImportStop();
this.progressModal.showErrorMessages([textStatus]);
}
});
}
/**
* Continue the import when it was stopped.
*/
continueImport() {
if (!this.configuration) {
throw 'Missing import configuration. Make sure the import had started before continuing.';
}
this.progressModal.hideContinueImportButton();
this.progressModal.hideCloseModalButton();
this.progressModal.clearWarningMessages();
this._ajaxImport(0, this.defaultBatchSize, false);
}
/**
* Set the import configuration.
*
* @param importConfiguration
*/
set configuration(importConfiguration) {
this._importConfiguration = importConfiguration;
}
/**
* Get the import configuration.
*
* @returns {*}
*/
get configuration() {
return this._importConfiguration;
}
/**
* Set progress modal.
*
* @param {ImportProgressModal} modal
*/
set progressModal(modal) {
this._modal = modal;
}
/**
* Get progress modal.
*
* @returns {ImportProgressModal}
*/
get progressModal() {
return this._modal;
}
/**
* Request import cancellation.
* Import operation will be cancelled at next iteration when requested.
*/
requestCancelImport() {
this._importCancelRequested = true;
}
/**
* Merge given configuration into current import configuration.
*
* @param {Object} configuration
* @private
*/
_mergeConfiguration(configuration) {
for (let key in configuration) {
this._importConfiguration[key] = configuration[key];
}
}
/**
* Cancel the import process.
* @private
*/
_cancelImport() {
this.progressModal.hide();
this._importCancelRequested = false;
}
/**
* Additional actions when import is stopped.
* @private
*/
_onImportStop() {
this.progressModal.showCloseModalButton();
this.progressModal.hideAbortImportButton();
}
/**
* Additional actions when import is finished.
* @private
*/
_onImportFinish() {
this._onImportStop();
this.progressModal.showSuccessMessage();
this.progressModal.setImportedProgressLabel();
this.progressModal.updateProgress(this.totalRowsCount, this.totalRowsCount);
}
/**
* Additional actions when import is starting.
* @private
*/
_onImportStart() {
// Marking the start of import operation.
this.batchSizeCalculator.markImportStart();
this.progressModal.showAbortImportButton();
}
}

View File

@@ -0,0 +1,54 @@
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
export default class PostSizeChecker {
constructor() {
// How close can we get to the post size limit. 0.9 means 90%.
this.postSizeLimitThreshold = 0.9;
}
/**
* Check if given postSizeLimit is reaching the required post size
*
* @param {number} postSizeLimit
* @param {number} requiredPostSize
*
* @returns {boolean}
*/
isReachingPostSizeLimit(postSizeLimit, requiredPostSize) {
return requiredPostSize >= postSizeLimit * this.postSizeLimitThreshold;
}
/**
* Get required post size in megabytes.
*
* @param {number} requiredPostSize
*
* @returns {number}
*/
getRequiredPostSizeInMegabytes(requiredPostSize) {
return parseInt(requiredPostSize / (1024 * 1024));
}
}

View File

@@ -0,0 +1,32 @@
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
import ImportDataPage from './ImportDataPage';
const $ = window.$;
$(() => {
new ImportDataPage();
});