diff --git a/application/controllers/MigrateController.php b/application/controllers/MigrateController.php deleted file mode 100644 index 945befe75..000000000 --- a/application/controllers/MigrateController.php +++ /dev/null @@ -1,53 +0,0 @@ -assertHttpMethod('post'); - if (! $this->getRequest()->isApiRequest()) { - $this->httpBadRequest('No API request'); - } - - if ( - ! preg_match('/([^;]*);?/', $this->getRequest()->getHeader('Content-Type'), $matches) - || $matches[1] !== 'application/json' - ) { - $this->httpBadRequest('No JSON content'); - } - - $urls = $this->getRequest()->getPost(); - - $result = []; - foreach ($urls as $urlString) { - $url = Url::fromPath($urlString); - $params = $url->onlyWith(['sort', 'limit', 'view', 'columns', 'page'])->getParams(); - $filter = $url->without(['sort', 'limit', 'view', 'columns', 'page'])->getParams(); - $filter = QueryString::parse((string) $filter); - $nonStrictOriginalFilter = QueryString::render($filter); - $filter = UrlMigrator::transformLegacyWildcardFilter($filter); - $nonStrictUpdatedFilter = QueryString::render($filter); - if ($nonStrictUpdatedFilter !== $nonStrictOriginalFilter) { - // The original filter might be formatted loosely, so if we render it again, - // it might look different although nothing changed - $result[] = rawurldecode($url->setParams($params)->setFilter($filter)->getAbsoluteUrl()); - } else { - $result[] = $urlString; - } - } - - $response = $this->getResponse()->json(); - $response->setSuccessData($result); - - $response->sendResponse(); - } -} diff --git a/configuration.php b/configuration.php index 72f04b099..43a625c1a 100644 --- a/configuration.php +++ b/configuration.php @@ -582,6 +582,5 @@ $this->provideJsFile('action-list.js'); $this->provideJsFile('loadmore.js'); - $this->provideJsFile('migrate.js'); $this->provideJsFile('progress-bar.js'); } diff --git a/doc/05-Upgrading.md b/doc/05-Upgrading.md index 57bcb2148..4acb7a8ee 100644 --- a/doc/05-Upgrading.md +++ b/doc/05-Upgrading.md @@ -7,9 +7,11 @@ If you are upgrading across multiple versions, make sure to follow the steps for **Removed Features** -* The migration widget in the top right is not toggled anymore for monitoring views, nor does it allow choosing - the preferred backend type for modules with support for IDO and Icinga DB. The default backend type is now - always Icinga DB. +* The migration widget in the top right has been removed. If you have not adjusted your navigation items, + dashboards and bookmarks to support the new filter syntax, you will need to do so manually now (see [Upgrading to + Icinga DB Web v1.1](#upgrading-to-icinga-db-web-v11) for details). + * Modules with support for IDO and Icinga DB will now default to use Icinga DB. + * The accompanying endpoint `icingadb/migrate/monitoring-url` has been removed. ## Upgrading to Icinga DB Web v1.2 diff --git a/public/css/widget/migrate-popup.less b/public/css/widget/migrate-popup.less deleted file mode 100644 index b4119a839..000000000 --- a/public/css/widget/migrate-popup.less +++ /dev/null @@ -1,157 +0,0 @@ -#migrate-popup { - @transitionLength: 350ms; - - display: flex; - min-width: 16em; - z-index: 1000; - position: fixed; - top: 0; - right: 4em; - pointer-events: none; - line-height: 1.5em; - - .transform(translateY(-100%)); - .transition(transform @transitionLength ease-in); - - &.active { - .transform(translateY(0%)); - .transition(transform @transitionLength ease-out); - } - - .suggestion-area { - .transform(translateY(0%)); - .transition(transform 0s linear @transitionLength); - } - - &.active .suggestion-area { - .transition(transform @transitionLength ease-out); - } - - &.minimized .suggestion-area { - .transform(translateY(-100%)); - .transition(transform @transitionLength ease-in); - } - - &.hidden .suggestion-area { - .transition(none); - } - - .minimizer { - width: 1.25em; - height: 1.5em; - margin-left: -1px; - z-index: 1; - pointer-events: auto; - - border-bottom-right-radius: 4px; - background-color: @body-bg-color; - - .transition(none); - - i:before { - width: 1em; - margin: .1em 0 0 0; - content: '\f102'; - font-size: 1.25em; - cursor: pointer; - color: @gray-light; - } - - i:hover:before { - color: @menu-highlight-color; - } - } - - &.minimized .minimizer { - border-bottom-left-radius: 4px; - .transition(border-bottom-left-radius 0s linear @transitionLength); - } - - &.hidden .minimizer i:before { - content: '\f103'; - } - - &:not(.active) .suggestion-area, &.hidden .suggestion-area { - box-shadow: none; - } - - .suggestion-area { - display: flex; - flex-direction: column-reverse; - - padding: .75em; - flex-grow: 1; - pointer-events: auto; - font-size: .75em; - - background: @body-bg-color; - border-radius: 0 0 4px 4px; - box-shadow: 0 0 1em 0 rgba(0, 0, 0, 0.3); - - button { - .link-button(); - } - - p { - display: none; - margin-bottom: .5em; - color: @text-color-light; - } - - .search-migration-suggestions:not(:empty) + .search-migration-hint { - display: block; - } - - & > button.close { - margin-left: auto; - margin-top: 1em; - - &:hover { - text-decoration: underline; - } - } - - ul { - padding: 0; - margin: 0; - list-style-type: none; - } - - li { - margin: .5em 0; - display: flex; - - &:last-of-type { - margin-bottom: 0; - } - - &:first-of-type { - margin-top: 0; - } - } - - li { - :not(:last-child) { - margin-right: .5em; - } - - button:hover{ - opacity: 0.8; - } - - button[value="1"] { - flex-grow: 1; - - color: @text-color; - text-decoration: underline; - } - - button[value="0"] { - i:before { - margin: 0; - content: '\e804'; - } - } - } - } -} diff --git a/public/js/migrate.js b/public/js/migrate.js deleted file mode 100644 index 3766c5395..000000000 --- a/public/js/migrate.js +++ /dev/null @@ -1,429 +0,0 @@ -/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ - -;(function(Icinga, $) { - - 'use strict'; - - const ANIMATION_LENGTH = 350; - - const POPUP_HTML = '
\n' + - '
\n' + - '
\n' + - ' \n' + - '
    \n' + - '

    Miss some results? Try the link(s) below

    \n' + - '
    \n' + - '
    \n' + - '
    \n' + - '
    '; - - const SUGGESTION_HTML = '
  • \n' + - ' \n' + - ' \n' + - '
  • '; - - Icinga.Behaviors = Icinga.Behaviors || {}; - - /** - * Icinga DB Migration behavior. - * - * @param icinga {Icinga} The current Icinga Object - */ - class Migrate extends Icinga.EventListener { - constructor(icinga) { - super(icinga); - - this.knownMigrations = {}; - this.searchMigrationReadyState = null; - this.$popup = null; - - // Some persistence, we don't want to annoy our users too much - this.storage = Icinga.Storage.BehaviorStorage('icingadb.migrate'); - this.tempStorage = Icinga.Storage.BehaviorStorage('icingadb.migrate'); - this.tempStorage.setBackend(window.sessionStorage); - this.previousMigrations = {}; - - this.on('rendered', this.onRendered, this); - this.on('close-column', this.onColumnClose, this); - this.on('click', '#migrate-popup button.close', this.onClose, this); - this.on('click', '#migrate-popup li button', this.onDecision, this); - this.on('click', '#migrate-popup .minimizer', this.onHandleClicked, this); - this.storage.onChange('minimized', this.onMinimized, this); - } - - onRendered(event) { - var _this = event.data.self; - var $target = $(event.target); - - if (_this.tempStorage.get('closed') || $('#layout.fullscreen-layout').length) { - // Don't bother in case the user closed the popup or we're in fullscreen - return; - } - - if (!$target.is('#main > .container')) { - if ($target.is('#main .container')) { - var attrUrl = $target.attr('data-icinga-url'); - var dataUrl = $target.data('icingaUrl'); - if (!! attrUrl && attrUrl !== dataUrl) { - // Search urls are redirected, update any migration suggestions - _this.prepareMigration($target); - return; - } - } - - // We are else really only interested in top-level containers - return; - } - - var $dashboard = $target.children('.dashboard'); - if ($dashboard.length) { - // After a page load dashlets have no id as `renderContentToContainer()` didn't ran yet - _this.icinga.ui.assignUniqueContainerIds(); - - $target = $dashboard.children('.container'); - } - - _this.prepareMigration($target); - } - - prepareMigration($target) { - let searchUrls = {}; - - $target.each((_, container) => { - let $container = $(container); - let href = decodeURIComponent($container.data('icingaUrl')); - let containerId = $container.attr('id'); - - if (!!href) { - if ( - typeof this.previousMigrations[containerId] !== 'undefined' - && this.previousMigrations[containerId] === href - ) { - delete this.previousMigrations[containerId]; - } else { - if ($container.find('[data-enrichment-type="search-bar"]').length) { - searchUrls[containerId] = href; - } - } - } - }); - - if (Object.keys(searchUrls).length) { - this.setSearchMigrationReadyState(false); - this.migrateUrls(searchUrls, 'search'); - } else { - this.setSearchMigrationReadyState(null); - } - - if (this.searchMigrationReadyState === null) { - this.cleanupPopup(); - } - } - - onColumnClose(event) { - var _this = event.data.self; - _this.Popup().find('.suggestion-area > ul li').each(function () { - var $suggestion = $(this); - var suggestionUrl = $suggestion.data('containerUrl'); - var $container = $('#' + $suggestion.data('containerId')); - - var containerUrl = ''; - if ($container.length) { - containerUrl = decodeURIComponent($container.data('icingaUrl')); - } - - if (suggestionUrl !== containerUrl) { - var $newContainer = $('#main > .container').filter(function () { - return decodeURIComponent($(this).data('icingaUrl')) === suggestionUrl; - }); - if ($newContainer.length) { - // Container moved - $suggestion.attr('id', 'suggest-' + $newContainer.attr('id')); - $suggestion.data('containerId', $newContainer.attr('id')); - } - } - }); - - _this.cleanupPopup(); - } - - onClose(event) { - var _this = event.data.self; - _this.tempStorage.set('closed', true); - _this.hidePopup(); - } - - onDecision(event) { - var _this = event.data.self; - var $button = $(event.target).closest('button'); - var $suggestion = $button.parent(); - var $container = $('#' + $suggestion.data('containerId')); - var containerUrl = decodeURIComponent($container.data('icingaUrl')); - - if ($button.attr('value') === '1') { - // Yes - var newHref = _this.knownMigrations[containerUrl]; - _this.icinga.loader.loadUrl(newHref, $container); - - _this.previousMigrations[$suggestion.data('containerId')] = containerUrl; - - if ($container.parent().is('.dashboard')) { - $container.find('h1 a').attr('href', _this.icinga.utils.removeUrlParams(newHref, ['showCompact'])); - } - } else { - // No - _this.knownMigrations[containerUrl] = false; - } - - if (_this.Popup().find('li').length === 1) { - _this.hidePopup(function () { - // Let the transition finish first, looks cleaner - $suggestion.remove(); - }); - } else { - $suggestion.remove(); - } - } - - onHandleClicked(event) { - var _this = event.data.self; - if (_this.togglePopup()) { - _this.storage.set('minimized', true); - } else { - _this.storage.remove('minimized'); - } - } - - onMinimized(isMinimized, oldValue) { - if (isMinimized && isMinimized !== oldValue && this.isShown()) { - this.minimizePopup(); - } - } - - migrateUrls(urls, type) { - var _this = this, - containerIds = [], - containerUrls = []; - - $.each(urls, function (containerId, containerUrl) { - if (typeof _this.knownMigrations[containerUrl] === 'undefined') { - containerUrls.push(containerUrl); - containerIds.push(containerId); - } - }); - - const endpoint = 'search-url'; - const changeCallback = this.changeSearchMigrationReadyState.bind(this); - - if (containerUrls.length) { - var req = $.ajax({ - context: this, - type: 'post', - url: this.icinga.config.baseUrl + '/icingadb/migrate/' + endpoint, - headers: {'Accept': 'application/json'}, - contentType: 'application/json', - data: JSON.stringify(containerUrls) - }); - - req.urls = urls; - req.suggestionType = type; - req.urlIndexToContainerId = containerIds; - req.done(this.processUrlMigrationResults); - req.always(() => changeCallback(true)); - } else { - // All urls have already been migrated once, show popup immediately - this.addSuggestions(urls, type); - changeCallback(true); - } - } - - processUrlMigrationResults(data, textStatus, req) { - var _this = this; - var result, containerId; - - if (data.status === 'success') { - result = data.data; - } else { // if (data.status === 'fail') - result = data.data.result; - - $.each(data.data.errors, function (k, error) { - _this.icinga.logger.error('[Migrate] Erroneous url "' + k + '": ' + error[0] + '\n' + error[1]); - }); - } - - $.each(result, function (i, migratedUrl) { - containerId = req.urlIndexToContainerId[i]; - _this.knownMigrations[req.urls[containerId]] = migratedUrl; - }); - - this.addSuggestions(req.urls, req.suggestionType); - } - - addSuggestions(urls, type) { - const where = '.search-migration-suggestions'; - - var _this = this, - hasSuggestions = false, - $ul = this.Popup().find('.suggestion-area > ul' + where); - $.each(urls, function (containerId, containerUrl) { - // No urls for which the user clicked "No" or an error occurred and only migrated urls please - if (_this.knownMigrations[containerUrl] !== false && _this.knownMigrations[containerUrl] !== containerUrl) { - var $container = $('#' + containerId); - - var $suggestion = $ul.find('li#suggest-' + containerId); - if ($suggestion.length) { - if ($suggestion.data('containerUrl') === containerUrl) { - // There's already a suggestion for this exact container and url - hasSuggestions = true; - return; - } - - $suggestion.data('containerUrl', containerUrl); - } else { - $suggestion = $(SUGGESTION_HTML); - $suggestion.attr('id', 'suggest-' + containerId); - $suggestion.data('containerId', containerId); - $suggestion.data('containerUrl', containerUrl); - $ul.append($suggestion); - } - - hasSuggestions = true; - - var title; - if ($container.data('icingaTitle')) { - title = $container.data('icingaTitle').split(' :: ').slice(0, -1).join(' :: '); - } else if ($container.parent().is('.dashboard')) { - title = $container.find('h1 a').text(); - } else { - title = $container.find('.tabs li.active a').text(); - } - - $suggestion.find('button:first-of-type').text(title); - } - }); - - if (hasSuggestions) { - this.showPopup(); - if (type === 'search') { - this.maximizePopup(); - } - } - } - - cleanupSuggestions() { - var _this = this, - toBeRemoved = []; - this.Popup().find('li').each(function () { - var $suggestion = $(this); - var $container = $('#' + $suggestion.data('containerId')); - var containerUrl = decodeURIComponent($container.data('icingaUrl')); - if ( - // Unknown url, yet - typeof _this.knownMigrations[containerUrl] === 'undefined' - // User doesn't want to migrate - || _this.knownMigrations[containerUrl] === false - // Already migrated or no migration necessary - || containerUrl === _this.knownMigrations[containerUrl] - // The container URL changed - || containerUrl !== $suggestion.data('containerUrl') - ) { - toBeRemoved.push($suggestion); - } - }); - - return toBeRemoved; - } - - cleanupPopup() { - let toBeRemoved = this.cleanupSuggestions(); - - if (this.Popup().find('li').length === toBeRemoved.length) { - this.hidePopup(() => { - // Let the transition finish first, looks cleaner - $.each(toBeRemoved, function (_, $suggestion) { - $suggestion.remove(); - }); - }); - } else { - $.each(toBeRemoved, function (_, $suggestion) { - $suggestion.remove(); - }); - - // Let showPopup() handle the automatic minimization in case all search suggestions have been removed - this.showPopup(); - } - } - - showPopup() { - var $popup = this.Popup(); - if (this.storage.get('minimized') && ! this.forceFullyMaximized()) { - if (this.isShown()) { - this.minimizePopup(); - } else { - $popup.addClass('active minimized hidden'); - } - } else { - $popup.addClass('active'); - } - } - - hidePopup(after) { - this.Popup().removeClass('active minimized hidden'); - - if (typeof after === 'function') { - setTimeout(after, ANIMATION_LENGTH); - } - } - - isShown() { - return this.Popup().is('.active'); - } - - minimizePopup() { - var $popup = this.Popup(); - $popup.addClass('minimized'); - setTimeout(function () { - $popup.addClass('hidden'); - }, ANIMATION_LENGTH); - } - - maximizePopup() { - this.Popup().removeClass('minimized hidden'); - } - - forceFullyMaximized() { - return this.Popup().find('.search-migration-suggestions:not(:empty)').length > 0; - } - - togglePopup() { - if (this.Popup().is('.minimized')) { - this.maximizePopup(); - return false; - } else { - this.minimizePopup(); - return true; - } - } - - setSearchMigrationReadyState(state) { - this.searchMigrationReadyState = state; - } - - changeSearchMigrationReadyState(state) { - this.setSearchMigrationReadyState(state); - } - - Popup() { - // Node.contains() is used due to `?renderLayout` - if (this.$popup === null || ! document.body.contains(this.$popup[0])) { - $('#layout').append($(POPUP_HTML)); - this.$popup = $('#migrate-popup'); - } - - return this.$popup; - } - } - - Icinga.Behaviors.Migrate = Migrate; - -})(Icinga, jQuery);