`;
+}
+
+const app = uiModules.get('kibana');
+
+app.directive('injectedItems', function ($injector) {
+ const $compile = $injector.get('$compile');
+
+ return {
+ restrict: 'E',
+ replace: true,
+ template: template,
+ scope: {
+ items: '='
+ },
+ link: ($scope, $el) => {
+ const items = $scope.items;
+
+ if (isArray(items) && items.length > 0) {
+ items.forEach(item => {
+ // Compile itemHtml with current $scope and append it into the container DOM element.
+ // We do this because we want to dynamically inject content (strings) into the DOM. This content
+ // may contain Angular directives so it must first be $compiled with the current $scope.
+ const itemHtml = $compile(makeAngularParseableExpression(item))($scope);
+ angular.element($el).append(itemHtml);
+ });
+ }
+ }
+ };
+});
diff --git a/src/core_plugins/getting_started/public/components/injected_items/injected_items.less b/src/core_plugins/getting_started/public/components/injected_items/injected_items.less
new file mode 100644
index 0000000000000..3102c5057d6fa
--- /dev/null
+++ b/src/core_plugins/getting_started/public/components/injected_items/injected_items.less
@@ -0,0 +1,3 @@
+.injectedItems__item {
+ display: inline;
+}
diff --git a/src/core_plugins/getting_started/public/getting_started_route.html b/src/core_plugins/getting_started/public/getting_started_route.html
new file mode 100644
index 0000000000000..870a1824b3790
--- /dev/null
+++ b/src/core_plugins/getting_started/public/getting_started_route.html
@@ -0,0 +1 @@
+
diff --git a/src/core_plugins/getting_started/public/getting_started_route.js b/src/core_plugins/getting_started/public/getting_started_route.js
new file mode 100644
index 0000000000000..46db8edf18ee8
--- /dev/null
+++ b/src/core_plugins/getting_started/public/getting_started_route.js
@@ -0,0 +1,14 @@
+import routes from 'ui/routes';
+import template from './getting_started_route.html';
+import './components/getting_started';
+import { GETTING_STARTED_ROUTE } from './lib/constants';
+
+routes
+.when(GETTING_STARTED_ROUTE, {
+ template: template,
+ controllerAs: 'gettingStartedRoute',
+ controller: class GettingStartedRouteController {
+ constructor() {
+ }
+ }
+});
diff --git a/src/core_plugins/getting_started/public/index.js b/src/core_plugins/getting_started/public/index.js
new file mode 100644
index 0000000000000..7ae2fd452e941
--- /dev/null
+++ b/src/core_plugins/getting_started/public/index.js
@@ -0,0 +1,3 @@
+import './lib/add_setup_work';
+import './lib/register_management_section';
+import './getting_started_route';
diff --git a/src/core_plugins/getting_started/public/lib/add_setup_work.js b/src/core_plugins/getting_started/public/lib/add_setup_work.js
new file mode 100644
index 0000000000000..028fe8e6f8159
--- /dev/null
+++ b/src/core_plugins/getting_started/public/lib/add_setup_work.js
@@ -0,0 +1,81 @@
+import { get } from 'lodash';
+import uiRoutes from 'ui/routes';
+import uiChrome from 'ui/chrome';
+import KbnUrlProvider from 'ui/url';
+import { Notifier } from 'ui/notify/notifier';
+import { IndexPatternsGetIdsProvider } from 'ui/index_patterns/_get_ids';
+import { hasOptedOutOfGettingStarted, optOutOfGettingStarted } from 'ui/getting_started/opt_out_helpers';
+
+import {
+ GETTING_STARTED_ROUTE,
+ CREATE_INDEX_PATTERN_ROUTE
+} from './constants';
+
+uiRoutes
+ .addSetupWork(function gettingStartedGateCheck(Private, $injector) {
+ const getIds = Private(IndexPatternsGetIdsProvider);
+ const kbnUrl = Private(KbnUrlProvider);
+ const config = $injector.get('config');
+ const $route = $injector.get('$route');
+
+ const currentRoute = get($route, 'current.$$route');
+
+ return getIds()
+ .then(indexPatterns => {
+ const indexPatternsExist = Array.isArray(indexPatterns) && indexPatterns.length > 0;
+ const isOnGettingStartedPage = get(currentRoute, 'originalPath') === GETTING_STARTED_ROUTE;
+
+ if (indexPatternsExist) {
+
+ // The user need not see the Getting Started page, so opt them out of it
+ optOutOfGettingStarted();
+
+ // Some routes require a default index pattern to be present. If we're
+ // NOT on such a route, there's nothing more to do; send the user on their way
+ if (!currentRoute.requireDefaultIndex) {
+ return;
+ }
+
+ // Otherwise, check if we have a default index pattern
+ let defaultIndexPattern = config.get('defaultIndex');
+
+ // If we don't have an default index pattern, make the first index pattern the
+ // default one
+ if (!Boolean(defaultIndexPattern)) {
+ defaultIndexPattern = indexPatterns[0];
+ config.set('defaultIndex', defaultIndexPattern);
+ }
+
+ // At this point, we have a default index pattern and are all set!
+ return;
+ }
+
+ // At this point, no index patterns exist.
+
+ // If the user has explicitly opted out of the Getting Started page
+ if (hasOptedOutOfGettingStarted()) {
+
+ // Some routes require a default index pattern to be present. If we're
+ // NOT on such a route, there's nothing more to do; send the user on their way
+ if (!currentRoute.requireDefaultIndex) {
+ return;
+ }
+
+ // Otherwise, redirect the user to the index pattern creation page with
+ // a notification about creating an index pattern
+ const notify = new Notifier({
+ location: 'Index Patterns'
+ });
+ notify.error('Please create a new index pattern');
+ kbnUrl.change(CREATE_INDEX_PATTERN_ROUTE);
+ return;
+ }
+
+ // Redirect the user to the Getting Started page (unless they are on it already)
+ if (!isOnGettingStartedPage) {
+ uiChrome.setVisible(false);
+ kbnUrl.change(GETTING_STARTED_ROUTE);
+ return;
+ }
+ });
+ });
\ No newline at end of file
diff --git a/src/core_plugins/getting_started/public/lib/constants.js b/src/core_plugins/getting_started/public/lib/constants.js
new file mode 100644
index 0000000000000..2764ef2570208
--- /dev/null
+++ b/src/core_plugins/getting_started/public/lib/constants.js
@@ -0,0 +1,2 @@
+export const GETTING_STARTED_ROUTE = '/management/kibana/getting_started';
+export const CREATE_INDEX_PATTERN_ROUTE = '/management/kibana/index';
\ No newline at end of file
diff --git a/src/core_plugins/getting_started/public/lib/register_management_section.js b/src/core_plugins/getting_started/public/lib/register_management_section.js
new file mode 100644
index 0000000000000..f7759164ac16a
--- /dev/null
+++ b/src/core_plugins/getting_started/public/lib/register_management_section.js
@@ -0,0 +1,8 @@
+import { management } from 'ui/management';
+import { GETTING_STARTED_ROUTE } from './constants';
+
+management.getSection('kibana').register('getting_started', {
+ display: 'Getting Started',
+ order: 50,
+ url: `#${GETTING_STARTED_ROUTE}`
+});
diff --git a/src/core_plugins/kibana/public/management/index.js b/src/core_plugins/kibana/public/management/index.js
index 391d70fb9193b..91bd1dc7eede5 100644
--- a/src/core_plugins/kibana/public/management/index.js
+++ b/src/core_plugins/kibana/public/management/index.js
@@ -19,10 +19,6 @@ uiRoutes
redirectTo: '/management'
});
-require('ui/index_patterns/route_setup/load_default')({
- whenMissingRedirectTo: '/management/kibana/index'
-});
-
uiModules
.get('apps/management')
.directive('kbnManagementApp', function (Private, $location, timefilter, buildNum, buildSha) {
diff --git a/src/core_plugins/testbed/public/components/blahblah/blahblah.html b/src/core_plugins/testbed/public/components/blahblah/blahblah.html
new file mode 100644
index 0000000000000..6991d208d24c3
--- /dev/null
+++ b/src/core_plugins/testbed/public/components/blahblah/blahblah.html
@@ -0,0 +1,3 @@
+
+ You have an important decision to make. To do so, .
+
diff --git a/src/core_plugins/testbed/public/components/blahblah/blahblah.js b/src/core_plugins/testbed/public/components/blahblah/blahblah.js
new file mode 100644
index 0000000000000..5a7db76c444ed
--- /dev/null
+++ b/src/core_plugins/testbed/public/components/blahblah/blahblah.js
@@ -0,0 +1,19 @@
+import { uiModules } from 'ui/modules';
+import template from './blahblah.html';
+
+const app = uiModules.get('kibana');
+
+app.directive('blahblah', function () {
+ return {
+ restrict: 'E',
+ replace: true,
+ template: template,
+ scope: {},
+ controller: ($scope) => {
+ $scope.testClick = () => {
+ alert(`I'm doing something important!`);
+ console.log($scope);
+ };
+ }
+ };
+});
diff --git a/src/core_plugins/testbed/public/components/blahblah/index.js b/src/core_plugins/testbed/public/components/blahblah/index.js
new file mode 100644
index 0000000000000..b560cb1a0cbe4
--- /dev/null
+++ b/src/core_plugins/testbed/public/components/blahblah/index.js
@@ -0,0 +1 @@
+import './blahblah';
diff --git a/src/core_plugins/testbed/public/index.js b/src/core_plugins/testbed/public/index.js
index 1732c655ad73c..67a8d0dc21d2b 100644
--- a/src/core_plugins/testbed/public/index.js
+++ b/src/core_plugins/testbed/public/index.js
@@ -1 +1,25 @@
-import './testbed';
\ No newline at end of file
+import './testbed';
+
+
+import './components/blahblah';
+import { GettingStartedRegistryProvider } from 'ui/getting_started/registry';
+import { GETTING_STARTED_REGISTRY_TYPES } from 'ui/getting_started/constants';
+
+GettingStartedRegistryProvider.register(() => ({
+ type: GETTING_STARTED_REGISTRY_TYPES.TOP_MESSAGE,
+ template: ``
+}));
+GettingStartedRegistryProvider.register(() => ({
+ type: GETTING_STARTED_REGISTRY_TYPES.TOP_MESSAGE,
+ template: `Nam hendrerit augue id egestas ultricies.`
+}));
+
+GettingStartedRegistryProvider.register(() => ({
+ type: GETTING_STARTED_REGISTRY_TYPES.MANAGE_AND_MONITOR_MESSAGE,
+ template: `Lorem ipsum dolor sit amet, consectetur consectetur adipiscing elit.`
+}));
+
+GettingStartedRegistryProvider.register(() => ({
+ type: GETTING_STARTED_REGISTRY_TYPES.MANAGE_AND_MONITOR_MESSAGE,
+ template: `Nam luctus mattis urna, ac fringilla tellus efficitur at.`
+}));
diff --git a/src/ui/public/documentation_links/documentation_links.js b/src/ui/public/documentation_links/documentation_links.js
index 2b49f38e9cdf0..dbb7038f4ef7b 100644
--- a/src/ui/public/documentation_links/documentation_links.js
+++ b/src/ui/public/documentation_links/documentation_links.js
@@ -7,6 +7,18 @@ const urlVersion = `${major}.${minor}`;
const baseUrl = 'https://www.elastic.co/';
export const documentationLinks = {
+ elasticsearch: {
+ docs: `${baseUrl}guide/en/elasticsearch/reference/current`
+ },
+ beats: {
+ docs: `${baseUrl}guide/en/beats/libbeat/current`
+ },
+ logstash: {
+ docs: `${baseUrl}guide/en/logstash/current`
+ },
+ kibana: {
+ docs: `${baseUrl}guide/en/kibana/current`
+ },
filebeat: {
installation: `${baseUrl}guide/en/beats/filebeat/${urlVersion}/filebeat-installation.html`,
configuration: `${baseUrl}guide/en/beats/filebeat/${urlVersion}/filebeat-configuration.html`,
@@ -25,5 +37,7 @@ export const documentationLinks = {
},
query: {
luceneQuerySyntax: `${baseUrl}guide/en/elasticsearch/reference/${urlVersion}/query-dsl-query-string-query.html#query-string-syntax`
- }
+ },
+ demoSite: 'http://demo.elastic.co',
+ gettingStarted: `${baseUrl}products/kibana/getting-started-link`
};
diff --git a/src/ui/public/getting_started/constants.js b/src/ui/public/getting_started/constants.js
new file mode 100644
index 0000000000000..a1b839d00ed67
--- /dev/null
+++ b/src/ui/public/getting_started/constants.js
@@ -0,0 +1,6 @@
+export const GETTING_STARTED_OPT_OUT_FLAG = 'kibana.isGettingStartedOptedOut';
+
+export const GETTING_STARTED_REGISTRY_TYPES = {
+ TOP_MESSAGE: 'topMessage',
+ MANAGE_AND_MONITOR_MESSAGE: 'monitorAndManageMessage'
+};
\ No newline at end of file
diff --git a/src/ui/public/getting_started/opt_out_directive.js b/src/ui/public/getting_started/opt_out_directive.js
new file mode 100644
index 0000000000000..2b5db91839300
--- /dev/null
+++ b/src/ui/public/getting_started/opt_out_directive.js
@@ -0,0 +1,14 @@
+import { uiModules } from 'ui/modules';
+import { optOutOfGettingStarted } from './opt_out_helpers';
+
+const app = uiModules.get('kibana');
+app.directive('kbnGettingStartedOptOut', () => {
+ return {
+ restrict: 'A',
+ link: (scope, element) => {
+ element.on('click', () => {
+ optOutOfGettingStarted();
+ });
+ }
+ };
+});
diff --git a/src/ui/public/getting_started/opt_out_helpers.js b/src/ui/public/getting_started/opt_out_helpers.js
new file mode 100644
index 0000000000000..e6cd8320f0ffe
--- /dev/null
+++ b/src/ui/public/getting_started/opt_out_helpers.js
@@ -0,0 +1,11 @@
+import uiChrome from 'ui/chrome';
+import { GETTING_STARTED_OPT_OUT_FLAG } from './constants';
+
+export function hasOptedOutOfGettingStarted() {
+ return window.localStorage.getItem(GETTING_STARTED_OPT_OUT_FLAG) || false;
+}
+
+export function optOutOfGettingStarted() {
+ window.localStorage.setItem(GETTING_STARTED_OPT_OUT_FLAG, true);
+ uiChrome.setVisible(true);
+}
\ No newline at end of file
diff --git a/src/ui/public/getting_started/registry.js b/src/ui/public/getting_started/registry.js
new file mode 100644
index 0000000000000..f7cb2ce174749
--- /dev/null
+++ b/src/ui/public/getting_started/registry.js
@@ -0,0 +1,18 @@
+import { uiRegistry } from 'ui/registry/_registry';
+
+export const GettingStartedRegistryProvider = uiRegistry({
+ name: 'gettingStartedTopMessages',
+ group: [ 'type' ]
+});
+
+/**
+ * Usage:
+ *
+ * import { GettingStartedRegistryProvider } from 'ui/getting_started/registry';
+ * import { GETTING_STARTED_REGISTRY_TYPES } from 'ui/getting_started/constants';
+ *
+ * GettingStartedRegistryProvider.register(($injector, Private, someOtherService, ...) => ({
+ * type: GETTING_STARTED_REGISTRY_TYPES.TOP_MESSAGE,
+ * template: 'plain text | html markup | markup with directives'
+ * }));
+ */
diff --git a/src/ui/public/images/icon-dashboard.svg b/src/ui/public/images/icon-dashboard.svg
new file mode 100644
index 0000000000000..d48ff0aa753dd
--- /dev/null
+++ b/src/ui/public/images/icon-dashboard.svg
@@ -0,0 +1,8 @@
+
diff --git a/src/ui/public/images/icon-shield.svg b/src/ui/public/images/icon-shield.svg
new file mode 100644
index 0000000000000..98a9fa0bdd82c
--- /dev/null
+++ b/src/ui/public/images/icon-shield.svg
@@ -0,0 +1,14 @@
+
+
\ No newline at end of file
diff --git a/src/ui/public/images/logo-beats-small.svg b/src/ui/public/images/logo-beats-small.svg
new file mode 100644
index 0000000000000..b7666b0238dfb
--- /dev/null
+++ b/src/ui/public/images/logo-beats-small.svg
@@ -0,0 +1,23 @@
+
+
+
diff --git a/src/ui/public/images/logo-kibana-small.svg b/src/ui/public/images/logo-kibana-small.svg
new file mode 100644
index 0000000000000..094ff605c0fc5
--- /dev/null
+++ b/src/ui/public/images/logo-kibana-small.svg
@@ -0,0 +1,20 @@
+
+
+
diff --git a/src/ui/public/images/logo-logstash-small.svg b/src/ui/public/images/logo-logstash-small.svg
new file mode 100644
index 0000000000000..5a4f62ad02195
--- /dev/null
+++ b/src/ui/public/images/logo-logstash-small.svg
@@ -0,0 +1,20 @@
+
+
+
diff --git a/src/ui/public/index_patterns/route_setup/load_default.js b/src/ui/public/index_patterns/route_setup/load_default.js
deleted file mode 100644
index 715593144e845..0000000000000
--- a/src/ui/public/index_patterns/route_setup/load_default.js
+++ /dev/null
@@ -1,58 +0,0 @@
-import _ from 'lodash';
-import { Notifier } from 'ui/notify/notifier';
-import { NoDefaultIndexPattern } from 'ui/errors';
-import { IndexPatternsGetIdsProvider } from '../_get_ids';
-import uiRoutes from 'ui/routes';
-const notify = new Notifier({
- location: 'Index Patterns'
-});
-
-module.exports = function (opts) {
- opts = opts || {};
- const whenMissingRedirectTo = opts.whenMissingRedirectTo || null;
- let defaultRequiredToasts = null;
-
- uiRoutes
- .addSetupWork(function loadDefaultIndexPattern(Private, Promise, $route, config) {
- const getIds = Private(IndexPatternsGetIdsProvider);
- const route = _.get($route, 'current.$$route');
-
- return getIds()
- .then(function (patterns) {
- let defaultId = config.get('defaultIndex');
- let defined = !!defaultId;
- const exists = _.contains(patterns, defaultId);
-
- if (defined && !exists) {
- config.remove('defaultIndex');
- defaultId = defined = false;
- }
-
- if (!defined && route.requireDefaultIndex) {
- // If there is only one index pattern, set it as default
- if (patterns.length === 1) {
- defaultId = patterns[0];
- config.set('defaultIndex', defaultId);
- } else {
- throw new NoDefaultIndexPattern();
- }
- }
- });
- })
- .afterWork(
- // success
- null,
-
- // failure
- function (err, kbnUrl) {
- const hasDefault = !(err instanceof NoDefaultIndexPattern);
- if (hasDefault || !whenMissingRedirectTo) throw err; // rethrow
-
- kbnUrl.change(whenMissingRedirectTo);
- if (!defaultRequiredToasts) defaultRequiredToasts = [];
- else defaultRequiredToasts.push(notify.error(err));
- }
- );
-
-
-};
diff --git a/test/functional/config.js b/test/functional/config.js
index 050a8d4d0c3ce..f8a07dcc017a4 100644
--- a/test/functional/config.js
+++ b/test/functional/config.js
@@ -12,6 +12,7 @@ import {
SettingsPageProvider,
MonitoringPageProvider,
PointSeriesPageProvider,
+ GettingStartedPageProvider
} from './page_objects';
import {
@@ -52,6 +53,7 @@ export default function () {
settings: SettingsPageProvider,
monitoring: MonitoringPageProvider,
pointSeries: PointSeriesPageProvider,
+ gettingStarted: GettingStartedPageProvider,
},
services: {
kibanaServer: KibanaServerProvider,
diff --git a/test/functional/page_objects/getting_started_page.js b/test/functional/page_objects/getting_started_page.js
new file mode 100644
index 0000000000000..7505718a33b93
--- /dev/null
+++ b/test/functional/page_objects/getting_started_page.js
@@ -0,0 +1,14 @@
+export function GettingStartedPageProvider({ getService }) {
+
+ const log = getService('log');
+ const testSubjects = getService('testSubjects');
+
+ class GettingStartedPage {
+ async clickOptOutLink() {
+ log.debug('Clicking opt-out link');
+ await testSubjects.click('lnkGettingStartedOptOut');
+ }
+ }
+
+ return new GettingStartedPage();
+}
\ No newline at end of file
diff --git a/test/functional/page_objects/index.js b/test/functional/page_objects/index.js
index ea811c354d8fd..c7cdb817c3bcb 100644
--- a/test/functional/page_objects/index.js
+++ b/test/functional/page_objects/index.js
@@ -9,3 +9,4 @@ export { VisualizePageProvider } from './visualize_page';
export { SettingsPageProvider } from './settings_page';
export { MonitoringPageProvider } from './monitoring_page';
export { PointSeriesPageProvider } from './point_series_page';
+export { GettingStartedPageProvider } from './getting_started_page';