diff --git a/src/legacy/ui/public/chrome/api/angular.js b/src/legacy/ui/public/chrome/api/angular.js index 70aa3f8d86c2bfb..4aeabeb8c2df3af 100644 --- a/src/legacy/ui/public/chrome/api/angular.js +++ b/src/legacy/ui/public/chrome/api/angular.js @@ -17,24 +17,16 @@ * under the License. */ -import React, { Fragment } from 'react'; import _ from 'lodash'; -import { modifyUrl } from 'ui/url'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; + import { uiModules } from '../../modules'; -import { toastNotifications } from '../../notify'; -import { UrlOverflowServiceProvider } from '../../error_url_overflow'; import { directivesProvider } from '../directives'; import { registerSubUrlHooks } from './sub_url_hooks'; - -const URL_LIMIT_WARN_WITHIN = 1000; +import { configureAppAngularModule } from 'ui/legacy_compat'; export function initAngularApi(chrome, internals) { - chrome.getFirstPathSegment = _.noop; - chrome.setupAngular = function () { const kibana = uiModules.get('kibana'); @@ -47,98 +39,18 @@ export function initAngularApi(chrome, internals) { .value('buildNum', internals.buildNum) .value('buildSha', internals.buildSha) .value('serverName', internals.serverName) - .value('sessionId', Date.now()) .value('chrome', chrome) - .value('esUrl', (function () { - const a = document.createElement('a'); - a.href = chrome.addBasePath('/elasticsearch'); - const protocolPort = /https/.test(a.protocol) ? 443 : 80; - const port = a.port || protocolPort; - return { - host: a.hostname, - port, - protocol: a.protocol, - pathname: a.pathname - }; - }())) - .config($locationProvider => { - $locationProvider.html5Mode({ - enabled: false, - requireBase: false, - rewriteLinks: false, - }); - }) .config(chrome.$setupXsrfRequestInterceptor) - .config(function ($compileProvider, $locationProvider) { + .config(function ($compileProvider) { if (!internals.devMode) { $compileProvider.debugInfoEnabled(false); } - - $locationProvider.hashPrefix(''); }) - .run(internals.capture$httpLoadingCount) - .run(internals.$setupBreadcrumbsAutoClear) - .run(internals.$setupHelpExtensionAutoClear) - .run(internals.$initNavLinksDeepWatch) - .run(($location, $rootScope, Private, config) => { - chrome.getFirstPathSegment = () => { - return $location.path().split('/')[1]; - }; - - const urlOverflow = Private(UrlOverflowServiceProvider); - const check = () => { - // disable long url checks when storing state in session storage - if (config.get('state:storeInSessionStorage')) { - return; - } - - if ($location.path() === '/error/url-overflow') { - return; - } - - try { - if (urlOverflow.check($location.absUrl()) <= URL_LIMIT_WARN_WITHIN) { - toastNotifications.addWarning({ - title: i18n.translate('common.ui.chrome.bigUrlWarningNotificationTitle', { - defaultMessage: 'The URL is big and Kibana might stop working' - }), - text: ( - - state:storeInSessionStorage, - advancedSettingsLink: ( - - - - ) - }} - /> - - ), - }); - } - } catch (e) { - window.location.href = modifyUrl(window.location.href, parts => { - parts.hash = '#/error/url-overflow'; - }); - // force the browser to reload to that Kibana's potentially unstable state is unloaded - window.location.reload(); - } - }; - - $rootScope.$on('$routeUpdate', check); - $rootScope.$on('$routeChangeStart', check); - }); + .run(internals.$initNavLinksDeepWatch); directivesProvider(chrome, internals); registerSubUrlHooks(kibana, internals); + configureAppAngularModule(kibana); uiModules.link(kibana); }; diff --git a/src/legacy/ui/public/chrome/api/breadcrumbs.ts b/src/legacy/ui/public/chrome/api/breadcrumbs.ts index e8995edf46d6d8f..87201d0e758c166 100644 --- a/src/legacy/ui/public/chrome/api/breadcrumbs.ts +++ b/src/legacy/ui/public/chrome/api/breadcrumbs.ts @@ -17,8 +17,6 @@ * under the License. */ -import { IRootScopeService } from 'angular'; -import { fatalError } from 'ui/notify/fatal_error'; import { ChromeBreadcrumb, ChromeSetup } from '../../../../../core/public'; export type Breadcrumb = ChromeBreadcrumb; @@ -34,16 +32,12 @@ export function __newPlatformInit__(instance: ChromeSetup) { } function createBreadcrumbsApi(chrome: { [key: string]: any }) { - // A flag used to determine if we should automatically - // clear the breadcrumbs between angular route changes. - let breadcrumbSetSinceRouteChange = false; let currentBreadcrumbs: Breadcrumb[] = []; // reset breadcrumbSetSinceRouteChange any time the breadcrumbs change, even // if it was done directly through the new platform newPlatformChrome.getBreadcrumbs$().subscribe({ next(nextBreadcrumbs) { - breadcrumbSetSinceRouteChange = true; currentBreadcrumbs = nextBreadcrumbs; }, }); @@ -79,39 +73,6 @@ function createBreadcrumbsApi(chrome: { [key: string]: any }) { newPlatformChrome.setBreadcrumbs(currentBreadcrumbs.filter(fn)); }, }, - - /** - * internal angular run function that will be called when angular bootstraps and - * lets us integrate with the angular router so that we can automatically clear - * the breadcrumbs if we switch to a Kibana app that does not use breadcrumbs correctly - */ - $setupBreadcrumbsAutoClear: ($rootScope: IRootScopeService, $injector: any) => { - const $route = $injector.has('$route') ? $injector.get('$route') : {}; - - $rootScope.$on('$routeChangeStart', () => { - breadcrumbSetSinceRouteChange = false; - }); - - $rootScope.$on('$routeChangeSuccess', () => { - const current = $route.current || {}; - - if (breadcrumbSetSinceRouteChange || (current.$$route && current.$$route.redirectTo)) { - return; - } - - const k7BreadcrumbsProvider = current.k7Breadcrumbs; - if (!k7BreadcrumbsProvider) { - newPlatformChrome.setBreadcrumbs([]); - return; - } - - try { - chrome.breadcrumbs.set($injector.invoke(k7BreadcrumbsProvider)); - } catch (error) { - fatalError(error); - } - }); - }, }; } @@ -119,7 +80,6 @@ export function initBreadcrumbsApi( chrome: { [key: string]: any }, internals: { [key: string]: any } ) { - const { breadcrumbs, $setupBreadcrumbsAutoClear } = createBreadcrumbsApi(chrome); + const { breadcrumbs } = createBreadcrumbsApi(chrome); chrome.breadcrumbs = breadcrumbs; - internals.$setupBreadcrumbsAutoClear = $setupBreadcrumbsAutoClear; } diff --git a/src/legacy/ui/public/chrome/api/help_extension.ts b/src/legacy/ui/public/chrome/api/help_extension.ts index a51df213c681ac8..2fafae89f2c9ea4 100644 --- a/src/legacy/ui/public/chrome/api/help_extension.ts +++ b/src/legacy/ui/public/chrome/api/help_extension.ts @@ -17,8 +17,6 @@ * under the License. */ -import { IRootScopeService } from 'angular'; - import { ChromeHelpExtension, ChromeSetup } from '../../../../../core/public'; let newPlatformChrome: ChromeSetup; @@ -34,17 +32,6 @@ export type HelpExtensionApi = ReturnType['helpEx export type HelpExtension = ChromeHelpExtension; function createHelpExtensionApi() { - /** - * reset helpExtensionSetSinceRouteChange any time the helpExtension changes, even - * if it was done directly through the new platform - */ - let helpExtensionSetSinceRouteChange = false; - newPlatformChrome.getHelpExtension$().subscribe({ - next() { - helpExtensionSetSinceRouteChange = true; - }, - }); - return { helpExtension: { /** @@ -60,30 +47,6 @@ function createHelpExtensionApi() { */ get$: () => newPlatformChrome.getHelpExtension$(), }, - - /** - * internal angular run function that will be called when angular bootstraps and - * lets us integrate with the angular router so that we can automatically clear - * the helpExtension if we switch to a Kibana app that does not set its own - * helpExtension - */ - $setupHelpExtensionAutoClear: ($rootScope: IRootScopeService, $injector: any) => { - const $route = $injector.has('$route') ? $injector.get('$route') : {}; - - $rootScope.$on('$routeChangeStart', () => { - helpExtensionSetSinceRouteChange = false; - }); - - $rootScope.$on('$routeChangeSuccess', () => { - const current = $route.current || {}; - - if (helpExtensionSetSinceRouteChange || (current.$$route && current.$$route.redirectTo)) { - return; - } - - newPlatformChrome.setHelpExtension(current.helpExtension); - }); - }, }; } @@ -91,7 +54,6 @@ export function initHelpExtensionApi( chrome: { [key: string]: any }, internal: { [key: string]: any } ) { - const { helpExtension, $setupHelpExtensionAutoClear } = createHelpExtensionApi(); + const { helpExtension } = createHelpExtensionApi(); chrome.helpExtension = helpExtension; - internal.$setupHelpExtensionAutoClear = $setupHelpExtensionAutoClear; } diff --git a/src/legacy/ui/public/chrome/api/loading_count.js b/src/legacy/ui/public/chrome/api/loading_count.js index 54b31bacc0ce19b..979993c76d5a5c8 100644 --- a/src/legacy/ui/public/chrome/api/loading_count.js +++ b/src/legacy/ui/public/chrome/api/loading_count.js @@ -19,8 +19,6 @@ import * as Rx from 'rxjs'; -import { isSystemApiRequest } from '../../system_api'; - let newPlatformHttp; export function __newPlatformInit__(instance) { @@ -30,27 +28,7 @@ export function __newPlatformInit__(instance) { newPlatformHttp = instance; } -export function initLoadingCountApi(chrome, internals) { - /** - * Injected into angular module by ui/chrome angular integration - * and adds a root-level watcher that will capture the count of - * active $http requests on each digest loop and expose the count to - * the core.loadingCount api - * @param {Angular.Scope} $rootScope - * @param {HttpService} $http - * @return {undefined} - */ - internals.capture$httpLoadingCount = function ($rootScope, $http) { - newPlatformHttp.addLoadingCount(new Rx.Observable(observer => { - const unwatch = $rootScope.$watch(() => { - const reqs = $http.pendingRequests || []; - observer.next(reqs.filter(req => !isSystemApiRequest(req)).length); - }); - - return unwatch; - })); - }; - +export function initLoadingCountApi(chrome) { const manualCount$ = new Rx.BehaviorSubject(0); newPlatformHttp.addLoadingCount(manualCount$); diff --git a/src/legacy/ui/public/chrome/directives/kbn_chrome.html b/src/legacy/ui/public/chrome/directives/kbn_chrome.html index c1cfcd77021d43b..e282a898ff0db38 100644 --- a/src/legacy/ui/public/chrome/directives/kbn_chrome.html +++ b/src/legacy/ui/public/chrome/directives/kbn_chrome.html @@ -18,7 +18,7 @@
diff --git a/src/legacy/ui/public/chrome/directives/kbn_chrome.js b/src/legacy/ui/public/chrome/directives/kbn_chrome.js index 682af0abc47d9a1..5bc3d8a28bac8ca 100644 --- a/src/legacy/ui/public/chrome/directives/kbn_chrome.js +++ b/src/legacy/ui/public/chrome/directives/kbn_chrome.js @@ -54,10 +54,14 @@ export function kbnChromeProvider(chrome, internals) { }, controllerAs: 'chrome', - controller($scope) { + controller($scope, $location) { // Notifications $scope.notifList = notify._notifs; + $scope.getFirstPathSegment = () => { + return $location.path().split('/')[1]; + }; + // Non-scope based code (e.g., React) // Banners diff --git a/src/legacy/ui/public/legacy_compat/angular_config.tsx b/src/legacy/ui/public/legacy_compat/angular_config.tsx new file mode 100644 index 000000000000000..27f06d435c52335 --- /dev/null +++ b/src/legacy/ui/public/legacy_compat/angular_config.tsx @@ -0,0 +1,243 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React, { Fragment } from 'react'; +import * as Rx from 'rxjs'; + +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import { IHttpService, ILocationProvider, ILocationService, IRootScopeService } from 'angular'; +import { CoreSetup } from 'kibana/public'; +import { fatalError } from 'ui/notify'; +// @ts-ignore +import { modifyUrl } from 'ui/url'; +// @ts-ignore +import { UrlOverflowServiceProvider } from '../error_url_overflow'; +import { getNewPlatform } from '../new_platform'; +import { toastNotifications } from '../notify'; +// @ts-ignore +import { isSystemApiRequest } from '../system_api'; + +const URL_LIMIT_WARN_WITHIN = 1000; + +export const configureAppAngularModule = (angularModule: any) => { + const newPlatform = getNewPlatform().setup.core; + + angularModule + .config(setupLocationProvider(newPlatform)) + .value('sessionId', Date.now()) + .value('esUrl', getEsUrl(newPlatform)) + .run(capture$httpLoadingCount(newPlatform)) + .run($setupBreadcrumbsAutoClear(newPlatform)) + .run($setupHelpExtensionAutoClear(newPlatform)) + .run($setupUrlOverflowHandling(newPlatform)); +}; + +const setupLocationProvider = (newPlatform: CoreSetup) => ( + $locationProvider: ILocationProvider +) => { + $locationProvider.html5Mode({ + enabled: false, + requireBase: false, + rewriteLinks: false, + }); + + $locationProvider.hashPrefix(''); +}; + +const getEsUrl = (newPlatform: CoreSetup) => { + const a = document.createElement('a'); + a.href = newPlatform.basePath.addToPath('/elasticsearch'); + const protocolPort = /https/.test(a.protocol) ? 443 : 80; + const port = a.port || protocolPort; + return { + host: a.hostname, + port, + protocol: a.protocol, + pathname: a.pathname, + }; +}; + +/** + * Injected into angular module by ui/chrome angular integration + * and adds a root-level watcher that will capture the count of + * active $http requests on each digest loop and expose the count to + * the core.loadingCount api + * @param {Angular.Scope} $rootScope + * @param {HttpService} $http + * @return {undefined} + */ +const capture$httpLoadingCount = (newPlatform: CoreSetup) => ( + $rootScope: IRootScopeService, + $http: IHttpService +) => { + newPlatform.http.addLoadingCount( + new Rx.Observable(observer => { + const unwatch = $rootScope.$watch(() => { + const reqs = $http.pendingRequests || []; + observer.next(reqs.filter(req => !isSystemApiRequest(req)).length); + }); + + return unwatch; + }) + ); +}; + +/** + * internal angular run function that will be called when angular bootstraps and + * lets us integrate with the angular router so that we can automatically clear + * the breadcrumbs if we switch to a Kibana app that does not use breadcrumbs correctly + */ +const $setupBreadcrumbsAutoClear = (newPlatform: CoreSetup) => ( + $rootScope: IRootScopeService, + $injector: any +) => { + // A flag used to determine if we should automatically + // clear the breadcrumbs between angular route changes. + let breadcrumbSetSinceRouteChange = false; + const $route = $injector.has('$route') ? $injector.get('$route') : {}; + + // reset breadcrumbSetSinceRouteChange any time the breadcrumbs change, even + // if it was done directly through the new platform + newPlatform.chrome.getBreadcrumbs$().subscribe({ + next() { + breadcrumbSetSinceRouteChange = true; + }, + }); + + $rootScope.$on('$routeChangeStart', () => { + breadcrumbSetSinceRouteChange = false; + }); + + $rootScope.$on('$routeChangeSuccess', () => { + const current = $route.current || {}; + + if (breadcrumbSetSinceRouteChange || (current.$$route && current.$$route.redirectTo)) { + return; + } + + const k7BreadcrumbsProvider = current.k7Breadcrumbs; + if (!k7BreadcrumbsProvider) { + newPlatform.chrome.setBreadcrumbs([]); + return; + } + + try { + newPlatform.chrome.setBreadcrumbs($injector.invoke(k7BreadcrumbsProvider)); + } catch (error) { + fatalError(error); + } + }); +}; + +/** + * internal angular run function that will be called when angular bootstraps and + * lets us integrate with the angular router so that we can automatically clear + * the helpExtension if we switch to a Kibana app that does not set its own + * helpExtension + */ +const $setupHelpExtensionAutoClear = (newPlatform: CoreSetup) => ( + $rootScope: IRootScopeService, + $injector: any +) => { + /** + * reset helpExtensionSetSinceRouteChange any time the helpExtension changes, even + * if it was done directly through the new platform + */ + let helpExtensionSetSinceRouteChange = false; + newPlatform.chrome.getHelpExtension$().subscribe({ + next() { + helpExtensionSetSinceRouteChange = true; + }, + }); + + const $route = $injector.has('$route') ? $injector.get('$route') : {}; + + $rootScope.$on('$routeChangeStart', () => { + helpExtensionSetSinceRouteChange = false; + }); + + $rootScope.$on('$routeChangeSuccess', () => { + const current = $route.current || {}; + + if (helpExtensionSetSinceRouteChange || (current.$$route && current.$$route.redirectTo)) { + return; + } + + newPlatform.chrome.setHelpExtension(current.helpExtension); + }); +}; + +const $setupUrlOverflowHandling = (newPlatform: CoreSetup) => ( + $location: ILocationService, + $rootScope: IRootScopeService, + Private: any, + config: any +) => { + const urlOverflow = Private(UrlOverflowServiceProvider); + const check = () => { + // disable long url checks when storing state in session storage + if (config.get('state:storeInSessionStorage')) { + return; + } + + if ($location.path() === '/error/url-overflow') { + return; + } + + try { + if (urlOverflow.check($location.absUrl()) <= URL_LIMIT_WARN_WITHIN) { + toastNotifications.addWarning({ + title: i18n.translate('common.ui.chrome.bigUrlWarningNotificationTitle', { + defaultMessage: 'The URL is big and Kibana might stop working', + }), + text: ( + + state:storeInSessionStorage, + advancedSettingsLink: ( + + + + ), + }} + /> + + ), + }); + } + } catch (e) { + window.location.href = modifyUrl(window.location.href, (parts: any) => { + parts.hash = '#/error/url-overflow'; + }); + // force the browser to reload to that Kibana's potentially unstable state is unloaded + window.location.reload(); + } + }; + + $rootScope.$on('$routeUpdate', check); + $rootScope.$on('$routeChangeStart', check); +}; diff --git a/src/legacy/ui/public/legacy_compat/index.ts b/src/legacy/ui/public/legacy_compat/index.ts new file mode 100644 index 000000000000000..b29056954051baf --- /dev/null +++ b/src/legacy/ui/public/legacy_compat/index.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { configureAppAngularModule } from './angular_config';