Files
server/opt/psa/admin/htdocs/modules/panel-migrator/scripts/migration-status.js
2026-01-07 20:52:11 +01:00

437 lines
18 KiB
JavaScript

// 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();
});