JEMBOT MAWOT Bypass Shell
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* 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.md.
* 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://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
*/
import ConfirmModal from '@components/modal';
const {$} = window;
/**
* Module Admin Page Controller.
* @constructor
*/
class AdminModuleController {
/**
* Initialize all listeners and bind everything
* @method init
* @memberof AdminModule
*/
constructor(moduleCardController) {
this.eventEmitter = window.prestashop.component.EventEmitter;
this.moduleCardController = moduleCardController;
this.DEFAULT_MAX_RECENTLY_USED = 10;
this.DEFAULT_MAX_PER_CATEGORIES = 6;
this.DISPLAY_GRID = 'grid';
this.DISPLAY_LIST = 'list';
this.CATEGORY_RECENTLY_USED = 'recently-used';
this.currentCategoryDisplay = {};
this.currentDisplay = '';
this.isCategoryGridDisplayed = false;
this.currentTagsList = [];
this.currentRefCategory = null;
this.currentRefStatus = null;
this.currentSorting = null;
this.pstaggerInput = null;
this.lastBulkAction = null;
this.isUploadStarted = false;
this.findModuleUsed = false;
this.recentlyUsedSelector = '#module-recently-used-list .modules-list';
/**
* Loaded modules list.
* Containing the card and list display.
* @type {Array}
*/
this.modulesList = [];
this.moduleShortList = '.module-short-list';
// See more & See less selector
this.seeMoreSelector = '.see-more';
this.seeLessSelector = '.see-less';
// Selectors into vars to make it easier to change them while keeping same code logic
this.moduleItemGridSelector = '.module-item-grid';
this.moduleItemListSelector = '.module-item-list';
this.categorySelectorLabelSelector = '.module-category-selector-label';
this.categorySelector = '.module-category-selector';
this.categoryItemSelector = '.module-category-menu';
this.categoryResetBtnSelector = '.module-category-reset';
this.moduleInstallBtnSelector = 'input.module-install-btn';
this.moduleSortingDropdownSelector = '.module-sorting-author select';
this.categoryGridSelector = '#modules-categories-grid';
this.categoryGridItemSelector = '.module-category-item';
// Upgrade All selectors
this.upgradeAllSource = '.module_action_menu_upgrade_all';
this.upgradeContainer = '#modules-list-container-update';
this.upgradeAllTargets = `${this.upgradeContainer} .module_action_menu_upgrade:visible`;
// Notification selectors
this.notificationContainer = '#modules-list-container-notification';
// Bulk action selectors
this.bulkActionDropDownSelector = '.module-bulk-actions';
this.bulkItemSelector = '.module-bulk-menu';
this.bulkActionCheckboxListSelector = '.module-checkbox-bulk-list input';
this.bulkActionCheckboxGridSelector = '.module-checkbox-bulk-grid input';
this.checkedBulkActionListSelector = `${this.bulkActionCheckboxListSelector}:checked`;
this.checkedBulkActionGridSelector = `${this.bulkActionCheckboxGridSelector}:checked`;
this.bulkActionCheckboxSelector = '#module-modal-bulk-checkbox';
this.bulkConfirmModalSelector = '#module-modal-bulk-confirm';
this.bulkConfirmModalActionNameSelector = '#module-modal-bulk-confirm-action-name';
this.bulkConfirmModalListSelector = '#module-modal-bulk-confirm-list';
this.bulkConfirmModalAckBtnSelector = '#module-modal-confirm-bulk-ack';
// Placeholders
this.placeholderGlobalSelector = '.module-placeholders-wrapper';
this.placeholderFailureGlobalSelector = '.module-placeholders-failure';
this.placeholderFailureMsgSelector = '.module-placeholders-failure-msg';
this.placeholderFailureRetryBtnSelector = '#module-placeholders-failure-retry';
// Module's statuses selectors
this.statusSelectorLabelSelector = '.module-status-selector-label';
this.statusItemSelector = '.module-status-menu';
this.statusResetBtnSelector = '.module-status-reset';
// Selectors for Module Import
this.importModalBtnSelector = '#page-header-desc-configuration-add_module';
this.dropZoneModalSelector = '#module-modal-import';
this.dropZoneModalFooterSelector = '#module-modal-import .modal-footer';
this.dropZoneImportZoneSelector = '#importDropzone';
this.moduleImportModalCloseBtn = '#module-modal-import-closing-cross';
this.moduleImportStartSelector = '.module-import-start';
this.moduleImportProcessingSelector = '.module-import-processing';
this.moduleImportSuccessSelector = '.module-import-success';
this.moduleImportSuccessConfigureBtnSelector = '.module-import-success-configure';
this.moduleImportFailureSelector = '.module-import-failure';
this.moduleImportFailureRetrySelector = '.module-import-failure-retry';
this.moduleImportFailureDetailsBtnSelector = '.module-import-failure-details-action';
this.moduleImportSelectFileManualSelector = '.module-import-start-select-manual';
this.moduleImportFailureMsgDetailsSelector = '.module-import-failure-details';
this.moduleImportConfirmSelector = '.module-import-confirm';
this.initSortingDropdown();
this.initBOEventRegistering();
this.initCurrentDisplay();
this.initSortingDisplaySwitch();
this.initBulkDropdown();
this.initSearchBlock();
this.initCategorySelect();
this.initCategoriesGrid();
this.initActionButtons();
this.initAddModuleAction();
this.initDropzone();
this.initPageChangeProtection();
this.initPlaceholderMechanism();
this.initFilterStatusDropdown();
this.fetchModulesList();
this.getNotificationsCount();
this.initializeSeeMore();
}
initFilterStatusDropdown() {
const self = this;
const body = $('body');
body.on('click', self.statusItemSelector, function () {
// Get data from li DOM input
self.currentRefStatus = parseInt($(this).data('status-ref'), 10);
// Change dropdown label to set it to the current status' displayname
$(self.statusSelectorLabelSelector).text($(this).text());
$(self.statusResetBtnSelector).show();
self.updateModuleVisibility();
});
body.on('click', self.statusResetBtnSelector, function () {
$(self.statusSelectorLabelSelector).text($(this).text());
$(this).hide();
self.currentRefStatus = null;
self.updateModuleVisibility();
});
}
initBulkDropdown() {
const self = this;
const body = $('body');
body.on('click', self.getBulkCheckboxesSelector(), () => {
const selector = $(self.bulkActionDropDownSelector);
if ($(self.getBulkCheckboxesCheckedSelector()).length > 0) {
selector.closest('.module-top-menu-item').removeClass('disabled');
} else {
selector.closest('.module-top-menu-item').addClass('disabled');
}
});
body.on('click', self.bulkItemSelector, function initializeBodyChange() {
if ($(self.getBulkCheckboxesCheckedSelector()).length === 0) {
$.growl.warning({
message: window.translate_javascripts['Bulk Action - One module minimum'],
});
return;
}
self.lastBulkAction = $(this).data('ref');
const modulesListString = self.buildBulkActionModuleList();
const actionString = $(this)
.find(':checked')
.text()
.toLowerCase();
$(self.bulkConfirmModalListSelector).html(modulesListString);
$(self.bulkConfirmModalActionNameSelector).text(actionString);
if (self.lastBulkAction === 'bulk-uninstall') {
$(self.bulkActionCheckboxSelector).show();
} else {
$(self.bulkActionCheckboxSelector).hide();
}
$(self.bulkConfirmModalSelector).modal('show');
});
body.on('click', this.bulkConfirmModalAckBtnSelector, (event) => {
event.preventDefault();
event.stopPropagation();
$(self.bulkConfirmModalSelector).modal('hide');
self.doBulkAction(self.lastBulkAction);
});
}
initBOEventRegistering() {
this.eventEmitter.on('Module Enabled', (context) => this.onModuleDisabled(context));
this.eventEmitter.on('Module Disabled', (context) => this.onModuleDisabled(context));
this.eventEmitter.on('Module Uninstalled', (context) => this.installHandler(context));
this.eventEmitter.on('Module Delete', (context) => this.onModuleDelete(context));
this.eventEmitter.on('Module Installed', (context) => this.installHandler(context));
}
installHandler(event) {
this.updateModuleStatus(event);
this.updateModuleVisibility();
}
/**
* Updates the modulesList object
*
* @param event a DOM element that contains module data such as id, name, version...
*/
updateModuleStatus(event) {
this.modulesList = this.modulesList.map((module) => {
const moduleElement = $(event);
if ((moduleElement.data('tech-name') === module.techName)
&& (moduleElement.data('version') !== undefined)) {
const newModule = {
domObject: moduleElement,
id: moduleElement.data('id'),
name: moduleElement.data('name').toLowerCase(),
scoring: parseFloat(moduleElement.data('scoring')),
logo: moduleElement.data('logo'),
author: moduleElement.data('author').toLowerCase(),
version: moduleElement.data('version'),
description: moduleElement.data('description').toLowerCase(),
techName: moduleElement.data('tech-name').toLowerCase(),
childCategories: moduleElement.data('child-categories'),
categories: String(moduleElement.data('categories')).toLowerCase(),
type: moduleElement.data('type'),
price: parseFloat(moduleElement.data('price')),
active: parseInt(moduleElement.data('active'), 10),
installed: moduleElement.data('installed') === 1,
access: moduleElement.data('last-access'),
display: moduleElement.hasClass('module-item-list') ? this.DISPLAY_LIST : this.DISPLAY_GRID,
container: module.container,
};
return newModule;
}
return module;
});
}
onModuleDisabled(event) {
const self = this;
self.updateModuleStatus(event);
self.getModuleItemSelector();
$('.modules-list').each(() => {
self.updateModuleVisibility();
});
}
onModuleDelete(event) {
this.modulesList = this.modulesList.filter((value) => value.techName !== $(event).data('tech-name'));
this.installHandler(event);
}
initPlaceholderMechanism() {
const self = this;
if ($(self.placeholderGlobalSelector).length) {
self.ajaxLoadPage();
}
// Retry loading mechanism
$('body').on('click', self.placeholderFailureRetryBtnSelector, () => {
$(self.placeholderFailureGlobalSelector).fadeOut();
$(self.placeholderGlobalSelector).fadeIn();
self.ajaxLoadPage();
});
}
ajaxLoadPage() {
const self = this;
$.ajax({
method: 'GET',
url: window.moduleURLs.catalogRefresh,
})
.done((response) => {
if (response.status === true) {
if (typeof response.domElements === 'undefined') response.domElements = null;
if (typeof response.msg === 'undefined') response.msg = null;
const stylesheet = document.styleSheets[0];
const stylesheetRule = '{display: none}';
const moduleGlobalSelector = '.modules-list';
const moduleSortingSelector = '.module-sorting-menu';
const requiredSelectorCombination = `${moduleGlobalSelector},${moduleSortingSelector}`;
if (stylesheet.insertRule) {
stylesheet.insertRule(requiredSelectorCombination + stylesheetRule, stylesheet.cssRules.length);
} else if (stylesheet.addRule) {
stylesheet.addRule(requiredSelectorCombination, stylesheetRule, -1);
}
$(self.placeholderGlobalSelector).fadeOut(800, () => {
$.each(response.domElements, (index, element) => {
$(element.selector).append(element.content);
});
$(moduleGlobalSelector)
.fadeIn(800)
.css('display', 'flex');
$(moduleSortingSelector).fadeIn(800);
$('[data-toggle="popover"]').popover();
self.initCurrentDisplay();
self.fetchModulesList();
});
} else {
$(self.placeholderGlobalSelector).fadeOut(800, () => {
$(self.placeholderFailureMsgSelector).text(response.msg);
$(self.placeholderFailureGlobalSelector).fadeIn(800);
});
}
})
.fail((response) => {
$(self.placeholderGlobalSelector).fadeOut(800, () => {
$(self.placeholderFailureMsgSelector).text(response.statusText);
$(self.placeholderFailureGlobalSelector).fadeIn(800);
});
});
}
fetchModulesList() {
const self = this;
let container;
let $this;
self.modulesList = [];
$('.modules-list').each(function prepareContainer() {
container = $(this);
container.find('.module-item').each(function prepareModules() {
$this = $(this);
self.modulesList.push({
domObject: $this,
id: $this.data('id'),
name: $this.data('name').toLowerCase(),
scoring: parseFloat($this.data('scoring')),
logo: $this.data('logo'),
author: $this.data('author').toLowerCase(),
version: $this.data('version'),
description: $this.data('description').toLowerCase(),
techName: $this.data('tech-name').toLowerCase(),
childCategories: $this.data('child-categories'),
categories: String($this.data('categories')).toLowerCase(),
type: $this.data('type'),
price: parseFloat($this.data('price')),
active: parseInt($this.data('active'), 10),
installed: $this.data('installed') === 1,
access: $this.data('last-access'),
display: $this.hasClass('module-item-list') ? self.DISPLAY_LIST : self.DISPLAY_GRID,
container,
});
if (self.isModulesPage()) {
$this.remove();
}
});
});
self.updateModuleVisibility();
$('body').trigger('moduleCatalogLoaded');
}
/**
* Prepare sorting
*
*/
updateModuleSorting() {
const self = this;
if (!self.currentSorting) {
return;
}
// Modules sorting
let order = 'asc';
let key = self.currentSorting;
const splittedKey = key.split('-');
if (splittedKey.length > 1) {
key = splittedKey[0];
if (splittedKey[1] === 'desc') {
order = 'desc';
}
}
const currentCompare = (a, b) => {
let aData = a[key];
let bData = b[key];
if (key === 'access') {
aData = new Date(aData).getTime();
bData = new Date(bData).getTime();
aData = Number.isNaN(aData) ? 0 : aData;
bData = Number.isNaN(bData) ? 0 : bData;
if (aData === bData) {
return b.name.localeCompare(a.name);
}
}
if (aData < bData) return -1;
if (aData > bData) return 1;
return 0;
};
self.modulesList.sort(currentCompare);
if (order === 'desc') {
self.modulesList.reverse();
}
}
updateModuleContainerDisplay() {
const self = this;
$('.module-short-list').each(function setShortListVisibility() {
const container = $(this);
const nbModulesInContainer = container.find('.module-item').length;
if (
(self.currentRefCategory && self.currentRefCategory !== String(container.find('.modules-list').data('name')))
|| (self.currentRefStatus !== null && nbModulesInContainer === 0)
|| (nbModulesInContainer === 0
&& String(container.find('.modules-list').data('name')) === self.CATEGORY_RECENTLY_USED)
|| (self.currentTagsList.length > 0 && nbModulesInContainer === 0)
) {
container.hide();
return;
}
container.show();
container
.find(`${self.seeMoreSelector}, ${self.seeLessSelector}`)
.toggle(nbModulesInContainer >= self.DEFAULT_MAX_PER_CATEGORIES);
});
}
updateModuleVisibility() {
const self = this;
self.updateModuleSorting();
if (self.isModulesPage() && !self.isReadMoreModalOpened()) {
$(self.recentlyUsedSelector)
.find('.module-item')
.remove();
$('.modules-list')
.find('.module-item')
.remove();
}
// Modules visibility management
let isVisible;
let currentModule;
let moduleCategory;
let tagExists;
let newValue;
let defaultMax;
const paramsUrl = (new URL(document.location)).searchParams;
const findModule = paramsUrl.get('find');
if (findModule && self.findModuleUsed !== true) {
self.currentTagsList.push(findModule);
self.findModuleUsed = true;
} else if (findModule) {
self.currentTagsList.pop(findModule);
}
const modulesListLength = self.modulesList.length;
const counter = {};
const checkTag = (index, value) => {
newValue = value.toLowerCase();
tagExists
|= currentModule.name.indexOf(newValue) !== -1
|| currentModule.description.indexOf(newValue) !== -1
|| currentModule.author.indexOf(newValue) !== -1
|| currentModule.techName.indexOf(newValue) !== -1;
};
for (let i = 0; i < modulesListLength; i += 1) {
currentModule = self.modulesList[i];
if (currentModule.display === self.currentDisplay) {
isVisible = true;
moduleCategory = self.currentRefCategory === self.CATEGORY_RECENTLY_USED
? self.CATEGORY_RECENTLY_USED
: currentModule.categories;
// Check for same category
if (self.currentRefCategory !== null) {
isVisible &= moduleCategory === self.currentRefCategory;
}
// Check for same status
if (self.currentRefStatus !== null) {
isVisible &= (
(
currentModule.active === self.currentRefStatus
&& currentModule.installed === true
)
|| (
currentModule.installed === false
&& self.currentRefStatus === 2
) || (
currentModule.installed === true
&& self.currentRefStatus === 3
)
);
}
// Check for tag list
if (self.currentTagsList.length) {
tagExists = false;
$.each(self.currentTagsList, checkTag);
isVisible &= tagExists;
}
/**
* If list display without search we must display only the first 5 modules
*/
if (self.currentDisplay === self.DISPLAY_LIST && !self.currentTagsList.length) {
if (self.currentCategoryDisplay[moduleCategory] === undefined) {
self.currentCategoryDisplay[moduleCategory] = false;
}
if (!counter[moduleCategory]) {
counter[moduleCategory] = 0;
}
defaultMax = moduleCategory === self.CATEGORY_RECENTLY_USED
? self.DEFAULT_MAX_RECENTLY_USED
: self.DEFAULT_MAX_PER_CATEGORIES;
if (counter[moduleCategory] >= defaultMax && isVisible) {
isVisible &= self.currentCategoryDisplay[moduleCategory];
}
}
// If visible, display (Thx captain obvious)
if (isVisible) {
counter[moduleCategory] += 1;
if (self.currentRefCategory === self.CATEGORY_RECENTLY_USED) {
$(self.recentlyUsedSelector).append(currentModule.domObject);
} else {
currentModule.container.append(currentModule.domObject);
}
}
}
}
self.updateModuleContainerDisplay();
self.updateTotalResults();
}
initPageChangeProtection() {
const self = this;
$(window).on('beforeunload', () => {
if (self.isUploadStarted === true) {
return (
'It seems some critical operation are running, are you sure you want to change page? '
+ 'It might cause some unexepcted behaviors.'
);
}
return undefined;
});
}
buildBulkActionModuleList() {
const checkBoxesSelector = this.getBulkCheckboxesCheckedSelector();
const moduleItemSelector = this.getModuleItemSelector();
let alreadyDoneFlag = 0;
let htmlGenerated = '';
let currentElement;
$(checkBoxesSelector).each(function prepareCheckboxes() {
if (alreadyDoneFlag === 10) {
// Break each
htmlGenerated += '- ...';
return false;
}
currentElement = $(this).closest(moduleItemSelector);
htmlGenerated += `- ${currentElement.data('name')}<br/>`;
alreadyDoneFlag += 1;
return true;
});
return htmlGenerated;
}
initAddModuleAction() {
const self = this;
const addModuleButton = $(self.importModalBtnSelector);
addModuleButton.attr('data-toggle', 'modal');
addModuleButton.attr('data-target', self.dropZoneModalSelector);
}
initDropzone() {
const self = this;
const body = $('body');
const dropzone = $('.dropzone');
// Reset modal when click on Retry in case of failure
body.on('click', this.moduleImportFailureRetrySelector, () => {
/* eslint-disable max-len */
$(
`${self.moduleImportSuccessSelector},${self.moduleImportFailureSelector},${self.moduleImportProcessingSelector}`,
).fadeOut(() => {
/**
* Added timeout for a better render of animation
* and avoid to have displayed at the same time
*/
setTimeout(() => {
$(self.moduleImportStartSelector).fadeIn(() => {
$(self.moduleImportFailureMsgDetailsSelector).hide();
$(self.moduleImportSuccessConfigureBtnSelector).hide();
dropzone.removeAttr('style');
});
}, 550);
});
/* eslint-enable max-len */
});
// Reinit modal on exit, but check if not already processing something
body.on('hidden.bs.modal', this.dropZoneModalSelector, () => {
$(`${self.moduleImportSuccessSelector}, ${self.moduleImportFailureSelector}`).hide();
$(self.moduleImportStartSelector).show();
dropzone.removeAttr('style');
$(self.moduleImportFailureMsgDetailsSelector).hide();
$(self.moduleImportSuccessConfigureBtnSelector).hide();
$(self.dropZoneModalFooterSelector).html('');
$(self.moduleImportConfirmSelector).hide();
});
// Change the way Dropzone.js lib handle file input trigger
body.on(
'click',
`.dropzone:not(${this.moduleImportSelectFileManualSelector}, ${this.moduleImportSuccessConfigureBtnSelector})`,
(event, manualSelect) => {
// if click comes from .module-import-start-select-manual, stop everything
if (typeof manualSelect === 'undefined') {
event.stopPropagation();
event.preventDefault();
}
},
);
body.on('click', this.moduleImportSelectFileManualSelector, (event) => {
event.stopPropagation();
event.preventDefault();
/**
* Trigger click on hidden file input, and pass extra data
* to .dropzone click handler fro it to notice it comes from here
*/
$('.dz-hidden-input').trigger('click', ['manual_select']);
});
// Handle modal closure
body.on('click', this.moduleImportModalCloseBtn, () => {
if (self.isUploadStarted !== true) {
$(self.dropZoneModalSelector).modal('hide');
}
});
// Fix issue on click configure button
body.on('click', this.moduleImportSuccessConfigureBtnSelector, function initializeBodyClickOnModuleImport(event) {
event.stopPropagation();
event.preventDefault();
window.location = $(this).attr('href');
});
// Open failure message details box
body.on('click', this.moduleImportFailureDetailsBtnSelector, () => {
$(self.moduleImportFailureMsgDetailsSelector).slideDown();
});
// @see: dropzone.js
const dropzoneOptions = {
url: window.moduleURLs.moduleImport,
acceptedFiles: '.zip, .tar',
// The name that will be used to transfer the file
paramName: 'file_uploaded',
uploadMultiple: false,
addRemoveLinks: true,
dictDefaultMessage: '',
hiddenInputContainer: self.dropZoneImportZoneSelector,
/**
* Add unlimited timeout. Otherwise dropzone timeout is 30 seconds
* and if a module is long to install, it is not possible to install the module.
*/
timeout: 0,
addedfile: () => {
$(`${self.moduleImportSuccessSelector}, ${self.moduleImportFailureSelector}`).hide();
self.animateStartUpload();
},
processing: () => {
// Leave it empty since we don't require anything while processing upload
},
error: (file, message) => {
self.displayOnUploadError(message);
},
complete: (file) => {
if (file.status !== 'error') {
const responseObject = $.parseJSON(file.xhr.response);
if (typeof responseObject.is_configurable === 'undefined') responseObject.is_configurable = null;
if (typeof responseObject.module_name === 'undefined') responseObject.module_name = null;
self.displayOnUploadDone(responseObject);
const elem = $(`<div data-tech-name="${responseObject.module_name}"></div>`);
this.eventEmitter.emit((responseObject.upgraded ? 'Module Upgraded' : 'Module Installed'), elem);
}
// State that we have finish the process to unlock some actions
self.isUploadStarted = false;
},
};
dropzone.dropzone($.extend(dropzoneOptions));
}
animateStartUpload() {
const self = this;
const dropzone = $('.dropzone');
// State that we start module upload
self.isUploadStarted = true;
$(self.moduleImportStartSelector).hide(0);
dropzone.css('border', 'none');
$(self.moduleImportProcessingSelector).fadeIn();
}
animateEndUpload(callback) {
const self = this;
$(self.moduleImportProcessingSelector)
.finish()
.fadeOut(callback);
}
/**
* Method to call for upload modal, when the ajax call went well.
*
* @param object result containing the server response
*/
displayOnUploadDone(result) {
const self = this;
self.animateEndUpload(() => {
if (result.status === true) {
if (result.is_configurable === true) {
const configureLink = window.moduleURLs.configurationPage.replace(/:number:/, result.module_name);
$(self.moduleImportSuccessConfigureBtnSelector).attr('href', configureLink);
$(self.moduleImportSuccessConfigureBtnSelector).show();
}
$(self.moduleImportSuccessSelector).fadeIn();
} else {
$(self.moduleImportFailureMsgDetailsSelector).html(result.msg);
$(self.moduleImportFailureSelector).fadeIn();
}
});
}
/**
* Method to call for upload modal, when the ajax call went wrong or when the action requested could not
* succeed for some reason.
*
* @param string message explaining the error.
*/
displayOnUploadError(message) {
const self = this;
self.animateEndUpload(() => {
$(self.moduleImportFailureMsgDetailsSelector).html(message);
$(self.moduleImportFailureSelector).fadeIn();
});
}
getBulkCheckboxesSelector() {
return this.currentDisplay === this.DISPLAY_GRID
? this.bulkActionCheckboxGridSelector
: this.bulkActionCheckboxListSelector;
}
getBulkCheckboxesCheckedSelector() {
return this.currentDisplay === this.DISPLAY_GRID
? this.checkedBulkActionGridSelector
: this.checkedBulkActionListSelector;
}
getModuleItemSelector() {
return this.currentDisplay === this.DISPLAY_GRID ? this.moduleItemGridSelector : this.moduleItemListSelector;
}
/**
* Get the module notifications count and displays it as a badge on the notification tab
* @return void
*/
getNotificationsCount() {
const self = this;
$.getJSON(window.moduleURLs.notificationsCount, self.updateNotificationsCount).fail(() => {
console.error('Could not retrieve module notifications count.');
});
}
updateNotificationsCount(badge) {
const destinationTabs = {
to_configure: $('#subtab-AdminModulesNotifications'),
to_update: $('#subtab-AdminModulesUpdates'),
};
Object.keys(destinationTabs).forEach((destinationKey) => {
if (destinationTabs[destinationKey].length !== 0) {
destinationTabs[destinationKey].find('.notification-counter').text(badge[destinationKey]);
}
});
}
initCategoriesGrid() {
const self = this;
$('body').on('click', this.categoryGridItemSelector, function initilaizeGridBodyClick(event) {
event.stopPropagation();
event.preventDefault();
const refCategory = $(this).data('category-ref');
// In case we have some tags we need to reset it !
if (self.currentTagsList.length) {
self.pstaggerInput.resetTags(false);
self.currentTagsList = [];
}
const menuCategoryToTrigger = $(`${self.categoryItemSelector}[data-category-ref="${refCategory}"]`);
if (!menuCategoryToTrigger.length) {
console.warn(`No category with ref (${refCategory}) seems to exist!`);
return false;
}
// Hide current category grid
if (self.isCategoryGridDisplayed === true) {
$(self.categoryGridSelector).fadeOut();
self.isCategoryGridDisplayed = false;
}
// Trigger click on right category
$(`${self.categoryItemSelector}[data-category-ref="${refCategory}"]`).click();
return true;
});
}
initCurrentDisplay() {
this.currentDisplay = this.currentDisplay === '' ? this.DISPLAY_LIST : this.DISPLAY_GRID;
}
initSortingDropdown() {
const self = this;
self.currentSorting = $(this.moduleSortingDropdownSelector)
.find(':checked')
.attr('value');
if (!self.currentSorting) {
self.currentSorting = 'access-desc';
}
$('body').on('change', self.moduleSortingDropdownSelector, function initializeBodySortingChange() {
self.currentSorting = $(this)
.find(':checked')
.attr('value');
self.updateModuleVisibility();
});
}
doBulkAction(requestedBulkAction) {
// This object is used to check if requested bulkAction is available and give proper
// url segment to be called for it
const forceDeletion = $('#force_bulk_deletion').prop('checked');
const bulkActionToUrl = {
'bulk-install': 'install',
'bulk-uninstall': 'uninstall',
'bulk-disable': 'disable',
'bulk-enable': 'enable',
'bulk-disable-mobile': 'disableMobile',
'bulk-enable-mobile': 'enableMobile',
'bulk-reset': 'reset',
'bulk-delete': 'delete',
};
// Note no grid selector used yet since we do not needed it at dev time
// Maybe useful to implement this kind of things later if intended to
// use this functionality elsewhere but "manage my module" section
if (typeof bulkActionToUrl[requestedBulkAction] === 'undefined') {
$.growl.error({
message: window.translate_javascripts['Bulk Action - Request not found'].replace('[1]', requestedBulkAction),
});
return false;
}
// Loop over all checked bulk checkboxes
const bulkActionSelectedSelector = this.getBulkCheckboxesCheckedSelector();
const bulkModuleAction = bulkActionToUrl[requestedBulkAction];
if ($(bulkActionSelectedSelector).length <= 0) {
console.warn(window.translate_javascripts['Bulk Action - One module minimum']);
return false;
}
const modulesActions = [];
let moduleTechName;
$(bulkActionSelectedSelector).each(function bulkActionSelector() {
moduleTechName = $(this).data('tech-name');
modulesActions.push({
techName: moduleTechName,
actionMenuObj: $(this)
.closest('.module-checkbox-bulk-list')
.next(),
});
});
this.performModulesAction(modulesActions, bulkModuleAction, forceDeletion);
return true;
}
performModulesAction(modulesActions, bulkModuleAction, forceDeletion) {
const self = this;
if (typeof self.moduleCardController === 'undefined') {
return;
}
// First let's filter modules that can't perform this action
const actionMenuLinks = filterAllowedActions(modulesActions);
if (!actionMenuLinks.length) {
return;
}
// Begin actions one after another
unstackModulesActions();
function requestModuleAction(actionMenuLink) {
if (self.moduleCardController.hasPendingRequest()) {
actionMenuLinks.push(actionMenuLink);
return;
}
self.moduleCardController.requestToController(
bulkModuleAction,
actionMenuLink,
forceDeletion,
unstackModulesActions,
);
}
function unstackModulesActions() {
if (actionMenuLinks.length <= 0) {
return;
}
const actionMenuLink = actionMenuLinks.shift();
requestModuleAction(actionMenuLink);
}
function filterAllowedActions(actions) {
const menuLinks = [];
let actionMenuLink;
$.each(actions, (index, moduleData) => {
actionMenuLink = $(
self.moduleCardController.moduleActionMenuLinkSelector + bulkModuleAction,
moduleData.actionMenuObj,
);
if (actionMenuLink.length > 0) {
menuLinks.push(actionMenuLink);
} else {
$.growl.error({
message: window.translate_javascripts['Bulk Action - Request not available for module']
.replace('[1]', bulkModuleAction)
.replace('[2]', moduleData.techName),
});
}
});
return menuLinks;
}
}
initActionButtons() {
const self = this;
$('body').on('click', self.moduleInstallBtnSelector, function initializeActionButtonsClick(event) {
const $this = $(this);
const $next = $($this.next());
event.preventDefault();
$this.hide();
$next.show();
$.ajax({
url: $this.data('url'),
dataType: 'json',
}).done(() => {
$next.fadeOut();
});
});
// "Upgrade All" button handler
$('body').on('click', self.upgradeAllSource, (event) => {
event.preventDefault();
const isMaintenanceMode = window.isShopMaintenance;
// Modal body element
const maintenanceLink = document.createElement('a');
maintenanceLink.classList.add('btn', 'btn-primary', 'btn-lg');
maintenanceLink.setAttribute('href', window.moduleURLs.maintenancePage);
maintenanceLink.innerHTML = window.moduleTranslations.moduleModalUpdateMaintenance;
const updateAllConfirmModal = new ConfirmModal(
{
id: 'confirm-module-update-modal',
confirmTitle: window.moduleTranslations.singleModuleModalUpdateTitle,
closeButtonLabel: window.moduleTranslations.moduleModalUpdateCancel,
confirmButtonLabel: isMaintenanceMode
? window.moduleTranslations.moduleModalUpdateUpgrade
: window.moduleTranslations.upgradeAnywayButtonText,
confirmButtonClass: isMaintenanceMode ? 'btn-primary' : 'btn-secondary',
confirmMessage: isMaintenanceMode ? '' : window.moduleTranslations.moduleModalUpdateConfirmMessage,
closable: true,
customButtons: isMaintenanceMode ? [] : [maintenanceLink],
},
() => {
if ($(self.upgradeAllTargets).length <= 0) {
console.warn(window.translate_javascripts['Upgrade All Action - One module minimum']);
return false;
}
const modulesActions = [];
let moduleTechName;
$(self.upgradeAllTargets).each(function bulkActionSelector() {
const moduleItemList = $(this).closest('.module-item-list');
moduleTechName = moduleItemList.data('tech-name');
modulesActions.push({
techName: moduleTechName,
actionMenuObj: $('.module-actions', moduleItemList),
});
});
this.performModulesAction(modulesActions, 'upgrade');
return true;
},
);
updateAllConfirmModal.show();
return true;
});
}
initCategorySelect() {
const self = this;
const body = $('body');
body.on('click', self.categoryItemSelector, function initializeCategorySelectClick() {
// Get data from li DOM input
self.currentRefCategory = $(this).data('category-ref');
self.currentRefCategory = self.currentRefCategory ? String(self.currentRefCategory).toLowerCase() : null;
// Change dropdown label to set it to the current category's displayname
$(self.categorySelectorLabelSelector).text($(this).data('category-display-name'));
$(self.categoryResetBtnSelector).show();
self.updateModuleVisibility();
});
body.on('click', self.categoryResetBtnSelector, function initializeCategoryResetButtonClick() {
const rawText = $(self.categorySelector).attr('aria-labelledby');
const upperFirstLetter = rawText.charAt(0).toUpperCase();
const removedFirstLetter = rawText.slice(1);
const originalText = upperFirstLetter + removedFirstLetter;
$(self.categorySelectorLabelSelector).text(originalText);
$(this).hide();
self.currentRefCategory = null;
self.updateModuleVisibility();
});
}
initSearchBlock() {
const self = this;
self.pstaggerInput = $('#module-search-bar').pstagger({
onTagsChanged: (tagList) => {
self.currentTagsList = tagList;
self.updateModuleVisibility();
},
onResetTags: () => {
self.currentTagsList = [];
self.updateModuleVisibility();
},
inputPlaceholder: window.translate_javascripts['Search - placeholder'],
closingCross: true,
context: self,
});
}
/**
* Initialize display switching between List or Grid
*/
initSortingDisplaySwitch() {
const self = this;
$('body').on('click', '.module-sort-switch', function switchSort() {
const switchTo = $(this).data('switch');
const isAlreadyDisplayed = $(this).hasClass('active-display');
if (typeof switchTo !== 'undefined' && isAlreadyDisplayed === false) {
self.switchSortingDisplayTo(switchTo);
self.currentDisplay = switchTo;
}
});
}
switchSortingDisplayTo(switchTo) {
if (switchTo !== this.DISPLAY_GRID && switchTo !== this.DISPLAY_LIST) {
console.error(`Can't switch to undefined display property "${switchTo}"`);
return;
}
$('.module-sort-switch').removeClass('module-sort-active');
$(`#module-sort-${switchTo}`).addClass('module-sort-active');
this.currentDisplay = switchTo;
this.updateModuleVisibility();
}
initializeSeeMore() {
const self = this;
$(`${self.moduleShortList} ${self.seeMoreSelector}`).on('click', function seeMore() {
self.currentCategoryDisplay[$(this).data('category')] = true;
$(this).addClass('d-none');
$(this)
.closest(self.moduleShortList)
.find(self.seeLessSelector)
.removeClass('d-none');
self.updateModuleVisibility();
});
$(`${self.moduleShortList} ${self.seeLessSelector}`).on('click', function seeMore() {
self.currentCategoryDisplay[$(this).data('category')] = false;
$(this).addClass('d-none');
$(this)
.closest(self.moduleShortList)
.find(self.seeMoreSelector)
.removeClass('d-none');
self.updateModuleVisibility();
});
}
updateTotalResults() {
const self = this;
const replaceFirstWordBy = (element, value) => {
const explodedText = element.text().split(' ');
explodedText[0] = value;
element.text(explodedText.join(' '));
};
// If there are some shortlist: each shortlist count the modules on the next container.
const $shortLists = $('.module-short-list');
if ($shortLists.length > 0) {
$shortLists.each(function shortLists() {
const $this = $(this);
replaceFirstWordBy(
$this.find('.module-search-result-wording'),
$this.next('.modules-list').find('.module-item').length,
);
});
// If there is no shortlist: the wording directly update from the only module container.
} else {
const modulesCount = $('.modules-list').find('.module-item').length;
replaceFirstWordBy($('.module-search-result-wording'), modulesCount);
// eslint-disable-next-line
const selectorToToggle =
self.currentDisplay === self.DISPLAY_LIST ? this.addonItemListSelector : this.addonItemGridSelector;
$(selectorToToggle).toggle(modulesCount !== this.modulesList.length / 2);
}
}
isModulesPage() {
return $(this.upgradeContainer).length === 0 && $(this.notificationContainer).length === 0;
}
isReadMoreModalOpened() {
return $('.modal-read-more').is(':visible');
}
}
export default AdminModuleController;
xxxxx1.0, XXX xxxx