// Copyright 1999-2024. WebPros International GmbH. All rights reserved. Jsw.onReady(function() { const appState = PanelMigrator.initAppState(); // Global variable representing current status of migration. Updated periodically with AJAX request. var statusData = MIGRATION_STATUS_DATA; // Run specified function for each customer in results column (from raw migration list) // callback - function that receives 2 arguments: customer login as string and status item HTML element function foreachResultCustomer(callback) { $$('.customerStatus').each(function(element) { var customerLogin = element.select(".customerLogin")[0].textContent.strip(); callback(customerLogin, element); }); } // Run specified function for each reseller in results column (from raw migration list) // callback - function that receives 2 arguments: reseller login as string and status item HTML element function foreachResultReseller(callback) { $$('.resellerStatus').each(function(element) { var resellerLogin = element.select(".resellerLogin")[0].textContent.strip(); callback(resellerLogin, element); }); } // Run specified function for each subscription in results column (from raw migration list) // callback - function that receives 2 arguments: subscription name as string and status item HTML element function foreachResultSubscription(callback) { $$('.subscription-finished').each(function (element) { var subscriptionName = element.select(".subscription-name")[0].textContent.strip(); callback(subscriptionName, element) }); } // Run specified function for each subscription in queue column (from raw migration list) // callback - function that receives 2 arguments: subscription name as string and queue item HTML element function foreachQueueSubscription(callback) { $$('.subscription-in-progress').each(function (element) { var subscriptionName = element.select(".subscription-name")[0].textContent.strip(); callback(subscriptionName, element); }); } // Run specified function for overall result // callback - function that receives 1 argument: overall item HTML element function forResultOverall(callback) { var overallElement = $$('.overallStatus')[0]; callback(overallElement); } function forResultServerSettings(callback) { var serverElement = $('migration-status-block-results-server-settings'); callback(serverElement); } // Root update function: called once we get new data about migration status with AJAX request function updateMigrationStatus() { updateQueue(); updatePreChecks(); updateResults(); } // Update left column: migration queue function updateQueue() { updateQueueOverallAction(); updateQueueCount(); updateQueueSubscriptions(); } // Update right column: migration results function updateResults() { updateResultsSubscription(); updateResultsServerSettings(); updateResultsReseller(); updateResultsCustomer(); updateResultsOverall(); updateSubscriptionCounts(); } // Update overall action - action executed on all subscriptions function updateQueueOverallAction() { if (statusData.overall.action) { $('overall-status-text').textContent = getPropertyChain(statusData, ['overall', 'action'], ''); $('overall-status').show(); } else { $('overall-status').hide(); } } // Update number of subscriptions in queue function updateQueueCount() { $('queue-count').textContent = countItems( getPropertyChain(statusData, ['subscriptions'], []), function(_, subscription) { return inArray(subscription.status, [statuses.IN_PROGRESS, statuses.ON_HOLD]) } ); } function updatePreChecks() { var preCheckTasksCount = 0; $$('.migration-status-pre-check-task').each(function(element) { var taskId = getChildElementText(element, 'task-id'); var taskStatus = getPropertyChain(statusData, ['preCheckTasks', taskId, 'status'], null); if (taskStatus == 'not-started' || taskStatus == 'pre-checks-completed') { var link = element.select('a').first(); link.stopObserving('click'); link.observe('click', function() { showPreChecksPopup(taskId, false); }); element.show(); preCheckTasksCount++; } else { element.hide(); } }); $('migration-status-block-pre-checks').toggle(preCheckTasksCount > 0); } // Update status of each subscription in queue function updateQueueSubscriptions() { foreachQueueSubscription(function(subscriptionName, element) { var subscriptionStatus = getPropertyChain( statusData, ['subscriptions', subscriptionName, 'status'], statuses.NOT_STARTED ); var action = getPropertyChain( statusData, ['subscriptions', subscriptionName, 'action'], '' ); var details = getPropertyChain( statusData, ['subscriptions', subscriptionName, 'details'], '' ); toggleChildElement( element, 'image-status-in-progress', subscriptionStatus == statuses.IN_PROGRESS ); toggleChildElement( element, 'image-status-on-hold', subscriptionStatus == statuses.ON_HOLD ); element.toggle(inArray(subscriptionStatus, [statuses.IN_PROGRESS, statuses.ON_HOLD])); var statusTextElement = element.select(".status-text")[0]; var detailsTextElement = element.select(".details-text")[0]; var detailsTextContainer = element.select(".details-text-container")[0]; var showAction = ( action && // Show action text only if subscription is "In progress". There could be situation when // progress file (which contains action) and subscription status files (which contains status) // are not in sync. For example, we need such check to avoid display actions for // subscriptions marked as "On hold". subscriptionStatus == statuses.IN_PROGRESS ); if (showAction) { statusTextElement.textContent = action; if (detailsTextElement && detailsTextContainer) { detailsTextElement.textContent = details; if (details) { detailsTextContainer.show(); } else { detailsTextContainer.hide(); } } } else { statusTextElement.textContent = ''; if (detailsTextElement && detailsTextContainer) { detailsTextElement.textContent = ''; detailsTextContainer.hide(); } } toggleChildElement(element, 'status-separator', showAction); toggleChildElement(element, 'status-text', showAction); updateSubscriptionNameColumn(element); }); } // Update migration results for subscriptions function updateResultsSubscription() { var hasSubscriptionsDisplayed = false; foreachResultSubscription(function(subscriptionName, element) { var issues = getPropertyChain( statusData, ['subscriptionIssues', subscriptionName], [] ); var status = getPropertyChain( statusData, ['subscriptions', subscriptionName, 'status'], statuses.NOT_STARTED ); var isStatusFinished = inArray( status, [statuses.FINISHED_OK, statuses.FINISHED_WARNINGS, statuses.FINISHED_ERRORS] ); var isStatusQueued = inArray( status, [statuses.IN_PROGRESS, statuses.ON_HOLD] ); // Show subscription in 2 cases: // 1) There are issues for that subscription (even if migration is still running). // 2) Migration of the subscription finished. if (issues.length > 0 || isStatusFinished) { // Show status of the last operation for subscription, if available - otherwise detect by issues if (isStatusFinished) { updateStatusImageByStatus(element, status); } else { updateStatusImageByIssues(element, issues) } toggleChildElement( element, 'action-resync', ( !isStatusQueued && status != statuses.NOT_STARTED && status != statuses.CANCELLED ) ); element.show(); hasSubscriptionsDisplayed = true; } else { element.hide(); } updateSubscriptionNameColumn(element); }); $('migration-status-block-results-subscription').toggle(hasSubscriptionsDisplayed); } // Update migration results for resellers function updateResultsReseller() { var hasResellerWithIssues = false; foreachResultReseller(function(resellerLogin, element) { var issues = getPropertyChain(statusData, ['resellerIssues', resellerLogin], []); element.toggle(issues.length > 0); hasResellerWithIssues = hasResellerWithIssues || issues.length > 0; }); $('migration-status-block-results-reseller').toggle(hasResellerWithIssues); } // Update migration results for customer function updateResultsCustomer() { var hasCustomerWithIssues = false; foreachResultCustomer(function(customerLogin, element) { var issues = getPropertyChain(statusData, ['customerIssues', customerLogin], []); element.toggle(issues.length > 0); hasCustomerWithIssues = hasCustomerWithIssues || issues.length > 0; }); $('migration-status-block-results-customer').toggle(hasCustomerWithIssues); } // Update overall migration results function updateResultsOverall() { forResultOverall(function(element) { var issues = getPropertyChain(statusData, ['overallIssues'], []); $('migration-status-block-results-overall').toggle(issues.length > 0); }); } // Update server configuration migration results function updateResultsServerSettings() { forResultServerSettings(function(element) { let migrationStatuses = getPropertyChain(statusData, ['serverSettingsStatuses'], null); let configurationResult = $('migration-status-block-results-server-configuration'); let extensionResult = $('migration-status-block-results-extensions'); let configurationStatus = migrationStatuses.configuration; let extensionStatus = migrationStatuses.extensions; configurationResult.toggle(showBlock(configurationStatus)); updateStatusImageByStatus(configurationResult, configurationStatus); extensionResult.toggle(showBlock(extensionStatus)); updateStatusImageByStatus(extensionResult, extensionStatus); element.toggle(showBlock(configurationStatus) || showBlock(extensionStatus)); function showBlock(status) { return inArray(status, [statuses.FINISHED_ERRORS, statuses.FINISHED_OK, statuses.FINISHED_WARNINGS]) } }); } // Update counts of successful/failed subscriptions function updateSubscriptionCounts() { // update right column: list of finished subscriptions const statusCounts = [ { status: statuses.FINISHED_OK, elementId: 'success-count' }, { status: statuses.FINISHED_ERRORS, elementId: 'failed-count' }, { status: statuses.FINISHED_WARNINGS, elementId: 'warning-count' } ]; statusCounts.forEach(({ status, elementId}) => { const count = countItems( statusData.subscriptions, function(_, subscription) { return subscription.status === status; } ); const element = $(elementId); element.textContent = count element.parentNode.toggle(count > 0); }); } // Update migration status periodically with AJAX function periodicUpdateMigrationStatus() { if (!appState.mounted) return; new Ajax.Request(URL_GET_MIGRATION_STATUS, { onSuccess: function (response) { if (!appState.mounted) return; statusData = response.responseText.evalJSON(); updateMigrationStatus(); }, onComplete: function() { setTimeout(function() {periodicUpdateMigrationStatus()}, 2000); } }); } // Utility function to show proper icon according to migration status function updateStatusImageByStatus(element, status) { toggleChildElement(element, 'image-status-success', status == statuses.FINISHED_OK); toggleChildElement(element, 'image-status-warning', status == statuses.FINISHED_WARNINGS); toggleChildElement(element, 'image-status-failure', status == statuses.FINISHED_ERRORS); } // Utility function to show proper icon according to issues list function updateStatusImageByIssues(element, issues) { var hasErrors = containsElement(issues, function(issue) { return issue.severity == issueSeverity.ERROR; }); var hasWarnings = containsElement(issues, function(issue) { return issue.severity == issueSeverity.WARNING; }); toggleChildElement(element, 'image-status-success', !hasErrors && !hasWarnings); toggleChildElement(element, 'image-status-warning', !hasErrors && hasWarnings); toggleChildElement(element, 'image-status-failure', hasErrors); } function assignControlHandlers() { foreachResultSubscription(function(subscriptionName, element) { observeClickOnChildElement(element, 'action-resync', function() { showResyncPopup([subscriptionName], null, SHOW_SERVER_CONFIGURATION_BLOCK, SHOW_EXTENSIONS_SETTINGS_BLOCK); }); }); foreachResultSubscription(function(subscriptionName, element) { observeClickOnChildElement(element, 'action-details', function() { var issues = getPropertyChain( statusData, ['subscriptionIssues', subscriptionName], [] ); showIssuesPopupDialog( migratorLocale.lmsg('issuesPopupTitleSubscription', {'subscription': subscriptionName}), issues ); }); }); foreachResultReseller(function(resellerLogin, element) { observeClickOnChildElement(element, 'action-details', function() { var issues = getPropertyChain(statusData, ['resellerIssues', resellerLogin], []); showIssuesPopupDialog( migratorLocale.lmsg('issuesPopupTitleReseller', {'reseller': resellerLogin}), issues ); }); }); foreachResultCustomer(function(customerLogin, element) { observeClickOnChildElement(element, 'action-details', function() { var issues = getPropertyChain(statusData, ['customerIssues', customerLogin], []); showIssuesPopupDialog( migratorLocale.lmsg('issuesPopupTitleCustomer', {'customer': customerLogin}), issues ); }); }); forResultOverall(function(element) { observeClickOnChildElement(element, 'action-details', function() { var issues = getPropertyChain(statusData, ['overallIssues'], []); showIssuesPopupDialog( migratorLocale.lmsg('issuesPopupTitleOverall'), issues ); }); }); forResultServerSettings(function(element) { let configurationResult = element.select('#migration-status-block-results-server-configuration').first(); let extensionResult = element.select('#migration-status-block-results-extensions').first(); observeClickOnChildElement(configurationResult, 'action-details', function() { let issues = getPropertyChain(statusData, ['serverSettingIssues'], []); showIssuesPopupDialog( migratorLocale.lmsg('issuesPopupTitleServerConfiguration'), issues.Configuration || [] ); }); observeClickOnChildElement(extensionResult, 'action-details', function() { let issues = getPropertyChain(statusData, ['serverSettingIssues'], []); showIssuesPopupDialog( migratorLocale.lmsg('issuesPopupTitleExtensions'), issues.Extensions || [] ); }); }); } // Update subscription name column - show link to subscription overview page if subscription // exists in Plesk function updateSubscriptionNameColumn(element) { var subscriptionNameNormalized = getChildElementText(element, 'subscription-name-normalized').strip(); var pleskSubscriptionUrl = getPropertyChain( statusData, ['pleskSubscriptionUrls', subscriptionNameNormalized], null ); var linkExists = pleskSubscriptionUrl !== null; toggleChildElement(element, 'subscription-name-plain', !linkExists); toggleChildElement(element, 'subscription-name-link', linkExists); if (pleskSubscriptionUrl) { var link = element.select('.subscription-name-link').first().select('a').first(); link.writeAttribute('href', pleskSubscriptionUrl); } } // Perform initial update from data passed in HTML updateMigrationStatus(); // Schedule run periodic updates with AJAX periodicUpdateMigrationStatus(); // Assign handlers to various action buttons assignControlHandlers(); });