public function Display(WebPage $oPage)
    {
        $oPage->add_style(<<<EOF
#changes_summary {
\tmax-height: 200px;
\toverflow: auto;
}
#changes_summary div {
\twidth:100;
\tmargin-top:0;
\tpadding-top: 0.5em;
\tpadding-left: 0;
}
#changes_summary div ul {
\tmargin-left:0;
\tpadding-left: 20px;
}
#changes_summary div.closed ul {
\tdisplay:none;
}
#changes_summary div li {
\tlist-style: none;
\twidth: 100;
\tmargin-left:0;
\tpadding-left: 0em;
}
.title {
\tpadding-left: 20px;
\tfont-weight: bold;
\tcursor: pointer;
\tbackground: url(../images/minus.gif) 2px 2px no-repeat;
}
#changes_summary div.closed .title {
\tbackground: url(../images/plus.gif) 2px 2px no-repeat;
}
EOF
);
        $this->bCanMoveForward = true;
        $bDisplayLicense = true;
        $sPreviousVersionDir = $this->oWizard->GetParameter('previous_version_dir', '');
        $aInstalledInfo = SetupUtils::GetApplicationVersion($this->oWizard);
        if ($aInstalledInfo === false) {
            throw new Exception('No previous version of ' . ITOP_APPLICATION . ' found in the supplied database. The upgrade cannot continue.');
        } else {
            if (strcasecmp($aInstalledInfo['product_name'], ITOP_APPLICATION) != 0) {
                $oPage->p("<b>Warning: The installed products seem different. Are you sure the you want to upgrade {$aInstalledInfo['product_name']} with " . ITOP_APPLICATION . "?</b>");
            }
        }
        $sInstalledVersion = $aInstalledInfo['product_version'];
        $sInstalledDataModelVersion = $aInstalledInfo['datamodel_version'];
        $oPage->add("<h2>Information about the upgrade from version {$sInstalledVersion} to " . ITOP_VERSION . '.' . ITOP_REVISION . "</h2>");
        if ($sInstalledVersion == ITOP_VERSION . '.' . ITOP_REVISION) {
            // Reinstalling the same version let's skip the license agreement...
            $bDisplayLicense = false;
        }
        $this->oWizard->SetParameter('license', $bDisplayLicense);
        // Remember for later
        if ($sInstalledDataModelVersion == '$ITOP_VERSION$.$WCREV$') {
            // Special case for upgrading some  development versions (temporary)
            $sCompatibleDMDir = SetupUtils::GetLatestDataModelDir();
            $sInstalledDataModelVersion = SetupUtils::GetDataModelVersion($sCompatibleDMDir);
        } else {
            $sCompatibleDMDir = SetupUtils::GetCompatibleDataModelDir($sInstalledDataModelVersion);
        }
        if ($sCompatibleDMDir === false) {
            // No compatible version exists... cannot upgrade. Either it is too old, or too new (downgrade !)
            $this->bCanMoveForward = false;
            $oPage->p("The current version of " . ITOP_APPLICATION . " (" . ITOP_VERSION . '.' . ITOP_REVISION . ") does not seem to be compatible with the installed version ({$sInstalledVersion}).");
            $oPage->p("The upgrade cannot continue, sorry.");
        } else {
            $sUpgradeDMVersion = SetupUtils::GetDataModelVersion($sCompatibleDMDir);
            $sPreviousSourceDir = isset($aInstalledInfo['source_dir']) ? $aInstalledInfo['source_dir'] : 'modules';
            $aChanges = false;
            if (is_dir($sPreviousVersionDir)) {
                // Check if the previous version is a "genuine" one or not...
                $aChanges = SetupUtils::CheckVersion($sInstalledDataModelVersion, $sPreviousVersionDir . '/' . $sPreviousSourceDir);
            }
            if ($aChanges !== false && (count($aChanges['added']) > 0 || count($aChanges['removed']) > 0 || count($aChanges['modified']) > 0)) {
                // Some changes were detected, prompt the user to keep or discard them
                $oPage->p("<img src=\"../images/error.png\"/>&nbsp;Some modifications were detected between the " . ITOP_APPLICATION . " version in '{$sPreviousVersionDir}' and a genuine {$sInstalledVersion} version.");
                $oPage->p("What do you want to do?");
                $aWritableDirs = array('modules', 'portal');
                $aErrors = SetupUtils::CheckWritableDirs($aWritableDirs);
                $sChecked = $this->oWizard->GetParameter('upgrade_type') == 'keep-previous' ? ' checked ' : '';
                $sDisabled = count($aErrors) > 0 ? ' disabled ' : '';
                $oPage->p('<input id="radio_upgrade_keep" type="radio" name="upgrade_type" value="keep-previous"' . $sChecked . $sDisabled . '/><label for="radio_upgrade_keep">&nbsp;Preserve the modifications of the installed version (the dasboards inside ' . ITOP_APPLICATION . ' may not be editable).</label>');
                $oPage->add('<input type="hidden" name="datamodel_previous_version" value="' . htmlentities($sInstalledDataModelVersion, ENT_QUOTES, 'UTF-8') . '">');
                $oPage->add('<input type="hidden" name="relative_source_dir" value="' . htmlentities($sPreviousSourceDir, ENT_QUOTES, 'UTF-8') . '">');
                if (count($aErrors) > 0) {
                    $oPage->p("Cannot copy the installed version due to the following access rights issue(s):");
                    foreach ($aErrors as $sDir => $oCheckResult) {
                        $oPage->p('<img src="../images/error.png"/>&nbsp;' . $oCheckResult->sLabel);
                    }
                }
                $sChecked = $this->oWizard->GetParameter('upgrade_type') == 'use-compatible' ? ' checked ' : '';
                $oPage->p('<input id="radio_upgrade_convert" type="radio" name="upgrade_type" value="use-compatible"' . $sChecked . '/><label for="radio_upgrade_convert">&nbsp;Discard the modifications, use a standard ' . $sUpgradeDMVersion . ' data model.</label>');
                $oPage->add('<input type="hidden" name="datamodel_path" value="' . htmlentities($sCompatibleDMDir, ENT_QUOTES, 'UTF-8') . '">');
                $oPage->add('<input type="hidden" name="datamodel_version" value="' . htmlentities($sUpgradeDMVersion, ENT_QUOTES, 'UTF-8') . '">');
                $oPage->add('<div id="changes_summary"><div class="closed"><span class="title">Details of the modifications</span><div>');
                if (count($aChanges['added']) > 0) {
                    $oPage->add('<ul>New files added:');
                    foreach ($aChanges['added'] as $sFilePath => $void) {
                        $oPage->add('<li>' . $sFilePath . '</li>');
                    }
                    $oPage->add('</ul>');
                }
                if (count($aChanges['removed']) > 0) {
                    $oPage->add('<ul>Deleted files:');
                    foreach ($aChanges['removed'] as $sFilePath => $void) {
                        $oPage->add('<li>' . $sFilePath . '</li>');
                    }
                    $oPage->add('</ul>');
                }
                if (count($aChanges['modified']) > 0) {
                    $oPage->add('<ul>Modified files:');
                    foreach ($aChanges['modified'] as $sFilePath => $void) {
                        $oPage->add('<li>' . $sFilePath . '</li>');
                    }
                    $oPage->add('</ul>');
                }
                $oPage->add('</div></div></div>');
            } else {
                // No changes detected... or no way to tell because of the lack of a manifest or previous source dir
                // Use the "compatible" datamodel as-is.
                $oPage->p("<img src=\"../images/validation_ok.png\"/>&nbsp;The datamodel will be upgraded from version {$sInstalledDataModelVersion} to version {$sUpgradeDMVersion}.");
                $oPage->add('<input type="hidden" name="upgrade_type" value="use-compatible">');
                $oPage->add('<input type="hidden" name="datamodel_path" value="' . htmlentities($sCompatibleDMDir, ENT_QUOTES, 'UTF-8') . '">');
                $oPage->add('<input type="hidden" name="datamodel_version" value="' . htmlentities($sUpgradeDMVersion, ENT_QUOTES, 'UTF-8') . '">');
            }
            // Check if there are "extensions" to preserve and if it's possible
            if (is_dir($sPreviousVersionDir . '/extensions')) {
                $aExtensions = glob($sPreviousVersionDir . '/extensions/*', GLOB_ONLYDIR);
                if ($aExtensions !== false && count($aExtensions) > 0 && realpath($sPreviousVersionDir . '/extensions') != realpath(APPROOT . 'extensions')) {
                    $aWritableDirs = array('extensions');
                    $aErrors = SetupUtils::CheckWritableDirs($aWritableDirs);
                    if (count($aErrors) > 0) {
                        $oPage->p("Cannot copy the extensions from '{$sPreviousVersionDir}/extensions' to '" . APPROOT . "extensions' due to the following access rights issue(s):");
                        foreach ($aErrors as $sDir => $oCheckResult) {
                            $oPage->p('<img src="../images/error.png"/>&nbsp;' . $oCheckResult->sLabel);
                        }
                    } else {
                        $oPage->p("<b>Note:</b> The extensions present in '{$sPreviousVersionDir}/extensions' will be copied to '" . APPROOT . "extensions'.");
                        $oPage->add('<input type="hidden" name="copy_extensions_from" value="' . htmlentities($sPreviousVersionDir . '/extensions', ENT_QUOTES, 'UTF-8') . '">');
                    }
                }
            }
            $oPage->add_ready_script(<<<EOF
\t\$("#changes_summary .title").click(function() { \$(this).parent().toggleClass('closed'); } );
\t\$('input[name=upgrade_type]').bind('click change', function() { WizardUpdateButtons(); });
EOF
);
            $oMutex = new iTopMutex('cron.' . $this->oWizard->GetParameter('db_name', '') . '_' . $this->oWizard->GetParameter('db_prefix', ''), $this->oWizard->GetParameter('db_server', ''), $this->oWizard->GetParameter('db_user', ''), $this->oWizard->GetParameter('db_pwd', ''));
            if ($oMutex->TryLock()) {
                $oMutex->Unlock();
            } else {
                $oPage->p("<img src=\"../images/error.png\"/>&nbsp;An iTop CRON process is being executed on the target database. It is highly recommended to stop any iTop CRON process prior to running the setup program.");
            }
        }
    }