Merge branch 'master' into feature/custom-toaster-banner

This commit is contained in:
Nicolás Bevacqua 2016-06-16 14:32:39 -03:00
commit f36cb6524a
288 changed files with 7169 additions and 5176 deletions

36
.github/ISSUE_TEMPLATE.md vendored Normal file
View file

@ -0,0 +1,36 @@
<!--
GitHub is reserved for bug reports and feature requests. The best place
to ask a general question is at the Elastic Discourse forums at
https://discuss.elastic.co. If you are in fact posting a bug report or
a feature request, please include one and only one of the below blocks
in your new issue.
-->
<!--
If you are filing a bug report, please remove the below feature
request block and provide responses for all of the below items.
-->
**Kibana version**:
**OS version**:
**Original install method (e.g. download page, yum, from source, etc.)**:
**Description of the problem including expected versus actual behavior**:
**Steps to reproduce**:
1.
2.
3.
**Errors in browser console (if relevant)**:
**Provide logs and/or server output (if relevant)**:
<!--
If you are filing a feature request, please remove the above bug
report block and provide responses for all of the below items.
-->
**Describe the feature**:

13
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View file

@ -0,0 +1,13 @@
<!--
Thank you for your interest in and contributing to Kibana! There
are a few simple things to check before submitting your pull request
that can help with the review process. You should delete these items
from your submission, but they are here to help bring them to your
attention.
-->
- Have you signed the [contributor license agreement](https://www.elastic.co/contributor-agreement)?
- Have you followed the [contributor guidelines](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md)?
- If submitting code, have you included unit tests that cover the changes?
- If submitting code, have you tested and built your code locally prior to submission with `npm test && npm run build`?
- If submitting code, is your pull request against master? Unless there is a good reason otherwise, we prefer pull requests against master and will backport as needed.

5
.gitignore vendored
View file

@ -12,7 +12,10 @@ target
.idea
*.iml
*.log
/test/output
/test/screenshots/diff
/test/screenshots/failure
/test/screenshots/session
/test/screenshots/visual_regression_gallery.html
/esvm
.htpasswd
.eslintcache

View file

@ -113,7 +113,7 @@ Once that is complete just run:
```
sh
npm run test && npm run build
npm run test && npm run build -- --skip-os-packages
```
#### Debugging unit tests
@ -121,27 +121,27 @@ npm run test && npm run build
The standard `npm run test` task runs several sub tasks and can take several minutes to complete, making debugging failures pretty painful. In order to ease the pain specialized tasks provide alternate methods for running the tests.
`npm run test:quick`
`npm run test:quick`
Runs both server and browser tests, but skips linting
`npm run test:server`
`npm run test:server`
Run only the server tests
`npm run test:browser`
`npm run test:browser`
Run only the browser tests. Coverage reports are available for browser tests by running `npm run test:coverage`. You can find the results under the `coverage/` directory that will be created upon completion.
`npm run test:dev`
Initializes an environment for debugging the browser tests. Includes an dedicated instance of the kibana server for building the test bundle, and a karma server. When running this task the build is optimized for the first time and then a karma-owned instance of the browser is opened. Click the "debug" button to open a new tab that executes the unit tests.
`npm run test:dev`
Initializes an environment for debugging the browser tests. Includes an dedicated instance of the kibana server for building the test bundle, and a karma server. When running this task the build is optimized for the first time and then a karma-owned instance of the browser is opened. Click the "debug" button to open a new tab that executes the unit tests.
![Browser test debugging](http://i.imgur.com/DwHxgfq.png)
`npm run mocha [test file or dir]` or `npm run mocha:debug [test file or dir]`
`npm run mocha [test file or dir]` or `npm run mocha:debug [test file or dir]`
Run a one off test with the local project version of mocha, babel compilation, and optional debugging. Great
for development and fixing individual tests.
#### Unit testing plugins
This should work super if you're using the [Kibana plugin generator](https://github.com/elastic/generator-kibana-plugin). If you're not using the generator, well, you're on your own. We suggest you look at how the generator works.
`npm run test:dev -- --kbnServer.testsBundle.pluginId=some_special_plugin --kbnServer.plugin-path=../some_special_plugin`
`npm run test:dev -- --kbnServer.testsBundle.pluginId=some_special_plugin --kbnServer.plugin-path=../some_special_plugin`
Run the tests for just your particular plugin. Assuming you plugin lives outside of the `installedPlugins directory`, which it should.
#### Running browser automation tests:
@ -151,13 +151,13 @@ Run the tests for just your particular plugin. Assuming you plugin lives outside
The following will start Kibana, Elasticsearch and Selenium for you. To run the functional UI tests use the following commands
`npm run test:ui`
`npm run test:ui`
Run the functional UI tests one time and exit. This is used by the CI systems and is great for quickly checking that things pass. It is essentially a combination of the next two tasks.
`npm run test:ui:server`
`npm run test:ui:server`
Start the server required for the `test:ui:runner` tasks. Once the server is started `test:ui:runner` can be run multiple times without waiting for the server to start.
`npm run test:ui:runner`
`npm run test:ui:runner`
Execute the front-end selenium tests. This requires the server started by the `test:ui:server` task.
##### If you already have ElasticSearch, Kibana, and Selenium Server running:
@ -184,15 +184,15 @@ npm run test:ui:runner
Packages are built using fpm, pleaserun, dpkg, and rpm. fpm and pleaserun can be installed using gem. Package building has only been tested on Linux and is not supported on any other platform.
```sh
gem install pleaserun
apt-get install ruby-dev
gem install fpm
npm run build:ospackages
apt-get install ruby-dev rpm
gem install fpm -v 1.5.0 # required by pleaserun 0.0.16
gem install pleaserun -v 0.0.16 # higher versions fail at the moment
npm run build -- --skip-archives
```
To specify a package to build you can add `rpm` or `deb` as an argument.
```sh
npm run build:ospackages -- --rpm
npm run build -- --rpm
```
Distributable packages can be found in `target/` after the build completes.

View file

@ -9,7 +9,7 @@ module.exports = function (grunt) {
pkg: grunt.file.readJSON('package.json'),
root: __dirname,
src: __dirname + '/src',
build: __dirname + '/build', // temporary build directory
buildDir: __dirname + '/build', // temporary build directory
plugins: __dirname + '/src/plugins',
server: __dirname + '/src/server',
target: __dirname + '/target', // location of the compressed build targets
@ -69,7 +69,10 @@ module.exports = function (grunt) {
grunt.config.merge(config);
config.userScriptsDir = __dirname + '/build/userScripts';
// must run before even services/platforms
grunt.config.set('build', require('./tasks/config/build')(grunt));
config.packageScriptsDir = __dirname + '/tasks/build/package_scripts';
// ensure that these run first, other configs need them
config.services = require('./tasks/config/services')(grunt);
config.platforms = require('./tasks/config/platforms')(grunt);

View file

@ -1,4 +1,4 @@
# Kibana 5.0.0-snapshot
# Kibana 5.0.0
Kibana is an open source ([Apache Licensed](https://github.com/elastic/kibana/blob/master/LICENSE.md)), browser based analytics and search dashboard for Elasticsearch. Kibana is a snap to setup and start using. Kibana strives to be easy to get started with, while also being flexible and powerful, just like Elasticsearch.
@ -41,9 +41,9 @@ Visit [Elastic.co](http://www.elastic.co/guide/en/kibana/current/index.html) for
For the daring, snapshot builds are available. These builds are created after each commit to the master branch, and therefore are not something you should run in production.
| platform | | | | |
| --- | --- | --- | --- | --- |
| OSX | [tar](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-snapshot-darwin-x64.tar.gz) | [zip](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-snapshot-darwin-x64.zip) | | |
| Linux x64 | [tar](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-snapshot-linux-x64.tar.gz) | [zip](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-snapshot-linux-x64.zip) | [deb](https://download.elastic.co/kibana/kibana-snapshot/kibana_5.0.0-snapshot_amd64.deb)| [rpm](https://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0_snapshot-1.x86_64.rpm) |
| Linux x86 | [tar](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-snapshot-linux-x86.tar.gz) | [zip](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-snapshot-linux-x86.zip) | [deb](https://download.elastic.co/kibana/kibana-snapshot/kibana_5.0.0-snapshot_i386.deb) | [rpm](https://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0_snapshot-1.i386.rpm) |
| Windows | [tar](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-snapshot-windows.tar.gz) | [zip](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-snapshot-windows.zip) | | |
| platform | |
| --- | --- |
| OSX | [tar](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-SNAPSHOT-darwin-x64.tar.gz) |
| Linux x64 | [tar](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-SNAPSHOT-linux-x64.tar.gz) [deb](https://download.elastic.co/kibana/kibana-snapshot/kibana_5.0.0-SNAPSHOT_amd64.deb) [rpm](https://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0_SNAPSHOT-1.x86_64.rpm) |
| Linux x86 | [tar](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-SNAPSHOT-linux-x86.tar.gz) [deb](https://download.elastic.co/kibana/kibana-snapshot/kibana_5.0.0-SNAPSHOT_i386.deb) [rpm](https://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0_SNAPSHOT-1.i386.rpm) |
| Windows | [zip](http://download.elastic.co/kibana/kibana-snapshot/kibana-5.0.0-SNAPSHOT-windows.zip) |

View file

@ -1,767 +1,9 @@
This is a collection of style guides for Kibana projects. The include guides for the following:
- [JavaScript](#javascript-style-guide)
- [Kibana Project](#kibana-style-guide)
- [Html](#html-style-guide)
# JavaScript Style Guide
## 2 Spaces for indention
Use 2 spaces for indenting your code and swear an oath to never mix tabs and
spaces - a special kind of hell is awaiting you otherwise.
## Newlines
Use UNIX-style newlines (`\n`), and a newline character as the last character
of a file. Windows-style newlines (`\r\n`) are forbidden inside any repository.
## No trailing whitespace
Just like you brush your teeth after every meal, you clean up any trailing
whitespace in your JS files before committing. Otherwise the rotten smell of
careless neglect will eventually drive away contributors and/or co-workers.
## Use Semicolons
According to [scientific research][hnsemicolons], the usage of semicolons is
a core value of our community. Consider the points of [the opposition][], but
be a traditionalist when it comes to abusing error correction mechanisms for
cheap syntactic pleasures.
[the opposition]: http://blog.izs.me/post/2353458699/an-open-letter-to-javascript-leaders-regarding
[hnsemicolons]: http://news.ycombinator.com/item?id=1547647
## 120 characters per line
Try to limit your lines to 80 characters. If it feels right, you can go up to 120 characters.
## Use single quotes
Use single quotes, unless you are writing JSON.
*Right:*
```js
var foo = 'bar';
```
*Wrong:*
```js
var foo = "bar";
```
## Opening braces go on the same line
Your opening braces go on the same line as the statement.
*Right:*
```js
if (true) {
console.log('winning');
}
```
*Wrong:*
```js
if (true)
{
console.log('losing');
}
```
Also, notice the use of whitespace before and after the condition statement.
## Always use braces for multi-line code
*Right:*
```js
if (err) {
return cb(err);
}
```
*Wrong:*
```js
if (err)
return cb(err);
```
## Prefer multi-line conditionals
But single-line conditionals are allowed for short lines
*Preferred:*
```js
if (err) {
return cb(err);
}
```
*Allowed:*
```js
if (err) return cb(err);
```
## Declare one variable per var statement
Declare one variable per var statement, it makes it easier to re-order the
lines. However, ignore [Crockford][crockfordconvention] when it comes to
declaring variables deeper inside a function, just put the declarations wherever
they make sense.
*Right:*
```js
var keys = ['foo', 'bar'];
var values = [23, 42];
var object = {};
while (keys.length) {
var key = keys.pop();
object[key] = values.pop();
}
```
*Wrong:*
```js
var keys = ['foo', 'bar'],
values = [23, 42],
object = {},
key;
while (keys.length) {
key = keys.pop();
object[key] = values.pop();
}
```
[crockfordconvention]: http://javascript.crockford.com/code.html
## Use lowerCamelCase for variables, properties and function names
Variables, properties and function names should use `lowerCamelCase`. They
should also be descriptive. Single character variables and uncommon
abbreviations should generally be avoided.
*Right:*
```js
var adminUser = db.query('SELECT * FROM users ...');
```
*Wrong:*
```js
var admin_user = db.query('SELECT * FROM users ...');
```
## Use UpperCamelCase for class names
Class names should be capitalized using `UpperCamelCase`.
*Right:*
```js
function BankAccount() {
}
```
*Wrong:*
```js
function bank_Account() {
}
```
## Use UPPERCASE for Constants
Constants should be declared as regular variables or static class properties,
using all uppercase letters.
Node.js / V8 actually supports mozilla's [const][const] extension, but
unfortunately that cannot be applied to class members, nor is it part of any
ECMA standard.
*Right:*
```js
var SECOND = 1 * 1000;
function File() {
}
File.FULL_PERMISSIONS = 0777;
```
*Wrong:*
```js
const SECOND = 1 * 1000;
function File() {
}
File.fullPermissions = 0777;
```
[const]: https://developer.mozilla.org/en/JavaScript/Reference/Statements/const
## Magic numbers
These are numbers (or other values) simply used in line in your code. **Do not use these**, give them a variable name so they can be understood and changed easily.
*Right:*
```js
var minWidth = 300;
if (width < minWidth) {
...
}
```
*Wrong:*
```js
if (width < 300) {
...
}
```
## Global definitions
Don't do this. Everything should be wrapped in a module that can be depended on by other modules. Even things as simple as a single value should be a module.
## Function definitions
Prefer the use of function declarations over function expressions. Function expressions are allowed, but should usually be avoided.
Also, keep function definitions above other code instead of relying on function hoisting.
*Preferred:*
```js
function myFunc() {
...
}
```
*Allowed:*
```js
var myFunc = function () {
...
};
```
## Object / Array creation
Use trailing commas and put *short* declarations on a single line. Only quote
keys when your interpreter complains:
*Right:*
```js
var a = ['hello', 'world'];
var b = {
good: 'code',
'is generally': 'pretty'
};
```
*Wrong:*
```js
var a = [
'hello', 'world'
];
var b = {"good": 'code'
, is generally: 'pretty'
};
```
## Object / Array iterations, transformations and operations
Use native ES5 methods to iterate and transform arrays and objects where possible. Do not use `for` and `while` loops.
Use descriptive variable names in the closures.
Use a utility library as needed and where it will make code more comprehensible.
*Right:*
```js
var userNames = users.map(function (user) {
return user.name;
});
// examples where lodash makes the code more readable
var userNames = _.pluck(users, 'name');
```
*Wrong:*
```js
var userNames = [];
for (var i = 0; i < users.length; i++) {
userNames.push(users[i].name);
}
```
## Use the === operator
Programming is not about remembering [stupid rules][comparisonoperators]. Use
the triple equality operator as it will work just as expected.
*Right:*
```js
var a = 0;
if (a !== '') {
console.log('winning');
}
```
*Wrong:*
```js
var a = 0;
if (a == '') {
console.log('losing');
}
```
[comparisonoperators]: https://developer.mozilla.org/en/JavaScript/Reference/Operators/Comparison_Operators
## Only use ternary operators for small, simple code
And **never** use multiple ternaries together
*Right:*
```js
var foo = (a === b) ? 1 : 2;
```
*Wrong:*
```js
var foo = (a === b) ? 1 : (a === c) ? 2 : 3;
```
## Do not extend built-in prototypes
Do not extend the prototype of native JavaScript objects. Your future self will
be forever grateful.
*Right:*
```js
var a = [];
if (!a.length) {
console.log('winning');
}
```
*Wrong:*
```js
Array.prototype.empty = function() {
return !this.length;
}
var a = [];
if (a.empty()) {
console.log('losing');
}
```
## Use descriptive conditions
Any non-trivial conditions should be assigned to a descriptively named variables, broken into
several names variables, or converted to be a function:
*Right:*
```js
var thing = ...;
var isShape = thing instanceof Shape;
var notSquare = !(thing instanceof Square);
var largerThan10 = isShape && thing.size > 10;
if (isShape && notSquare && largerThan10) {
console.log('some big polygon');
}
```
*Wrong:*
```js
if (
thing instanceof Shape
&& !(thing instanceof Square)
&& thing.size > 10
) {
console.log('bigger than ten?? Woah!');
}
```
## Name regular expressions
*Right:*
```js
var validPasswordRE = /^(?=.*\d).{4,}$/;
if (password.length >= 4 && validPasswordRE.test(password)) {
console.log('password is valid');
}
```
*Wrong:*
```js
if (password.length >= 4 && /^(?=.*\d).{4,}$/.test(password)) {
console.log('losing');
}
```
## Write small functions
Keep your functions short. A good function fits on a slide that the people in
the last row of a big room can comfortably read. So don't count on them having
perfect vision and limit yourself to ~15 lines of code per function.
## Return early from functions
To avoid deep nesting of if-statements, always return a function's value as early
as possible.
*Right:*
```js
function isPercentage(val) {
if (val < 0) return false;
if (val > 100) return false;
return true;
}
```
*Wrong:*
```js
function isPercentage(val) {
if (val >= 0) {
if (val < 100) {
return true;
} else {
return false;
}
} else {
return false;
}
}
```
Or for this particular example it may also be fine to shorten things even
further:
```js
function isPercentage(val) {
var isInRange = (val >= 0 && val <= 100);
return isInRange;
}
```
## Chaining operations
When using a chaining syntax (jquery or promises, for example), do not indent the subsequent chained operations, unless there is a logical grouping in them.
Also, if the chain is long, each method should be on a new line.
*Right:*
```js
$('.someClass')
.addClass('another-class')
.append(someElement)
```
```js
d3.selectAll('g.bar')
.enter()
.append('thing')
.data(anything)
.exit()
.each(function() ... )
```
```js
$http.get('/info')
.then(({ data }) => this.transfromInfo(data))
.then((transformed) => $http.post('/new-info', transformed))
.then(({ data }) => console.log(data));
```
*Wrong:*
```js
$('.someClass')
.addClass('another-class')
.append(someElement)
```
```js
d3.selectAll('g.bar')
.enter().append('thing').data(anything).exit()
.each(function() ... )
```
```js
$http.get('/info')
.then(({ data }) => this.transfromInfo(data))
.then((transformed) => $http.post('/new-info', transformed))
.then(({ data }) => console.log(data));
```
## Name your closures
Feel free to give your closures a descriptive name. It shows that you care about them, and
will produce better stack traces, heap and cpu profiles.
*Right:*
```js
req.on('end', function onEnd() {
console.log('winning');
});
```
*Wrong:*
```js
req.on('end', function() {
console.log('losing');
});
```
## No nested closures
Use closures, but don't nest them. Otherwise your code will become a mess.
*Right:*
```js
setTimeout(function() {
client.connect(afterConnect);
}, 1000);
function afterConnect() {
console.log('winning');
}
```
*Wrong:*
```js
setTimeout(function() {
client.connect(function() {
console.log('losing');
});
}, 1000);
```
## Use slashes for comments
Use slashes for both single line and multi line comments. Try to write
comments that explain higher level mechanisms or clarify difficult
segments of your code. **Don't use comments to restate trivial things**.
***Exception:*** Comment blocks describing a function and its arguments (docblock) should start with `/**`, contain a single `*` at the beginning of each line, and end with `*/`.
*Right:*
```js
// 'ID_SOMETHING=VALUE' -> ['ID_SOMETHING=VALUE', 'SOMETHING', 'VALUE']
var matches = item.match(/ID_([^\n]+)=([^\n]+)/));
/**
* Fetches a user from...
* @param {string} id - id of the user
* @return {Promise}
*/
function loadUser(id) {
// This function has a nasty side effect where a failure to increment a
// redis counter used for statistics will cause an exception. This needs
// to be fixed in a later iteration.
...
}
var isSessionValid = (session.expires < Date.now());
if (isSessionValid) {
...
}
```
*Wrong:*
```js
// Execute a regex
var matches = item.match(/ID_([^\n]+)=([^\n]+)/));
// Usage: loadUser(5, function() { ... })
function loadUser(id, cb) {
// ...
}
// Check if the session is valid
var isSessionValid = (session.expires < Date.now());
// If the session is valid
if (isSessionValid) {
// ...
}
```
## Do not comment out code
We use a version management system. If a line of code is no longer needed, remove it, don't simply comment it out.
## Classes/Constructors and Inheritance
While JavaScript it is not always considered an object-oriented language, it does have the building blocks for writing object oriented code. Of course, as with all things JavaScript, there are many ways this can be accomplished. Generally, we try to err on the side of readability.
### Capitalized function definition as Constructors
When Defining a Class/Constructor, use the function definition syntax.
*Right:*
```js
function ClassName() {
}
```
*Wrong:*
```js
var ClassName = function () {};
```
### Inheritance should be done with a utility
While you can do it with pure JS, a utility will remove a lot of boilerplate, and be more readable and functional.
*Right:*
```js
// uses a lodash inherits mixin
// inheritance is defined first - it's easier to read and the function will be hoisted
_.class(Square).inherits(Shape);
function Square(width, height) {
Square.Super.call(this);
}
```
*Wrong:*
```js
function Square(width, height) {
this.width = width;
this.height = height;
}
Square.prototype = Object.create(Shape);
```
### Keep Constructors Small
It is often the case that there are properties that can't be defined on the prototype, or work that needs to be done to completely create an object (like call its Super class). This is all that should be done within constructors.
Try to follow the [Write small functions](#write-small-functions) rule here too.
### Use the prototype
If a method/property *can* go on the prototype, it probably should.
```js
function Square() {
...
}
/**
* method does stuff
* @return {undefined}
*/
Square.prototype.method = function () {
...
}
```
### Handling scope and aliasing `this`
When creating a prototyped class, each method should almost always start with:
`var self = this;`
With the exception of very short methods (roughly 3 lines or less), `self` should always be used in place of `this`.
Avoid the use of `bind`
*Right:*
```js
Square.prototype.doFancyThings = function () {
var self = this;
somePromiseUtil()
.then(function (result) {
self.prop = result.prop;
});
}
```
*Wrong:*
```js
Square.prototype.doFancyThings = function () {
somePromiseUtil()
.then(function (result) {
this.prop = result.prop;
}).bind(this);
}
```
*Allowed:*
```js
Square.prototype.area = function () {
return this.width * this.height;
}
```
## Object.freeze, Object.preventExtensions, Object.seal, with, eval
Crazy shit that you will probably never need. Stay away from it.
## Getters and Setters
Feel free to use getters that are free from [side effects][sideeffect], like
providing a length property for a collection class.
Do not use setters, they cause more problems for people who try to use your
software than they can solve.
[sideeffect]: http://en.wikipedia.org/wiki/Side_effect_(computer_science)
- [JavaScript](style_guides/js_style_guide.md)
- [CSS](style_guides/css_style_guide.md)
- [HTML](style_guides/html_style_guide.md)
- [API](style_guides/api_style_guide.md)
# Kibana Style Guide
@ -866,59 +108,3 @@ require('ui/routes')
// angular route code goes here
});
```
# Html Style Guide
## Multiple attribute values
When a node has multiple attributes that would cause it to exceed the line character limit, each attribute including the first should be on its own line with a single indent. Also, when a node that is styled in this way has child nodes, there should be a blank line between the opening parent tag and the first child tag.
```
<ul
attribute1="value1"
attribute2="value2"
attribute3="value3">
<li></li>
<li></li>
...
</ul>
```
# Api Style Guide
## Paths
API routes must start with the `/api/` path segment, and should be followed by the plugin id if applicable:
*Right:* `/api/marvel/v1/nodes`
*Wrong:* `/marvel/api/v1/nodes`
## Versions
Kibana won't be supporting multiple API versions, so API's should not define a version.
*Right:* `/api/kibana/index_patterns`
*Wrong:* `/api/kibana/v1/index_patterns`
## snake_case
Kibana uses `snake_case` for the entire API, just like Elasticsearch. All urls, paths, query string parameters, values, and bodies should be `snake_case` formatted.
*Right:*
```
POST /api/kibana/index_patterns
{
"id": "...",
"time_field_name": "...",
"fields": [
...
]
}
```
# Attribution
This JavaScript guide forked from the [node style guide](https://github.com/felixge/node-style-guide) created by [Felix Geisendörfer](http://felixge.de/) and is
licensed under the [CC BY-SA 3.0](http://creativecommons.org/licenses/by-sa/3.0/)
license.

View file

@ -88,5 +88,5 @@
# logging.verbose: false
# Set the interval in milliseconds to sample system and process performance
# metrics. Minimum is 100ms. Defaults to 10000.
# ops.interval: 10000
# metrics. Minimum is 100ms. Defaults to 5000.
# ops.interval: 5000

View file

@ -8,6 +8,7 @@
:k4pull: https://github.com/elastic/kibana/pull/
:version: master
:esversion: master
:packageversion: master
include::introduction.asciidoc[]

View file

@ -26,7 +26,7 @@ wget -qO - https://packages.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add
+
["source","sh",subs="attributes"]
--------------------------------------------------
echo "deb http://packages.elastic.co/kibana/{version}/debian stable main" | sudo tee -a /etc/apt/sources.list.d/kibana.list
echo "deb https://packages.elastic.co/kibana/{packageversion}/debian stable main" | sudo tee -a /etc/apt/sources.list.d/kibana.list
--------------------------------------------------
+
[WARNING]
@ -82,11 +82,11 @@ rpm --import https://packages.elastic.co/GPG-KEY-elasticsearch
+
["source","sh",subs="attributes"]
--------------------------------------------------
[kibana-{version}]
name=Kibana repository for {version}.x packages
baseurl=http://packages.elastic.co/kibana/{version}/centos
[kibana-{packageversion}]
name=Kibana repository for {packageversion} packages
baseurl=https://packages.elastic.co/kibana/{packageversion}/centos
gpgcheck=1
gpgkey=http://packages.elastic.co/GPG-KEY-elasticsearch
gpgkey=https://packages.elastic.co/GPG-KEY-elasticsearch
enabled=1
--------------------------------------------------
+

View file

@ -27,7 +27,7 @@ authority for your Elasticsearch instance.
to `false`.
`elasticsearch.pingTimeout:`:: *Default: the value of the `elasticsearch.requestTimeout` setting* Time in milliseconds to
wait for Elasticsearch to respond to pings.
`elasticsearch.requestTimeout:`:: *Default: 300000* Time in milliseconds to wait for responses from the back end or
`elasticsearch.requestTimeout:`:: *Default: 30000* Time in milliseconds to wait for responses from the back end or
Elasticsearch. This value must be a positive integer.
`elasticsearch.requestHeadersWhitelist:`:: *Default: `[ 'authorization' ]`* List of Kibana client-side headers to send to Elasticsearch.
To send *no* client-side headers, set this value to [] (an empty list).
@ -42,7 +42,7 @@ retrying.
error messages.
`logging.verbose`:: *Default: false* Set the value of this setting to `true` to log all events, including system usage
information and all requests.
`ops.interval`:: *Default: 10000* Set the interval in milliseconds to sample system and process performance metrics.
`ops.interval`:: *Default: 5000* Set the interval in milliseconds to sample system and process performance metrics.
The minimum value is 100.
`status.allowAnonymous`:: *Default: false* If authentication is enabled, setting this to `true` allows
unauthenticated users to access the Kibana server status API and status page.

View file

@ -11,7 +11,7 @@
"dashboarding"
],
"private": false,
"version": "5.0.0-snapshot",
"version": "5.0.0-alpha4",
"build": {
"number": 8467,
"sha": "6cb7fec4e154faa0a4a3fee4b33dfef91b9870d9"
@ -38,7 +38,7 @@
"Tim Sullivan <tim@elastic.co>"
],
"scripts": {
"test": "grunt test",
"test": "grunt test; grunt test:visualRegression",
"test:dev": "grunt test:dev",
"test:quick": "grunt test:quick",
"test:browser": "grunt test:browser",
@ -48,7 +48,6 @@
"test:server": "grunt test:server",
"test:coverage": "grunt test:coverage",
"build": "grunt build",
"build:ospackages": "grunt build --os-packages",
"start": "sh ./bin/kibana --dev",
"precommit": "grunt precommit",
"karma": "karma start",
@ -95,8 +94,10 @@
"commander": "2.8.1",
"css-loader": "0.17.0",
"d3": "3.5.6",
"dragula": "3.7.0",
"elasticsearch": "10.1.2",
"elasticsearch-browser": "10.1.2",
"even-better": "7.0.2",
"expiry-js": "0.1.7",
"exports-loader": "0.6.2",
"expose-loader": "0.7.0",
@ -104,7 +105,6 @@
"file-loader": "0.8.4",
"font-awesome": "4.4.0",
"glob-all": "3.0.1",
"good": "6.3.0",
"good-squeeze": "2.1.0",
"gridster": "0.5.6",
"hapi": "8.8.1",
@ -125,8 +125,8 @@
"marked": "0.3.3",
"minimatch": "2.0.10",
"mkdirp": "0.5.1",
"moment": "2.10.6",
"moment-timezone": "0.4.1",
"moment": "2.13.0",
"moment-timezone": "0.5.4",
"node-uuid": "1.4.7",
"raw-loader": "0.5.1",
"request": "2.61.0",
@ -153,8 +153,11 @@
"auto-release-sinon": "1.0.3",
"babel-eslint": "4.1.8",
"chokidar": "1.4.3",
"dot": "1.0.3",
"elasticdump": "2.1.1",
"eslint": "1.10.3",
"eslint-plugin-mocha": "1.1.0",
"event-stream": "3.3.2",
"expect.js": "0.3.1",
"faker": "1.1.0",
"grunt": "0.4.5",
@ -162,7 +165,7 @@
"grunt-cli": "0.1.13",
"grunt-contrib-clean": "0.6.0",
"grunt-contrib-copy": "0.8.1",
"grunt-esvm": "3.1.1",
"grunt-esvm": "3.2.1",
"grunt-karma": "0.12.0",
"grunt-run": "0.5.0",
"grunt-s3": "0.2.0-alpha.3",
@ -170,6 +173,7 @@
"gruntify-eslint": "1.0.1",
"html-entities": "1.1.3",
"husky": "0.8.1",
"image-diff": "1.6.0",
"intern": "3.0.1",
"istanbul-instrumenter-loader": "0.1.3",
"karma": "0.13.9",
@ -181,14 +185,14 @@
"karma-safari-launcher": "0.1.1",
"license-checker": "3.1.0",
"load-grunt-config": "0.19.1",
"makelogs": "3.0.0-beta3",
"makelogs": "3.0.0",
"marked-text-renderer": "0.1.0",
"mocha": "2.3.0",
"ncp": "2.0.0",
"nock": "2.10.0",
"npm": "2.11.0",
"portscanner": "1.0.0",
"simple-git": "1.8.0",
"simple-git": "1.37.0",
"sinon": "1.17.2",
"source-map": "0.4.4",
"source-map-support": "0.4.0",

View file

@ -0,0 +1,6 @@
server:
port: 8274
logging:
json: true
optimize:
enabled: false

View file

@ -0,0 +1,90 @@
import { spawn } from 'child_process';
import { writeFileSync, readFile } from 'fs';
import { relative, resolve } from 'path';
import { safeDump } from 'js-yaml';
import es from 'event-stream';
import readYamlConfig from '../read_yaml_config';
import expect from 'expect.js';
const testConfigFile = follow(`fixtures/reload_logging_config/kibana.test.yml`);
const cli = follow(`../../../../bin/kibana`);
function follow(file) {
return relative(process.cwd(), resolve(__dirname, file));
}
function setLoggingJson(enabled) {
const conf = readYamlConfig(testConfigFile);
conf.logging = conf.logging || {};
conf.logging.json = enabled;
const yaml = safeDump(conf);
writeFileSync(testConfigFile, yaml);
return conf;
}
describe(`Server logging configuration`, function () {
it(`should be reloadable via SIGHUP process signaling`, function (done) {
this.timeout(60000);
let asserted = false;
let json = Infinity;
const conf = setLoggingJson(true);
const child = spawn(cli, [`--config`, testConfigFile]);
child.on('error', err => {
done(new Error(`error in child process while attempting to reload config.
${err.stack || err.message || err}`));
});
child.on('exit', code => {
expect(asserted).to.eql(true);
expect(code === null || code === 0).to.eql(true);
done();
});
child.stdout
.pipe(es.split())
.pipe(es.mapSync(function (line) {
if (!line) {
return line; // ignore empty lines
}
if (json--) {
expect(parseJsonLogLine).withArgs(line).to.not.throwError();
} else {
expectPlainTextLogLine(line);
}
}));
function parseJsonLogLine(line) {
try {
const data = JSON.parse(line);
const listening = data.tags.indexOf(`listening`) !== -1;
if (listening) {
switchToPlainTextLog();
}
} catch (err) {
expect(`Error parsing log line as JSON\n
${err.stack || err.message || err}`).to.eql(true);
}
}
function switchToPlainTextLog() {
json = 3; // ignore both "reloading" messages + ui settings status message
setLoggingJson(false);
child.kill(`SIGHUP`); // reload logging config
}
function expectPlainTextLogLine(line) {
// assert
const tags = `[\u001b[32minfo\u001b[39m][\u001b[36mconfig\u001b[39m]`;
const status = `Reloaded logging configuration due to SIGHUP.`;
const expected = `${tags} ${status}`;
const actual = line.slice(-expected.length);
expect(actual).to.eql(expected);
// cleanup
asserted = true;
setLoggingJson(true);
child.kill();
}
});
});

View file

@ -2,11 +2,8 @@ import _ from 'lodash';
import { statSync } from 'fs';
import { isWorker } from 'cluster';
import { resolve } from 'path';
import readYamlConfig from './read_yaml_config';
import { fromRoot } from '../../utils';
const cwd = process.cwd();
import readYamlConfig from './read_yaml_config';
let canCluster;
try {
@ -28,7 +25,7 @@ const configPathCollector = pathCollector();
const pluginDirCollector = pathCollector();
const pluginPathCollector = pathCollector();
function initServerSettings(opts, extraCliOptions) {
function readServerSettings(opts, extraCliOptions) {
const settings = readYamlConfig(opts.config);
const set = _.partial(_.set, settings);
const get = _.partial(_.get, settings);
@ -128,7 +125,8 @@ module.exports = function (program) {
}
}
const settings = initServerSettings(opts, this.getUnknownOptions());
const getCurrentSettings = () => readServerSettings(opts, this.getUnknownOptions());
const settings = getCurrentSettings();
if (canCluster && opts.dev && !isWorker) {
// stop processing the action and handoff to cluster manager
@ -156,6 +154,13 @@ module.exports = function (program) {
process.exit(1); // eslint-disable-line no-process-exit
}
process.on('SIGHUP', function reloadConfig() {
const settings = getCurrentSettings();
kbnServer.server.log(['info', 'config'], 'Reloading logging configuration due to SIGHUP.');
kbnServer.applyLoggingConfiguration(settings);
kbnServer.server.log(['info', 'config'], 'Reloaded logging configuration due to SIGHUP.');
});
return kbnServer;
});
};

View file

@ -44,8 +44,8 @@ describe('kibana cli', function () {
workingPath: testWorkingPath,
tempArchiveFile: tempArchiveFilePath,
plugin: 'test-plugin',
version: '5.0.0-snapshot',
plugins: [ { name: 'foo', path: join(testWorkingPath, 'foo'), version: '5.0.0-snapshot' } ]
version: '5.0.0-SNAPSHOT',
plugins: [ { name: 'foo', path: join(testWorkingPath, 'foo'), version: '5.0.0-SNAPSHOT' } ]
};
const errorStub = sinon.stub();

View file

@ -1,8 +1,10 @@
import { fromRoot } from '../../utils';
import fs from 'fs';
import install from './install';
import Logger from '../lib/logger';
import pkg from '../../utils/package_json';
import { parse, parseMilliseconds } from './settings';
import { find } from 'lodash';
function processCommand(command, options) {
let settings;
@ -18,6 +20,24 @@ function processCommand(command, options) {
install(settings, logger);
}
function getDefaultConfigPath() {
const paths = [
fromRoot('config/kibana.yml'),
'/etc/kibana/kibana.yml'
];
const availablePath = find(paths, configPath => {
try {
fs.accessSync(configPath, fs.R_OK);
return true;
} catch (e) {
//Check the next path
}
});
return availablePath || paths[0];
}
export default function pluginInstall(program) {
program
.command('install <plugin/url>')
@ -26,7 +46,7 @@ export default function pluginInstall(program) {
.option(
'-c, --config <path>',
'path to the config file',
fromRoot('config/kibana.yml')
getDefaultConfigPath()
)
.option(
'-t, --timeout <duration>',

View file

@ -5,7 +5,14 @@ import mkdirp from 'mkdirp';
import Logger from '../../lib/logger';
import list from '../list';
import { join } from 'path';
import { writeFileSync } from 'fs';
import { writeFileSync, appendFileSync } from 'fs';
function createPlugin(name, version, pluginBaseDir) {
const pluginDir = join(pluginBaseDir, name);
mkdirp.sync(pluginDir);
appendFileSync(join(pluginDir, 'package.json'), '{"version": "' + version + '"}');
}
describe('kibana cli', function () {
@ -33,41 +40,61 @@ describe('kibana cli', function () {
});
it('list all of the folders in the plugin folder', function () {
mkdirp.sync(join(pluginDir, 'plugin1'));
mkdirp.sync(join(pluginDir, 'plugin2'));
mkdirp.sync(join(pluginDir, 'plugin3'));
createPlugin('plugin1', '5.0.0-alpha2', pluginDir);
createPlugin('plugin2', '3.2.1', pluginDir);
createPlugin('plugin3', '1.2.3', pluginDir);
list(settings, logger);
expect(logger.log.calledWith('plugin1')).to.be(true);
expect(logger.log.calledWith('plugin2')).to.be(true);
expect(logger.log.calledWith('plugin3')).to.be(true);
expect(logger.log.calledWith('plugin1@5.0.0-alpha2')).to.be(true);
expect(logger.log.calledWith('plugin2@3.2.1')).to.be(true);
expect(logger.log.calledWith('plugin3@1.2.3')).to.be(true);
});
it('ignore folders that start with a period', function () {
mkdirp.sync(join(pluginDir, '.foo'));
mkdirp.sync(join(pluginDir, 'plugin1'));
mkdirp.sync(join(pluginDir, 'plugin2'));
mkdirp.sync(join(pluginDir, 'plugin3'));
mkdirp.sync(join(pluginDir, '.bar'));
createPlugin('.foo', '1.0.0', pluginDir);
createPlugin('plugin1', '5.0.0-alpha2', pluginDir);
createPlugin('plugin2', '3.2.1', pluginDir);
createPlugin('plugin3', '1.2.3', pluginDir);
createPlugin('.bar', '1.0.0', pluginDir);
list(settings, logger);
expect(logger.log.calledWith('.foo')).to.be(false);
expect(logger.log.calledWith('.bar')).to.be(false);
expect(logger.log.calledWith('.foo@1.0.0')).to.be(false);
expect(logger.log.calledWith('.bar@1.0.0')).to.be(false);
});
it('list should only list folders', function () {
mkdirp.sync(join(pluginDir, 'plugin1'));
mkdirp.sync(join(pluginDir, 'plugin2'));
mkdirp.sync(join(pluginDir, 'plugin3'));
createPlugin('plugin1', '1.0.0', pluginDir);
createPlugin('plugin2', '1.0.0', pluginDir);
createPlugin('plugin3', '1.0.0', pluginDir);
writeFileSync(join(pluginDir, 'plugin4'), 'This is a file, and not a folder.');
list(settings, logger);
expect(logger.log.calledWith('plugin1')).to.be(true);
expect(logger.log.calledWith('plugin2')).to.be(true);
expect(logger.log.calledWith('plugin3')).to.be(true);
expect(logger.log.calledWith('plugin1@1.0.0')).to.be(true);
expect(logger.log.calledWith('plugin2@1.0.0')).to.be(true);
expect(logger.log.calledWith('plugin3@1.0.0')).to.be(true);
});
it('list should throw an exception if a plugin does not have a package.json', function () {
createPlugin('plugin1', '1.0.0', pluginDir);
mkdirp.sync(join(pluginDir, 'empty-plugin'));
expect(function () {
list(settings, logger);
}).to.throwError('Unable to read package.json file for plugin empty-plugin');
});
it('list should throw an exception if a plugin have an empty package.json', function () {
createPlugin('plugin1', '1.0.0', pluginDir);
const invalidPluginDir = join(pluginDir, 'invalid-plugin');
mkdirp.sync(invalidPluginDir);
appendFileSync(join(invalidPluginDir, 'package.json'), '');
expect(function () {
list(settings, logger);
}).to.throwError('Unable to read package.json file for plugin invalid-plugin');
});
});

View file

@ -1,4 +1,4 @@
import { statSync, readdirSync } from 'fs';
import { statSync, readdirSync, readFileSync } from 'fs';
import { join } from 'path';
export default function list(settings, logger) {
@ -7,7 +7,13 @@ export default function list(settings, logger) {
const stat = statSync(join(settings.pluginDir, filename));
if (stat.isDirectory() && filename[0] !== '.') {
logger.log(filename);
try {
const packagePath = join(settings.pluginDir, filename, 'package.json');
const { version } = JSON.parse(readFileSync(packagePath, 'utf8'));
logger.log(filename + '@' + version);
} catch (e) {
throw new Error('Unable to read package.json file for plugin ' + filename);
}
}
});
logger.log(''); //intentional blank line for aesthetics

View file

@ -13,7 +13,7 @@
{
"_index": ".kibana",
"_type": "config",
"_id": "4.0.1-snapshot",
"_id": "4.0.1-SNAPSHOT",
"_score": 1,
"_source": {
"buildNum": 5921,

View file

@ -2,6 +2,7 @@ import _ from 'lodash';
module.exports = {
'valueFormatter': _.identity,
'geohashGridAgg': { 'vis': { 'params': {} } },
'geoJson': {
'type': 'FeatureCollection',
'features': [

View file

@ -9,7 +9,7 @@ function VisDetailsSpyProvider(Notifier, $filter, $rootScope, config) {
template: visDebugSpyPanelTemplate,
order: 5,
link: function ($scope, $el) {
$scope.$watch('vis.getState() | json', function (json) {
$scope.$watch('vis.getEnabledState() | json', function (json) {
$scope.visStateJson = json;
});
}

View file

@ -39,8 +39,9 @@ describe('plugins/elasticsearch', function () {
upgradeDoc('4.0.0-rc2', '4.0.2', true);
upgradeDoc('4.0.1', '4.1.0-rc', true);
upgradeDoc('4.0.0-rc1', '4.0.0', true);
upgradeDoc('4.0.0-rc1-snapshot', '4.0.0', false);
upgradeDoc('4.1.0-rc1-snapshot', '4.1.0-rc1', false);
upgradeDoc('4.0.0-rc1-SNAPSHOT', '4.0.0', false);
upgradeDoc('4.1.0-rc1-SNAPSHOT', '4.1.0-rc1', false);
upgradeDoc('5.0.0-alpha1', '5.0.0', false);
it('should handle missing _id field', function () {
let doc = {

View file

@ -91,10 +91,18 @@ describe('plugins/elasticsearch', function () {
});
});
it('should resolve with undefined if the nothing is upgradeable', function () {
const response = { hits: { hits: [ { _id: '4.0.1-beta1' }, { _id: '4.0.0-snapshot1' } ] } };
it('should create new config if the nothing is upgradeable', function () {
get.withArgs('pkg.buildNum').returns(9833);
client.create.returns(Promise.resolve());
const response = { hits: { hits: [ { _id: '4.0.1-alpha3' }, { _id: '4.0.1-beta1' }, { _id: '4.0.0-SNAPSHOT1' } ] } };
return upgrade(response).then(function (resp) {
expect(resp).to.be(undefined);
sinon.assert.calledOnce(client.create);
const params = client.create.args[0][0];
expect(params).to.have.property('body');
expect(params.body).to.have.property('buildNum', 9833);
expect(params).to.have.property('index', '.my-kibana');
expect(params).to.have.property('type', 'config');
expect(params).to.have.property('id', '4.0.1');
});
});

View file

@ -21,9 +21,9 @@ const versionChecks = [
['2.0.1', '^2.0.0', true],
['2.1.1', '^2.1.0', true],
['2.2.0', '^2.1.0', true],
['3.0.0-snapshot', '^2.1.0', false],
['3.0.0-SNAPSHOT', '^2.1.0', false],
['3.0.0', '^2.1.0', false],
['2.10.20-snapshot', '^2.10.20', true],
['2.10.20-SNAPSHOT', '^2.10.20', true],
['2.10.999', '^2.10.20', true],
];

View file

@ -3,7 +3,7 @@ const rcVersionRegex = /(\d+\.\d+\.\d+)\-rc(\d+)/i;
module.exports = function (server, doc) {
const config = server.config();
if (/beta|snapshot/i.test(doc._id)) return false;
if (/alpha|beta|snapshot/i.test(doc._id)) return false;
if (!doc._id) return false;
if (doc._id === config.get('pkg.version')) return false;

View file

@ -9,17 +9,21 @@ module.exports = function (server) {
const client = server.plugins.elasticsearch.client;
const config = server.config();
function createNewConfig() {
return client.create({
index: config.get('kibana.index'),
type: 'config',
body: { buildNum: config.get('pkg.buildNum') },
id: config.get('pkg.version')
});
}
return function (response) {
const newConfig = {};
// Check to see if there are any doc. If not then we set the build number and id
if (response.hits.hits.length === 0) {
return client.create({
index: config.get('kibana.index'),
type: 'config',
body: { buildNum: config.get('pkg.buildNum') },
id: config.get('pkg.version')
});
return createNewConfig();
}
// if we already have a the current version in the index then we need to stop
@ -30,9 +34,11 @@ module.exports = function (server) {
if (devConfig) return Promise.resolve();
// Look for upgradeable configs. If none of them are upgradeable
// then resolve with null.
// then create a new one.
const body = _.find(response.hits.hits, isUpgradeable.bind(null, server));
if (!body) return Promise.resolve();
if (!body) {
return createNewConfig();
}
// if the build number is still the template string (which it wil be in development)
// then we need to set it to the max interger. Otherwise we will set it to the build num

View file

@ -27,6 +27,8 @@ export default function TileMapVisType(Private, getAppState, courier, config) {
heatRadius: 25,
heatBlur: 15,
heatNormalizeData: true,
mapZoom: 2,
mapCenter: [15, 5],
wms: config.get('visualization:tileMap:WMSdefaults')
},
mapTypes: ['Scaled Circle Markers', 'Shaded Circle Markers', 'Shaded Geohash Grid', 'Heatmap'],
@ -46,54 +48,16 @@ export default function TileMapVisType(Private, getAppState, courier, config) {
pushFilter(filter, false, indexPatternName);
},
mapMoveEnd: function (event) {
const agg = _.get(event, 'chart.geohashGridAgg');
if (!agg) return;
agg.params.mapZoom = event.zoom;
agg.params.mapCenter = [event.center.lat, event.center.lng];
const editableVis = agg.vis.getEditableVis();
if (!editableVis) return;
const editableAgg = editableVis.aggs.byId[agg.id];
if (editableAgg) {
editableAgg.params.mapZoom = event.zoom;
editableAgg.params.mapCenter = [event.center.lat, event.center.lng];
}
mapMoveEnd: function (event, uiState) {
uiState.set('mapCenter', event.center);
},
mapZoomEnd: function (event) {
const agg = _.get(event, 'chart.geohashGridAgg');
if (!agg || !agg.params.autoPrecision) return;
mapZoomEnd: function (event, uiState) {
uiState.set('mapZoom', event.zoom);
// zoomPrecision maps event.zoom to a geohash precision value
// event.limit is the configurable max geohash precision
// default max precision is 7, configurable up to 12
const zoomPrecision = {
1: 2,
2: 2,
3: 2,
4: 3,
5: 3,
6: 4,
7: 4,
8: 5,
9: 5,
10: 6,
11: 6,
12: 7,
13: 7,
14: 8,
15: 9,
16: 10,
17: 11,
18: 12
};
const precision = config.get('visualization:tileMap:maxPrecision');
agg.params.precision = Math.min(zoomPrecision[event.zoom], precision);
courier.fetch();
const autoPrecision = _.get(event, 'chart.geohashGridAgg.params.autoPrecision');
if (autoPrecision) {
courier.fetch();
}
}
},
responseConverter: geoJsonConverter,

View file

@ -19,14 +19,13 @@ module.exports = function (kibana) {
title: 'Kibana',
listed: false,
description: 'the kibana you know and love',
//icon: 'plugins/kibana/settings/sections/about/barcode.svg',
main: 'plugins/kibana/kibana',
uses: [
'visTypes',
'spyModes',
'fieldFormats',
'navbarExtensions',
'settingsSections',
'managementSections',
'docViews'
],
@ -62,11 +61,12 @@ module.exports = function (kibana) {
icon: 'plugins/kibana/assets/dashboard.svg',
},
{
title: 'Settings',
title: 'Management',
order: 1000,
url: '/app/kibana#/settings',
url: '/app/kibana#/management',
description: 'define index patterns, change config, and more',
icon: 'plugins/kibana/assets/settings.svg',
linkToLastSubUrl: false
}
],
injectDefaultVars(server, options) {

View file

@ -15,7 +15,7 @@ uiModules
const filterManager = Private(FilterManagerProvider);
const notify = new Notifier();
const services = require('plugins/kibana/settings/saved_object_registry').all().map(function (serviceObj) {
const services = require('plugins/kibana/management/saved_object_registry').all().map(function (serviceObj) {
const service = $injector.get(serviceObj.service);
return {
type: service.type,
@ -55,6 +55,10 @@ uiModules
// create child ui state from the savedObj
const uiState = panelConfig.uiState || {};
$scope.uiState = $scope.parentUiState.createChild(getPanelId(panelConfig.panel), uiState, true);
const panelSavedVis = _.get(panelConfig, 'savedObj.vis'); // Sometimes this will be a search, and undef
if (panelSavedVis) {
panelSavedVis.setUiState($scope.uiState);
}
$scope.filter = function (field, value, operator) {
const index = $scope.savedObj.searchSource.get('index').id;
@ -75,7 +79,7 @@ uiModules
const service = _.find(services, { type: type });
if (!service) return;
$scope.editUrl = '#settings/objects/' + service.name + '/' + id + '?notFound=' + e.savedObjectType;
$scope.editUrl = '#management/kibana/objects/' + service.name + '/' + id + '?notFound=' + e.savedObjectType;
});
});

View file

@ -68,6 +68,9 @@ app.directive('dashboardApp', function (Notifier, courier, AppState, timefilter,
if (dash.timeRestore && dash.timeTo && dash.timeFrom && !getAppState.previouslyStored()) {
timefilter.time.to = dash.timeTo;
timefilter.time.from = dash.timeFrom;
if (dash.refreshInterval) {
timefilter.refreshInterval = dash.refreshInterval;
}
}
$scope.$on('$destroy', dash.destroy);
@ -204,10 +207,12 @@ app.directive('dashboardApp', function (Notifier, courier, AppState, timefilter,
$state.title = dash.id = dash.title;
$state.save();
const timeRestoreObj = _.pick(timefilter.refreshInterval, ['display', 'pause', 'section', 'value']);
dash.panelsJSON = angular.toJson($state.panels);
dash.uiStateJSON = angular.toJson($uiState.getChanges());
dash.timeFrom = dash.timeRestore ? timefilter.time.from : undefined;
dash.timeTo = dash.timeRestore ? timefilter.time.to : undefined;
dash.refreshInterval = dash.timeRestore ? timeRestoreObj : undefined;
dash.optionsJSON = angular.toJson($state.options);
dash.save()

View file

@ -33,6 +33,7 @@ module.factory('SavedDashboard', function (courier, config) {
timeRestore: false,
timeTo: undefined,
timeFrom: undefined,
refreshInterval: undefined
},
// if an indexPattern was saved with the searchsource of a SavedDashboard
@ -56,6 +57,15 @@ module.factory('SavedDashboard', function (courier, config) {
timeRestore: 'boolean',
timeTo: 'string',
timeFrom: 'string',
refreshInterval: {
type: 'object',
properties: {
display: {type: 'string'},
pause: { type: 'boolean'},
section: { type: 'integer'},
value: { type: 'integer'}
}
}
};
SavedDashboard.searchsource = true;

View file

@ -9,7 +9,7 @@ const module = uiModules.get('app/dashboard');
// Register this service with the saved object registry so it can be
// edited by the object editor.
require('plugins/kibana/settings/saved_object_registry').register({
require('plugins/kibana/management/saved_object_registry').register({
service: 'savedDashboards',
title: 'dashboards'
});

View file

@ -65,7 +65,7 @@ uiRoutes
return savedSearches.get($route.current.params.id)
.catch(courier.redirectWhenMissing({
'search': '/discover',
'index-pattern': '/settings/objects/savedSearches/' + $route.current.params.id
'index-pattern': '/management/kibana/objects/savedSearches/' + $route.current.params.id
}));
}
}
@ -496,7 +496,7 @@ app.controller('discover', function ($scope, config, courier, $route, $window, N
// we have a vis, just modify the aggs
if ($scope.vis) {
const visState = $scope.vis.getState();
const visState = $scope.vis.getEnabledState();
visState.aggs = visStateAggs;
$scope.vis.setState(visState);

View file

@ -0,0 +1,13 @@
import _ from 'lodash';
import $ from 'jquery';
import uiModules from 'ui/modules';
import noResultsTemplate from '../partials/no_results.html';
uiModules
.get('apps/discover')
.directive('discoverNoResults', function () {
return {
restrict: 'E',
template: noResultsTemplate
};
});

View file

@ -56,58 +56,7 @@
<div class="discover-wrapper col-md-10">
<div class="discover-content">
<!-- no results -->
<div ng-show="resultState === 'none'">
<div class="col-md-10 col-md-offset-1">
<h1>No results found <i aria-hidden="true" class="fa fa-meh-o"></i></h1>
<p>
Unfortunately I could not find any results matching your search. I tried really hard. I looked all over the place and frankly, I just couldn't find anything good. Help me, help you. Here are some ideas:
</p>
<div class="shard-failures" ng-show="failures">
<h3>Shard Failures</h3>
<p>The following shard failures ocurred:</p>
<ul>
<li ng-repeat="failure in failures | limitTo: failuresShown"><strong>Index:</strong> {{failure.index}} <strong>Shard:</strong> {{failure.shard}} <strong>Reason:</strong> {{failure.reason}} </li>
</ul>
<a ng-click="showAllFailures()" ng-if="failures.length > failuresShown" title="Show More">Show More</a>
<a ng-click="showLessFailures()" ng-if="failures.length === failuresShown && failures.length > 5" title="Show Less">Show Less</a>
</div>
<div ng-show="opts.timefield">
<p>
<h3>Expand your time range</h3>
<p>I see you are looking at an index with a date field. It is possible your query does not match anything in the current time range, or that there is no data at all in the currently selected time range. Try selecting a wider time range by opening the time picker <i aria-hidden="true" class="fa fa-clock-o"></i> in the top right corner of your screen.
</p>
</div>
<h3>Refine your query</h3>
<p>
The search bar at the top uses Elasticsearch's support for Lucene <a href="http://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html#query-string-syntax" target="_blank">Query String syntax</a>. Let's say we're searching web server logs that have been parsed into a few fields.
</p>
<p>
<h4>Examples:</h4>
Find requests that contain the number 200, in any field:
<pre>200</pre>
Or we can search in a specific field. Find 200 in the status field:
<pre>status:200</pre>
Find all status codes between 400-499:
<pre>status:[400 TO 499]</pre>
Find status codes 400-499 with the extension php:
<pre>status:[400 TO 499] AND extension:PHP</pre>
Or HTML
<pre>status:[400 TO 499] AND (extension:php OR extension:html)</pre>
</p>
</div>
</div>
<discover-no-results ng-show="resultState === 'none'"></discover-no-results>
<!-- loading -->
<div ng-show="resultState === 'loading'">

View file

@ -1,4 +1,5 @@
import 'plugins/kibana/discover/saved_searches/saved_searches';
import 'plugins/kibana/discover/directives/no_results';
import 'plugins/kibana/discover/directives/timechart';
import 'ui/navbar_extensions';
import 'ui/collapsible_sidebar';

View file

@ -0,0 +1,51 @@
<div>
<div class="col-md-10 col-md-offset-1" data-test-subj="discoverNoResults">
<h1>No results found <i aria-hidden="true" class="fa fa-meh-o"></i></h1>
<p>
Unfortunately I could not find any results matching your search. I tried really hard. I looked all over the place and frankly, I just couldn't find anything good. Help me, help you. Here are some ideas:
</p>
<div class="shard-failures" ng-show="failures">
<h3>Shard Failures</h3>
<p>The following shard failures ocurred:</p>
<ul>
<li ng-repeat="failure in failures | limitTo: failuresShown"><strong>Index:</strong> {{failure.index}} <strong>Shard:</strong> {{failure.shard}} <strong>Reason:</strong> {{failure.reason}} </li>
</ul>
<a ng-click="showAllFailures()" ng-if="failures.length > failuresShown" title="Show More">Show More</a>
<a ng-click="showLessFailures()" ng-if="failures.length === failuresShown && failures.length > 5" title="Show Less">Show Less</a>
</div>
<div ng-show="opts.timefield">
<p>
<h3>Expand your time range</h3>
<p>I see you are looking at an index with a date field. It is possible your query does not match anything in the current time range, or that there is no data at all in the currently selected time range. Click the button below to open the time picker. For future reference you can open the time picker by clicking on the <a class="btn btn-xs navbtn" ng-click="kbnTopNav.toggle('filter')" aria-expanded="kbnTopNav.is('filter')" aria-label="time picker" data-test-subj="discoverNoResultsTimefilter"><i aria-hidden="true" class="fa fa-clock-o"></i> time picker</a> button in the top right corner of your screen.
</p>
</div>
<h3>Refine your query</h3>
<p>
The search bar at the top uses Elasticsearch's support for Lucene <a href="http://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html#query-string-syntax" target="_blank">Query String syntax</a>. Let's say we're searching web server logs that have been parsed into a few fields.
</p>
<p>
<h4>Examples:</h4>
Find requests that contain the number 200, in any field:
<pre>200</pre>
Or we can search in a specific field. Find 200 in the status field:
<pre>status:200</pre>
Find all status codes between 400-499:
<pre>status:[400 TO 499]</pre>
Find status codes 400-499 with the extension php:
<pre>status:[400 TO 499] AND extension:PHP</pre>
Or HTML
<pre>status:[400 TO 499] AND (extension:php OR extension:html)</pre>
</p>
</div>
</div>

View file

@ -11,7 +11,7 @@ const module = uiModules.get('discover/saved_searches', [
// Register this service with the saved object registry so it can be
// edited by the object editor.
require('plugins/kibana/settings/saved_object_registry').register({
require('plugins/kibana/management/saved_object_registry').register({
service: 'savedSearches',
title: 'searches'
});

View file

@ -11,7 +11,7 @@ import 'ui/autoload/all';
import 'plugins/kibana/discover/index';
import 'plugins/kibana/visualize/index';
import 'plugins/kibana/dashboard/index';
import 'plugins/kibana/settings/index';
import 'plugins/kibana/management/index';
import 'plugins/kibana/doc';
import 'ui/vislib';
import 'ui/agg_response';

View file

@ -0,0 +1,23 @@
<div class="app-container">
<nav class="navbar navbar-default navbar-static-top subnav" data-test-subj="managementNav">
<bread-crumbs omit-current-page="true"></bread-crumbs>
<ul class="nav navbar-nav">
<li class="current-page" ng-hide="sectionName">
{{::section.display}}
</li>
<li
ng-if="sectionName"
ng-repeat="item in section.items.inOrder"
ng-class="item.class">
<a class="navbar-link" kbn-href="{{::item.url}}" data-test-subj="{{::item.name}}">
{{::item.display}}
</a>
</li>
</ul>
</nav>
<div role="main" class="management-container" ng-transclude></div>
</div>

View file

@ -0,0 +1,64 @@
import _ from 'lodash';
import 'plugins/kibana/management/sections';
import 'plugins/kibana/management/styles/main.less';
import 'ui/filters/start_from';
import 'ui/field_editor';
import 'plugins/kibana/management/sections/indices/_indexed_fields';
import 'plugins/kibana/management/sections/indices/_scripted_fields';
import 'ui/directives/bread_crumbs';
import uiRoutes from 'ui/routes';
import uiModules from 'ui/modules';
import appTemplate from 'plugins/kibana/management/app.html';
import landingTemplate from 'plugins/kibana/management/landing.html';
import chrome from 'ui/chrome/chrome';
import management from 'ui/management';
uiRoutes
.when('/management', {
template: landingTemplate
});
require('ui/index_patterns/route_setup/load_default')({
notRequiredRe: /^\/management\/data\//,
whenMissingRedirectTo: '/management/data/index'
});
uiModules
.get('apps/management')
.directive('kbnManagementApp', function (Private, $route, $location, timefilter, buildNum, buildSha) {
return {
restrict: 'E',
template: appTemplate,
transclude: true,
scope: {
sectionName: '@section'
},
link: function ($scope) {
timefilter.enabled = false;
$scope.sections = management.items.inOrder;
$scope.section = management.getSection($scope.sectionName) || management;
if ($scope.section) {
$scope.section.items.forEach(item => {
item.class = `#${$location.path()}`.indexOf(item.url) > -1 ? 'active' : undefined;
});
}
management.getSection('kibana').info = `Build ${buildNum}, Commit SHA ${buildSha.substr(0, 8)}`;
}
};
});
uiModules
.get('apps/management')
.directive('kbnManagementLanding', function (kbnVersion) {
return {
restrict: 'E',
link: function ($scope) {
$scope.sections = management.items.inOrder;
$scope.kbnVersion = kbnVersion;
}
};
});

View file

@ -0,0 +1,45 @@
<kbn-management-app>
<kbn-management-landing>
<div class="product-overview">
<span class="kibana-version">Version: {{::kbnVersion}}</span>
</div>
<div class="management-sections">
<div
ng-if="section.items.length > 0"
ng-repeat="section in sections"
ng-class="{ 'management-section-info-expanded': section.showInfo }"
class="col-xs-12 management-section management-section-{{::section.id}}">
<div class="panel panel-product management-panel-product">
<div class="panel-heading panel-heading-{{::section.id}}">
{{::section.display}}
<i
class="fa fa-info-circle pull-right panel-heading-icon"
ng-click="section.showInfo = !!!section.showInfo"
ng-if="section.info">
</i>
</div>
<div class="panel-body">
<div class="row">
<ul class="management-section-items list-unstyled">
<li
class="col-xs-4 col-md-3"
ng-repeat="item in section.items.inOrder">
<a class="management-link" kbn-href="{{::item.url}}">
{{::item.display}}
</a>
</li>
</ul>
</div>
<div class="management-section-info">
{{::section.info}}
</div>
</div>
</div>
</div>
</div>
</kbn-management-landing>
</kbn-management-app>

View file

@ -0,0 +1,3 @@
import 'plugins/kibana/management/sections/settings';
import 'plugins/kibana/management/sections/objects';
import 'plugins/kibana/management/sections/indices';

View file

@ -1,6 +1,6 @@
<kbn-settings-app section="indices">
<kbn-settings-indices>
<div ng-controller="settingsIndicesCreate" class="kbn-settings-indices-create">
<kbn-management-app section="data">
<kbn-management-indices>
<div ng-controller="managementIndicesCreate" class="kbn-management-indices-create">
<div class="page-header">
<h1>Configure an index pattern</h1>
In order to use Kibana you must configure at least one index pattern. Index patterns are
@ -177,5 +177,5 @@
</form>
</div>
</div>
</kbn-settings-indices>
</kbn-settings-app>
</kbn-management-indices>
</kbn-management-app>

View file

@ -3,21 +3,20 @@ import moment from 'moment';
import { IndexPatternMissingIndices } from 'ui/errors';
import 'ui/directives/validate_index_name';
import 'ui/directives/auto_select_if_only_one';
import PluginsKibanaSettingsSectionsIndicesRefreshKibanaIndexProvider from 'plugins/kibana/settings/sections/indices/_refresh_kibana_index';
import RefreshKibanaIndex from 'plugins/kibana/management/sections/indices/_refresh_kibana_index';
import uiRoutes from 'ui/routes';
import uiModules from 'ui/modules';
import createTemplate from 'plugins/kibana/settings/sections/indices/_create.html';
import createTemplate from 'plugins/kibana/management/sections/indices/_create.html';
uiRoutes
.when('/settings/indices/', {
.when('/management/data/index/', {
template: createTemplate
});
uiModules.get('apps/settings')
.controller('settingsIndicesCreate', function ($scope, kbnUrl, Private, Notifier, indexPatterns, es, config, Promise) {
uiModules.get('apps/management')
.controller('managementIndicesCreate', function ($scope, kbnUrl, Private, Notifier, indexPatterns, es, config, Promise) {
const notify = new Notifier();
const refreshKibanaIndex = Private(PluginsKibanaSettingsSectionsIndicesRefreshKibanaIndexProvider);
const refreshKibanaIndex = Private(RefreshKibanaIndex);
const intervals = indexPatterns.intervals;
let samplePromise;
@ -73,7 +72,7 @@ uiModules.get('apps/settings')
config.set('defaultIndex', indexPattern.id);
}
indexPatterns.cache.clear(indexPattern.id);
kbnUrl.change('/settings/indices/' + indexPattern.id);
kbnUrl.change('/management/kibana/indices/' + indexPattern.id);
});
}
});

View file

@ -1,13 +1,13 @@
<kbn-settings-app section="indices">
<kbn-settings-indices>
<div ng-controller="settingsIndicesEdit">
<kbn-management-app section="kibana">
<kbn-management-indices>
<div ng-controller="managementIndicesEdit">
<div class="page-header">
<kbn-settings-index-header
<kbn-management-index-header
index-pattern="indexPattern"
set-default="setDefaultPattern()"
refresh-fields="indexPattern.refreshFields()"
delete="removePattern()">
</kbn-settings-index-header>
</kbn-management-index-header>
<p>
This page lists every field in the <strong>{{::indexPattern.id}}</strong>
@ -38,7 +38,7 @@
<br />
<ul class="nav nav-tabs">
<li class="kbn-settings-tab" ng-class="{ active: state.tab === fieldType.index }" ng-repeat="fieldType in fieldTypes">
<li class="kbn-management-tab" ng-class="{ active: state.tab === fieldType.index }" ng-repeat="fieldType in fieldTypes">
<a ng-click="changeTab(fieldType)">
{{ fieldType.title }}
<small>({{ fieldType.count }})</small>
@ -50,5 +50,5 @@
<scripted-fields ng-show="state.tab == 'scriptedFields'" class="fields scripted-fields"></scripted-fields>
</div>
</kbn-settings-indices>
</kbn-settings-app>
</kbn-management-indices>
</kbn-management-app>

View file

@ -1,38 +1,44 @@
import _ from 'lodash';
import 'plugins/kibana/settings/sections/indices/_indexed_fields';
import 'plugins/kibana/settings/sections/indices/_scripted_fields';
import 'plugins/kibana/settings/sections/indices/_index_header';
import PluginsKibanaSettingsSectionsIndicesRefreshKibanaIndexProvider from 'plugins/kibana/settings/sections/indices/_refresh_kibana_index';
import 'plugins/kibana/management/sections/indices/_indexed_fields';
import 'plugins/kibana/management/sections/indices/_scripted_fields';
import 'plugins/kibana/management/sections/indices/_index_header';
import RefreshKibanaIndex from 'plugins/kibana/management/sections/indices/_refresh_kibana_index';
import UrlProvider from 'ui/url';
import PluginsKibanaSettingsSectionsIndicesFieldTypesProvider from 'plugins/kibana/settings/sections/indices/_field_types';
import IndicesFieldTypesProvider from 'plugins/kibana/management/sections/indices/_field_types';
import uiRoutes from 'ui/routes';
import uiModules from 'ui/modules';
import editTemplate from 'plugins/kibana/settings/sections/indices/_edit.html';
import editTemplate from 'plugins/kibana/management/sections/indices/_edit.html';
uiRoutes
.when('/settings/indices/:indexPatternId', {
.when('/management/kibana/indices/:indexPatternId?', {
template: editTemplate,
resolve: {
indexPattern: function ($route, courier) {
return courier.indexPatterns.get($route.current.params.indexPatternId)
.catch(courier.redirectWhenMissing('/settings/indices'));
indexPattern: function ($route, config, courier) {
const params = $route.current.params;
if (typeof params.indexPatternId === 'undefined') {
params.indexPatternId = config.get('defaultIndex');
}
return courier.indexPatterns.get(params.indexPatternId)
.catch(courier.redirectWhenMissing('/management/data/index'));
}
}
});
uiModules.get('apps/settings')
.controller('settingsIndicesEdit', function ($scope, $location, $route, config, courier, Notifier, Private, AppState, docTitle) {
uiModules.get('apps/management')
.controller('managementIndicesEdit', function ($scope, $location, $route, config, courier, Notifier, Private, AppState, docTitle) {
const notify = new Notifier();
const $state = $scope.state = new AppState();
const refreshKibanaIndex = Private(PluginsKibanaSettingsSectionsIndicesRefreshKibanaIndexProvider);
const refreshKibanaIndex = Private(RefreshKibanaIndex);
$scope.kbnUrl = Private(UrlProvider);
$scope.indexPattern = $route.current.locals.indexPattern;
docTitle.change($scope.indexPattern.id);
const otherIds = _.without($route.current.locals.indexPatternIds, $scope.indexPattern.id);
const fieldTypes = Private(PluginsKibanaSettingsSectionsIndicesFieldTypesProvider);
const fieldTypes = Private(IndicesFieldTypesProvider);
$scope.$watch('indexPattern.fields', function () {
$scope.fieldTypes = fieldTypes($scope.indexPattern);
});
@ -65,7 +71,7 @@ uiModules.get('apps/settings')
courier.indexPatterns.delete($scope.indexPattern)
.then(refreshKibanaIndex)
.then(function () {
$location.url('/settings/indices');
$location.url('/management/data/index');
})
.catch(notify.fatal);
};

View file

@ -1,7 +1,7 @@
<kbn-settings-app section="indices">
<kbn-settings-indices>
<kbn-management-app section="kibana">
<kbn-management-indices>
<div class="page-header">
<kbn-settings-index-header index-pattern="fieldSettings.indexPattern"></kbn-settings-index-header>
<kbn-management-index-header index-pattern="fieldSettings.indexPattern"></kbn-management-index-header>
<h2 ng-if="fieldSettings.mode === 'create'">
Create {{ fieldSettings.field.scripted ? 'Scripted ' : '' }}Field
@ -14,5 +14,5 @@
<field-editor index-pattern="fieldSettings.indexPattern" field="fieldSettings.field"></field-editor>
</kbn-settings-indices>
</kbn-settings-app>
</kbn-management-indices>
</kbn-management-app>

View file

@ -1,19 +1,19 @@
import 'ui/field_editor';
import 'plugins/kibana/settings/sections/indices/_index_header';
import 'plugins/kibana/management/sections/indices/_index_header';
import IndexPatternsFieldProvider from 'ui/index_patterns/_field';
import UrlProvider from 'ui/url';
import uiRoutes from 'ui/routes';
import fieldEditorTemplate from 'plugins/kibana/settings/sections/indices/_field_editor.html';
import fieldEditorTemplate from 'plugins/kibana/management/sections/indices/_field_editor.html';
uiRoutes
.when('/settings/indices/:indexPatternId/field/:fieldName', { mode: 'edit' })
.when('/settings/indices/:indexPatternId/create-field/', { mode: 'create' })
.defaults(/settings\/indices\/[^\/]+\/(field|create-field)(\/|$)/, {
.when('/management/kibana/indices/:indexPatternId/field/:fieldName', { mode: 'edit' })
.when('/management/kibana/indices/:indexPatternId/create-field/', { mode: 'create' })
.defaults(/management\/kibana\/indices\/[^\/]+\/(field|create-field)(\/|$)/, {
template: fieldEditorTemplate,
resolve: {
indexPattern: function ($route, courier) {
return courier.indexPatterns.get($route.current.params.indexPatternId)
.catch(courier.redirectWhenMissing('/settings/indices'));
.catch(courier.redirectWhenMissing('/management/kibana/indices'));
}
},
controllerAs: 'fieldSettings',
@ -22,7 +22,6 @@ uiRoutes
const notify = new Notifier({ location: 'Field Editor' });
const kbnUrl = Private(UrlProvider);
this.mode = $route.current.mode;
this.indexPattern = $route.current.locals.indexPattern;
@ -54,4 +53,3 @@ uiRoutes
};
}
});

View file

@ -1,8 +1,8 @@
import uiModules from 'ui/modules';
import indexHeaderTemplate from 'plugins/kibana/settings/sections/indices/_index_header.html';
import indexHeaderTemplate from 'plugins/kibana/management/sections/indices/_index_header.html';
uiModules
.get('apps/settings')
.directive('kbnSettingsIndexHeader', function (config) {
.get('apps/management')
.directive('kbnManagementIndexHeader', function (config) {
return {
restrict: 'E',
template: indexHeaderTemplate,

View file

@ -1,12 +1,12 @@
import _ from 'lodash';
import 'ui/paginated_table';
import nameHtml from 'plugins/kibana/settings/sections/indices/_field_name.html';
import typeHtml from 'plugins/kibana/settings/sections/indices/_field_type.html';
import controlsHtml from 'plugins/kibana/settings/sections/indices/_field_controls.html';
import nameHtml from 'plugins/kibana/management/sections/indices/_field_name.html';
import typeHtml from 'plugins/kibana/management/sections/indices/_field_type.html';
import controlsHtml from 'plugins/kibana/management/sections/indices/_field_controls.html';
import uiModules from 'ui/modules';
import indexedFieldsTemplate from 'plugins/kibana/settings/sections/indices/_indexed_fields.html';
import indexedFieldsTemplate from 'plugins/kibana/management/sections/indices/_indexed_fields.html';
uiModules.get('apps/settings')
uiModules.get('apps/management')
.directive('indexedFields', function ($filter) {
const yesTemplate = '<i class="fa fa-check" aria-label="yes"></i>';
const noTemplate = '';

View file

@ -1,12 +1,12 @@
import _ from 'lodash';
import 'ui/paginated_table';
import popularityHtml from 'plugins/kibana/settings/sections/indices/_field_popularity.html';
import controlsHtml from 'plugins/kibana/settings/sections/indices/_field_controls.html';
import dateScripts from 'plugins/kibana/settings/sections/indices/_date_scripts';
import popularityHtml from 'plugins/kibana/management/sections/indices/_field_popularity.html';
import controlsHtml from 'plugins/kibana/management/sections/indices/_field_controls.html';
import dateScripts from 'plugins/kibana/management/sections/indices/_date_scripts';
import uiModules from 'ui/modules';
import scriptedFieldsTemplate from 'plugins/kibana/settings/sections/indices/_scripted_fields.html';
import scriptedFieldsTemplate from 'plugins/kibana/management/sections/indices/_scripted_fields.html';
uiModules.get('apps/settings')
uiModules.get('apps/management')
.directive('scriptedFields', function (kbnUrl, Notifier, $filter) {
const rowScopes = []; // track row scopes, so they can be destroyed as needed
const filter = $filter('filter');
@ -19,7 +19,7 @@ uiModules.get('apps/settings')
scope: true,
link: function ($scope) {
const fieldCreatorPath = '/settings/indices/{{ indexPattern }}/scriptedField';
const fieldCreatorPath = '/management/kibana/indices/{{ indexPattern }}/scriptedField';
const fieldEditorPath = fieldCreatorPath + '/{{ fieldName }}';
$scope.perPage = 25;

View file

@ -2,10 +2,9 @@
<div class="sidebar-list">
<div class="sidebar-list-header">
<h5>
Index Patterns&nbsp;
<a
ng-if="editingId"
href="#/settings/indices"
href="#/management/data/index"
class="btn btn-primary btn-xs"
aria-label="Add New">
<span class="sr-only">Add New</span>

View file

@ -0,0 +1,64 @@
import management from 'ui/management';
import 'plugins/kibana/management/sections/indices/_create';
import 'plugins/kibana/management/sections/indices/_edit';
import 'plugins/kibana/management/sections/indices/_field_editor';
import uiRoutes from 'ui/routes';
import uiModules from 'ui/modules';
import indexTemplate from 'plugins/kibana/management/sections/indices/index.html';
const indexPatternsResolutions = {
indexPatternIds: function (courier) {
return courier.indexPatterns.getIds();
}
};
// add a dependency to all of the subsection routes
uiRoutes
.defaults(/management\/kibana\/indices/, {
resolve: indexPatternsResolutions
});
uiRoutes
.defaults(/management\/data\/index/, {
resolve: indexPatternsResolutions
});
// wrapper directive, which sets some global stuff up like the left nav
uiModules.get('apps/management')
.directive('kbnManagementIndices', function ($route, config, kbnUrl) {
return {
restrict: 'E',
transclude: true,
template: indexTemplate,
link: function ($scope) {
$scope.editingId = $route.current.params.indexPatternId;
config.bindToScope($scope, 'defaultIndex');
$scope.$watch('defaultIndex', function () {
const ids = $route.current.locals.indexPatternIds;
$scope.indexPatternList = ids.map(function (id) {
return {
id: id,
url: kbnUrl.eval('#/management/kibana/indices/{{id}}', {id: id}),
class: 'sidebar-item-title ' + ($scope.editingId === id ? 'active' : ''),
default: $scope.defaultIndex === id
};
});
});
$scope.$emit('application.load');
}
};
});
management.getSection('data').register('indices', {
display: 'Existing Data',
order: 0,
path: 'data/index/'
});
management.getSection('kibana').register('indices', {
display: 'Index Patterns',
order: 0,
path: 'kibana/indices/'
});

View file

@ -1,5 +1,5 @@
<kbn-settings-app section="objects">
<kbn-settings-objects class="container">
<kbn-management-app section="kibana">
<kbn-management-objects class="container-fluid">
<div class="header">
<h2 class="title">Edit Saved Objects</h2>
<button class="btn btn-default controls" ng-click="exportAll()"><i aria-hidden="true" class="fa fa-download"></i> Export Everything</button>
@ -17,7 +17,7 @@
</form>
<ul class="nav nav-tabs">
<li class="kbn-settings-tab" ng-class="{ active: state.tab === service.title }" ng-repeat="service in services">
<li class="kbn-management-tab" ng-class="{ active: state.tab === service.title }" ng-repeat="service in services">
<a title="{{ service.title }}" ng-click="changeTab(service)">{{ service.title }}
<small>
({{service.data.length}}<span ng-show="service.total > service.data.length"> of {{service.total}}</span>)
@ -76,5 +76,5 @@
</div>
</div>
</kbn-settings-objects>
</kbn-settings-app>
</kbn-management-objects>
</kbn-management-app>

View file

@ -1,8 +1,8 @@
import { saveAs } from '@spalger/filesaver';
import { extend, find, flattenDeep, partialRight, pick, pluck, sortBy } from 'lodash';
import angular from 'angular';
import registry from 'plugins/kibana/settings/saved_object_registry';
import objectIndexHTML from 'plugins/kibana/settings/sections/objects/_objects.html';
import registry from 'plugins/kibana/management/saved_object_registry';
import objectIndexHTML from 'plugins/kibana/management/sections/objects/_objects.html';
import 'ui/directives/file_upload';
import uiRoutes from 'ui/routes';
import uiModules from 'ui/modules';
@ -10,12 +10,12 @@ import uiModules from 'ui/modules';
const MAX_SIZE = Math.pow(2, 31) - 1;
uiRoutes
.when('/settings/objects', {
.when('/management/kibana/objects', {
template: objectIndexHTML
});
uiModules.get('apps/settings')
.directive('kbnSettingsObjects', function (kbnIndex, Notifier, Private, kbnUrl, Promise) {
uiModules.get('apps/management')
.directive('kbnManagementObjects', function (kbnIndex, Notifier, Private, kbnUrl, Promise) {
return {
restrict: 'E',
controller: function ($scope, $injector, $q, AppState, es) {
@ -79,13 +79,16 @@ uiModules.get('apps/settings')
id: item.id
};
kbnUrl.change('/settings/objects/{{ service }}/{{ id }}', params);
kbnUrl.change('/management/kibana/objects/{{ service }}/{{ id }}', params);
};
$scope.bulkDelete = function () {
$scope.currentTab.service.delete(pluck($scope.selectedItems, 'id')).then(refreshData).then(function () {
$scope.currentTab.service.delete(pluck($scope.selectedItems, 'id'))
.then(refreshData)
.then(function () {
$scope.selectedItems.length = 0;
});
})
.catch(error => notify.error(error));
};
$scope.bulkExport = function () {

View file

@ -1,5 +1,5 @@
<kbn-settings-app section="objects">
<kbn-settings-objects-view class="container">
<kbn-management-app section="kibana">
<kbn-management-objects-view class="container">
<div class="pull-right" style="margin-top: 20px;">
<a href="{{ link }}" class="btn btn-primary">View {{ title }}</a>
<a confirm-click="delete()" class="btn btn-danger"><i class="fa fa-trash-o"></i> Delete {{ title }} Object</a>
@ -32,5 +32,5 @@
<button aria-label="Cancel" class="btn btn-primary" ng-click="cancel()">Cancel</button>
<button aria-label="Save {{ title }} Object" class="btn btn-success" ng-click="submit()" ng-disabled="objectForm.$invalid || aceInvalidEditors.length !==0">Save {{ title }} Object</button>
</div>
</kbn-settings-objects-view>
</kbn-settings-app>
</kbn-management-objects-view>
</kbn-management-app>

View file

@ -1,19 +1,19 @@
import _ from 'lodash';
import angular from 'angular';
import rison from 'rison-node';
import registry from 'plugins/kibana/settings/saved_object_registry';
import objectViewHTML from 'plugins/kibana/settings/sections/objects/_view.html';
import registry from 'plugins/kibana/management/saved_object_registry';
import objectViewHTML from 'plugins/kibana/management/sections/objects/_view.html';
import IndexPatternsCastMappingTypeProvider from 'ui/index_patterns/_cast_mapping_type';
import uiRoutes from 'ui/routes';
import uiModules from 'ui/modules';
uiRoutes
.when('/settings/objects/:service/:id', {
.when('/management/kibana/objects/:service/:id', {
template: objectViewHTML
});
uiModules.get('apps/settings')
.directive('kbnSettingsObjectsView', function (kbnIndex, Notifier) {
uiModules.get('apps/management')
.directive('kbnManagementObjectsView', function (kbnIndex, Notifier) {
return {
restrict: 'E',
controller: function ($scope, $injector, $routeParams, $location, $window, $rootScope, es, Private) {
@ -210,7 +210,7 @@ uiModules.get('apps/settings')
.then(function (resp) {
const msg = 'You successfully ' + action + ' the "' + $scope.obj._source.title + '" ' + $scope.title.toLowerCase() + ' object';
$location.path('/settings/objects').search({
$location.path('/management/kibana/objects').search({
_a: rison.encode({
tab: serviceObj.title
})

View file

@ -0,0 +1,15 @@
import management from 'ui/management';
import 'plugins/kibana/management/sections/objects/_view';
import 'plugins/kibana/management/sections/objects/_objects';
import 'ace';
import 'ui/directives/confirm_click';
import uiModules from 'ui/modules';
// add the module deps to this module
uiModules.get('apps/management');
management.getSection('kibana').register('objects', {
display: 'Saved Objects',
order: 10,
path: 'kibana/objects'
});

View file

@ -2,9 +2,9 @@ import _ from 'lodash';
import 'ui/elastic_textarea';
import 'ui/filters/markdown';
import uiModules from 'ui/modules';
import advancedRowTemplate from 'plugins/kibana/settings/sections/advanced/advanced_row.html';
import advancedRowTemplate from 'plugins/kibana/management/sections/settings/advanced_row.html';
uiModules.get('apps/settings')
uiModules.get('apps/management')
.directive('advancedRow', function (config, Notifier) {
return {
restrict: 'A',

View file

@ -1,5 +1,5 @@
<kbn-settings-app section="advanced">
<kbn-settings-advanced class="container">
<kbn-management-app section="kibana">
<kbn-management-advanced class="container-fluid">
<div class="bs-callout bs-callout-warning">
<h4>Caution: You can break stuff here</h4>
Be careful in here, these settings are for very advanced users only.
@ -24,5 +24,5 @@
<tr ng-repeat="conf in configs | filter:advancedFilter" advanced-row="conf" configs="configs"></tr>
</tbody>
</table>
</kbn-settings-advanced>
</kbn-settings-app>
</kbn-management-advanced>
</kbn-management-app>

View file

@ -1,18 +1,18 @@
import _ from 'lodash';
import registry from 'ui/registry/settings_sections';
import toEditableConfig from 'plugins/kibana/settings/sections/advanced/lib/to_editable_config';
import 'plugins/kibana/settings/sections/advanced/advanced_row';
import toEditableConfig from 'plugins/kibana/management/sections/settings/lib/to_editable_config';
import 'plugins/kibana/management/sections/settings/advanced_row';
import management from 'ui/management';
import uiRoutes from 'ui/routes';
import uiModules from 'ui/modules';
import indexTemplate from 'plugins/kibana/settings/sections/advanced/index.html';
import indexTemplate from 'plugins/kibana/management/sections/settings/index.html';
uiRoutes
.when('/settings/advanced', {
.when('/management/kibana/settings', {
template: indexTemplate
});
uiModules.get('apps/settings')
.directive('kbnSettingsAdvanced', function (config, Notifier, Private, $rootScope) {
uiModules.get('apps/management')
.directive('kbnManagementAdvanced', function (config, Notifier, Private, $rootScope) {
return {
restrict: 'E',
link: function ($scope) {
@ -39,9 +39,8 @@ uiModules.get('apps/settings')
};
});
registry.register(_.constant({
order: 2,
name: 'advanced',
display: 'Advanced',
url: '#/settings/advanced'
}));
management.getSection('kibana').register('settings', {
display: 'Advanced Settings',
order: 20,
path: 'kibana/settings'
});

View file

@ -1,5 +1,5 @@
import getEditorType from 'plugins/kibana/settings/sections/advanced/lib/get_editor_type';
import getEditorType from 'plugins/kibana/management/sections/settings/lib/get_editor_type';
import expect from 'expect.js';
describe('Settings', function () {

View file

@ -1,5 +1,5 @@
import getValType from 'plugins/kibana/settings/sections/advanced/lib/get_val_type';
import getValType from 'plugins/kibana/management/sections/settings/lib/get_val_type';
import expect from 'expect.js';
describe('Settings', function () {

View file

@ -1,5 +1,5 @@
import toEditableConfig from 'plugins/kibana/settings/sections/advanced/lib/to_editable_config';
import toEditableConfig from 'plugins/kibana/management/sections/settings/lib/to_editable_config';
import expect from 'expect.js';
describe('Settings', function () {

View file

@ -1,13 +1,14 @@
@import (reference) "~ui/styles/theme";
@import (reference) "~ui/styles/variables";
kibana-settings-app,
kbn-settings-indices,
kbn-settings-indices-edit,
kbn-settings-indices-create,
kbn-settings-advanced,
kbn-settings-objects,
kbn-settings-objects-view {
kbn-management-app,
kbn-management-landing,
kbn-management-indices,
kbn-management-indices-edit,
kbn-management-indices-create,
kbn-management-advanced,
kbn-management-objects,
kbn-management-objects-view {
display: block;
}
@ -15,41 +16,110 @@ nav.navbar {
height: 70px;
}
.tab-management {
background-color: @kibanaGray6;
}
.settings-nav {
text-transform: capitalize;
}
li.kbn-settings-tab:first-letter {
li.kbn-management-tab:first-letter {
text-transform: capitalize;
}
kbn-settings-app {
div.app-container {
div.container-fluid {
padding-left: 0;
padding-right: 0;
}
kbn-management-app {
bread-crumbs ul {
height: 36px;
}
li.current-page {
font-size: 1.5em;
margin: 0 10px;
}
}
kbn-settings-objects {
kbn-management-landing {
padding: 5px;
}
.management-panel-product {
margin-bottom: 10px;
.panel-body {
padding-bottom: 30px;
position: relative;
}
}
.management-section {
padding-left: 5px;
padding-right: 5px;
}
.management-link {
font-size: 17px;
line-height: 32px;
}
.panel-heading-icon {
font-size: 0.9em;
padding: 5px;
}
.product-overview {
color: @kibanaGray2;
padding: 5px 0 10px 10px;
}
.management-section-info {
bottom: 10px;
display: none;
color: @kibanaPink1;
font-size: 0.8em;
position: absolute;
}
.management-section-info-expanded {
.panel-heading-icon {
color: @black;
}
.management-section-info {
display: block;
}
}
kbn-management-objects {
form {
margin-bottom: @line-height-computed;
}
.list-unstyled {
li {
border-bottom: 1px solid;
border-bottom-color: @settings-objects-list-border;
border-bottom-color: @management-objects-list-border;
padding: 8px;
}
}
.empty {
color: @settings-objects-empty-color;
color: @management-objects-empty-color;
}
.item {
padding: 12px;
.item-title {
margin-left: 30px;
}
.actions {
margin-top: -6px;
}
}
.action-bar {
margin-top: 10px;
background-color: @settings-objects-action-bar-bg;
background-color: @management-objects-action-bar-bg;
padding: 4px 12px;
label {
@ -71,7 +141,7 @@ kbn-settings-objects {
}
}
kbn-settings-advanced {
kbn-management-advanced {
// super specific rule to override bootstrap's equally specific rule
// https://github.com/twbs/bootstrap/blob/1f329f8f17aa989eabc6e94bdcab93e69ef0e463/less/tables.less#L35
.table > tbody > tr > td {
@ -79,32 +149,13 @@ kbn-settings-advanced {
}
}
kbn-settings-objects-view {
kbn-management-objects-view {
label {
font-family: @font-family-monospace;
}
.ace_editor { height: 300px; }
}
.kbn-settings-about {
margin-top: 30px;
.jumbotron h1 {
font-weight: bold;
}
&-versions {
td {
width: 50%;
}
td:first-child {
font-weight: bold;
text-align: right;
}
}
}
.advanced-settings {
overflow-x: scroll;
@ -112,7 +163,7 @@ kbn-settings-objects-view {
width: 100%;
tr.default td.value {
color: @settings-advanced-table-default-color;
color: @management-advanced-table-default-color;
}
td {
@ -125,23 +176,9 @@ kbn-settings-objects-view {
}
}
.objects-settings {
.item {
padding: 12px;
.item-title {
margin-left: 30px;
}
.actions {
margin-top: -6px;
}
}
}
.indices-settings {
i.active {
color: @settings-indices-active-color;
color: @management-indices-active-color;
}
tr.field-settings {
@ -168,7 +205,7 @@ kbn-settings-objects-view {
}
}
kbn-settings-indices {
kbn-management-indices {
.fields {
table {
.table-striped()
@ -197,7 +234,8 @@ kbn-settings-indices {
}
}
.kbn-settings-indices-create {
.time-and-pattern > div {}
}
@import "~ui/dragula/gu-dragula.less";

View file

@ -1,14 +0,0 @@
<div class="app-container">
<nav class="navbar navbar-default navbar-static-top subnav" data-test-subj="settingsNav">
<div class="container-fluid">
<bread-crumbs></bread-crumbs>
<ul class="nav navbar-nav">
<li ng-repeat="section in sections" ng-class="section.class">
<a class="navbar-link" kbn-href="{{section.url}}" data-test-subj="{{section.name}}">{{section.display}}</a>
</li>
</ul>
</div>
</nav>
<div role="main" class="settings-section-container {{section.name}}-settings" ng-transclude></div>
</div>

View file

@ -1,53 +0,0 @@
import _ from 'lodash';
import 'plugins/kibana/settings/sections/indices/index';
import 'plugins/kibana/settings/sections/advanced/index';
import 'plugins/kibana/settings/sections/objects/index';
import 'plugins/kibana/settings/sections/status/index';
import 'plugins/kibana/settings/sections/about/index';
import 'plugins/kibana/settings/styles/main.less';
import 'ui/filters/start_from';
import 'ui/field_editor';
import 'plugins/kibana/settings/sections/indices/_indexed_fields';
import 'plugins/kibana/settings/sections/indices/_scripted_fields';
import 'ui/directives/bread_crumbs';
import registry from 'ui/registry/settings_sections';
import uiRoutes from 'ui/routes';
import uiModules from 'ui/modules';
import appTemplate from 'plugins/kibana/settings/app.html';
uiRoutes
.when('/settings', {
redirectTo: '/settings/indices'
});
require('ui/index_patterns/route_setup/load_default')({
notRequiredRe: /^\/settings\//,
whenMissingRedirectTo: '/settings/indices'
});
uiModules
.get('apps/settings')
.directive('kbnSettingsApp', function (Private, $route, timefilter) {
const sections = Private(registry);
return {
restrict: 'E',
template: appTemplate,
transclude: true,
scope: {
sectionName: '@section'
},
link: function ($scope, $el) {
timefilter.enabled = false;
$scope.sections = sections.inOrder;
$scope.section = _.find($scope.sections, { name: $scope.sectionName });
$scope.sections.forEach(section => {
section.class = section === $scope.section ? 'active' : undefined;
});
}
};
});
// preload

View file

@ -1,454 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64"
height="64"
id="svg5235"
version="1.1"
inkscape:version="0.91 r13725"
viewBox="0 0 64 64"
sodipodi:docname="barcode.svg">
<defs
id="defs5237" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.5"
inkscape:cx="4.3636364"
inkscape:cy="32"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:window-width="2880"
inkscape:window-height="1140"
inkscape:window-x="0"
inkscape:window-y="23"
inkscape:window-maximized="0" />
<metadata
id="metadata5240">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<g
id="g4300"
transform="matrix(1.1002186,0,0,1.1002186,-3.2119331,-3.0092752)">
<g
transform="matrix(0.57440105,0,0,0.57440105,-36.987859,-56.970747)"
id="g6209">
<rect
style="opacity:1;fill:#39bdb1;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect5251-3"
width="2.4159346"
height="100.54836"
x="97.825981"
y="104.33428" />
<rect
style="opacity:1;fill:#ea458b;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect5251-9"
width="7.5297971"
height="100.54836"
x="85.735123"
y="104.33428" />
<rect
style="opacity:1;fill:#f3bb19;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect5251-8"
width="5.0022545"
height="100.54836"
x="75.889793"
y="104.33428" />
<rect
style="opacity:1;fill:#2d448e;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect5251-2"
width="3.2287681"
height="100.54836"
x="72.783882"
y="104.33428" />
<rect
style="opacity:1;fill:#83cb1c;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect5251"
width="3.159905"
height="100.54836"
x="69.63768"
y="104.33428" />
<rect
style="opacity:1;fill:#ea458b;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect5251-9-9"
width="6.9419966"
height="100.54836"
x="119.37186"
y="104.33428" />
<rect
style="opacity:1;fill:#006555;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect5251-5-9"
width="4.9434743"
height="100.54836"
x="131.24544"
y="104.33428" />
<rect
style="opacity:1;fill:#006555;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect5251-5-0"
width="1.8869137"
height="100.54836"
x="83.986267"
y="104.33428" />
<rect
style="opacity:1;fill:#83cb1c;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect5251-7"
width="4.7083545"
height="100.54836"
x="93.214729"
y="104.33428" />
<rect
style="opacity:1;fill:#83cb1c;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect5251-36"
width="5.8251753"
height="100.54836"
x="136.24173"
y="104.33428" />
<rect
style="opacity:1;fill:#39bdb1;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect5251-3-7"
width="3.1212945"
height="100.54836"
x="80.870918"
y="104.33428" />
<rect
style="opacity:1;fill:#ea458b;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect5251-9-9-3"
width="3.5915353"
height="100.54836"
x="100.15076"
y="104.33428" />
<rect
style="opacity:1;fill:#ea458b;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect5251-9-9-8"
width="3.9442151"
height="100.54836"
x="166.68982"
y="104.33428" />
<rect
style="opacity:1;fill:#39bdb1;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect5251-3-3"
width="3.3564136"
height="100.54836"
x="110.55486"
y="104.33428" />
<rect
style="opacity:1;fill:#39bdb1;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect5251-3-0"
width="4.1793332"
height="100.54836"
x="148.82066"
y="104.33429" />
<rect
style="opacity:1;fill:#f3bb19;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect5251-8-3"
width="2.1808138"
height="100.54838"
x="129.07057"
y="104.33427" />
<rect
style="opacity:1;fill:#f3bb19;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect5251-8-33"
width="3.0037344"
height="100.54836"
x="142.00218"
y="104.33428" />
<rect
style="opacity:1;fill:#83cb1c;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect5251-7-3"
width="4.3556747"
height="100.54836"
x="106.20512"
y="104.33428" />
<rect
style="opacity:1;fill:#2d448e;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect5251-2-0"
width="2.4159336"
height="100.54836"
x="103.73636"
y="104.33428" />
<rect
style="opacity:1;fill:#83cb1c;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect5251-36-7"
width="3.8266549"
height="100.54836"
x="116.25652"
y="104.33428" />
<rect
style="opacity:1;fill:#006555;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect5251-5-3"
width="2.2983737"
height="100.54838"
x="113.9641"
y="104.33427" />
<rect
style="opacity:1;fill:#39bdb1;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect5251-3-7-7"
width="2.8273947"
height="100.54836"
x="126.30791"
y="104.33428" />
<rect
style="opacity:1;fill:#39bdb1;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect5251-3-7-70"
width="2.0632553"
height="100.54836"
x="144.8824"
y="104.33428" />
<rect
style="opacity:1;fill:#ea458b;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect5251-9-9-8-4"
width="1.8869162"
height="100.54836"
x="146.99847"
y="104.33428" />
<rect
style="opacity:1;fill:#39bdb1;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect5251-3-7-70-2"
width="4.708354"
height="100.54836"
x="158.32114"
y="104.33428" />
<rect
style="opacity:1;fill:#f3bb19;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect5251-8-339-2"
width="5.413713"
height="100.54838"
x="152.97215"
y="104.33427" />
<rect
style="opacity:1;fill:#83cb1c;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect5251-36-5"
width="3.8266544"
height="100.54836"
x="162.99417"
y="104.33428" />
</g>
<g
transform="matrix(0.57440105,0,0,1.9251702,3.0622581,2.9589583)"
id="g4544">
<line
style="fill:none;stroke:#000000;stroke-width:2"
x1="1"
y1="0"
x2="1"
y2="30"
id="line4450" />
<line
style="fill:none;stroke:#000000;stroke-width:1"
x1="3.5"
y1="0"
x2="3.5"
y2="30"
id="line4452" />
<line
style="fill:none;stroke:#000000;stroke-width:1"
x1="6.5"
y1="0"
x2="6.5"
y2="30"
id="line4454" />
<line
style="fill:none;stroke:#000000;stroke-width:1"
x1="11.5"
y1="0"
x2="11.5"
y2="30"
id="line4456" />
<line
style="fill:none;stroke:#000000;stroke-width:1"
x1="14.5"
y1="0"
x2="14.5"
y2="30"
id="line4458" />
<line
style="fill:none;stroke:#000000;stroke-width:2"
x1="17"
y1="0"
x2="17"
y2="30"
id="line4460" />
<line
style="fill:none;stroke:#000000;stroke-width:2"
x1="23"
y1="0"
x2="23"
y2="30"
id="line4462" />
<line
style="fill:none;stroke:#000000;stroke-width:1"
x1="28.5"
y1="0"
x2="28.5"
y2="30"
id="line4464" />
<line
style="fill:none;stroke:#000000;stroke-width:1"
x1="30.5"
y1="0"
x2="30.5"
y2="30"
id="line4466" />
<line
style="fill:none;stroke:#000000;stroke-width:2"
x1="34"
y1="0"
x2="34"
y2="30"
id="line4468" />
<line
style="fill:none;stroke:#000000;stroke-width:2"
x1="37"
y1="0"
x2="37"
y2="30"
id="line4470" />
<line
style="fill:none;stroke:#000000;stroke-width:2"
x1="41"
y1="0"
x2="41"
y2="30"
id="line4472" />
<line
style="fill:none;stroke:#000000;stroke-width:1"
x1="44.5"
y1="0"
x2="44.5"
y2="30"
id="line4474" />
<line
style="fill:none;stroke:#000000;stroke-width:2"
x1="47"
y1="0"
x2="47"
y2="30"
id="line4476" />
<line
style="fill:none;stroke:#000000;stroke-width:1"
x1="50.5"
y1="0"
x2="50.5"
y2="30"
id="line4478" />
<line
style="fill:none;stroke:#000000;stroke-width:2"
x1="56"
y1="0"
x2="56"
y2="30"
id="line4480" />
<line
style="fill:none;stroke:#000000;stroke-width:1"
x1="59.5"
y1="0"
x2="59.5"
y2="30"
id="line4482" />
<line
style="fill:none;stroke:#000000;stroke-width:1"
x1="61.5"
y1="0"
x2="61.5"
y2="30"
id="line4484" />
<line
style="fill:none;stroke:#000000;stroke-width:2"
x1="67"
y1="0"
x2="67"
y2="30"
id="line4486" />
<line
style="fill:none;stroke:#000000;stroke-width:1"
x1="72.5"
y1="0"
x2="72.5"
y2="30"
id="line4488" />
<line
style="fill:none;stroke:#000000;stroke-width:1"
x1="75.5"
y1="0"
x2="75.5"
y2="30"
id="line4490" />
<line
style="fill:none;stroke:#000000;stroke-width:1"
x1="77.5"
y1="0"
x2="77.5"
y2="30"
id="line4492" />
<line
style="fill:none;stroke:#000000;stroke-width:1"
x1="79.5"
y1="0"
x2="79.5"
y2="30"
id="line4494" />
<line
style="fill:none;stroke:#000000;stroke-width:4"
x1="83"
y1="0"
x2="83"
y2="30"
id="line4496" />
<line
style="fill:none;stroke:#000000;stroke-width:2"
x1="89"
y1="0"
x2="89"
y2="30"
id="line4498" />
<line
style="fill:none;stroke:#000000;stroke-width:3"
x1="94.5"
y1="0"
x2="94.5"
y2="30"
id="line4500" />
<line
style="fill:none;stroke:#000000;stroke-width:1"
x1="97.5"
y1="0"
x2="97.5"
y2="30"
id="line4502" />
<line
style="fill:none;stroke:#000000;stroke-width:2"
x1="100"
y1="0"
x2="100"
y2="30"
id="line4504" />
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 16 KiB

View file

@ -1,35 +0,0 @@
<kbn-settings-app section="about">
<div class="kbn-settings-about container" ng-controller="settingsAbout">
<div class="col-md-4 col-md-offset-4 jumbotron">
<center>
<img kbn-src="/plugins/kibana/settings/sections/about/barcode.svg" alt="Kibana Barcode Logo" width="128" height="128"><br>
<h1>Kibana</h1>
<p>
<table class="table table-condensed kbn-settings-about-versions">
<tr>
<td>Version</td>
<td>{{kbnVersion}}</td>
</tr>
<tr>
<td>Build</td>
<td>{{buildNum}}</td>
</tr>
<tr>
<td>Commit SHA</td>
<td>{{buildSha | limitTo:7}}</td>
</tr>
<tr>
<td>Server name</td>
<td>{{serverName}}</td>
</tr>
</table>
</p>
<small>© 2015 All Rights Reserved - <a href="https://elastic.co">Elasticsearch</a></small>
</center>
</div>
</div>
</kbn-settings-app>

View file

@ -1,25 +0,0 @@
import _ from 'lodash';
import registry from 'ui/registry/settings_sections';
import uiRoutes from 'ui/routes';
import uiModules from 'ui/modules';
import indexTemplate from 'plugins/kibana/settings/sections/about/index.html';
uiRoutes
.when('/settings/about', {
template: indexTemplate
});
uiModules.get('apps/settings')
.controller('settingsAbout', function ($scope, kbnVersion, buildNum, buildSha, serverName) {
$scope.kbnVersion = kbnVersion;
$scope.buildNum = buildNum;
$scope.buildSha = buildSha;
$scope.serverName = serverName;
});
registry.register(_.constant({
order: 1001,
name: 'about',
display: 'About',
url: '#/settings/about'
}));

View file

@ -1,54 +0,0 @@
import _ from 'lodash';
import registry from 'ui/registry/settings_sections';
import 'plugins/kibana/settings/sections/indices/_create';
import 'plugins/kibana/settings/sections/indices/_edit';
import 'plugins/kibana/settings/sections/indices/_field_editor';
import uiRoutes from 'ui/routes';
import uiModules from 'ui/modules';
import indexTemplate from 'plugins/kibana/settings/sections/indices/index.html';
// add a dependency to all of the subsection routes
uiRoutes
.defaults(/settings\/indices/, {
resolve: {
indexPatternIds: function (courier) {
return courier.indexPatterns.getIds();
}
}
});
// wrapper directive, which sets some global stuff up like the left nav
uiModules.get('apps/settings')
.directive('kbnSettingsIndices', function ($route, config, kbnUrl) {
return {
restrict: 'E',
transclude: true,
template: indexTemplate,
link: function ($scope) {
$scope.editingId = $route.current.params.indexPatternId;
config.bindToScope($scope, 'defaultIndex');
$scope.$watch('defaultIndex', function () {
const ids = $route.current.locals.indexPatternIds;
$scope.indexPatternList = ids.map(function (id) {
return {
id: id,
url: kbnUrl.eval('#/settings/indices/{{id}}', {id: id}),
class: 'sidebar-item-title ' + ($scope.editingId === id ? 'active' : ''),
default: $scope.defaultIndex === id
};
});
});
$scope.$emit('application.load');
}
};
});
registry.register(_.constant({
order: 1,
name: 'indices',
display: 'Indices',
url: '#/settings/indices'
}));

View file

@ -1,18 +0,0 @@
import _ from 'lodash';
import registry from 'ui/registry/settings_sections';
import 'plugins/kibana/settings/sections/objects/_view';
import 'plugins/kibana/settings/sections/objects/_objects';
import 'ace';
import 'ui/directives/confirm_click';
import uiModules from 'ui/modules';
// add the module deps to this module
uiModules.get('apps/settings');
registry.register(_.constant({
order: 3,
name: 'objects',
display: 'Objects',
url: '#/settings/objects'
}));

View file

@ -1,9 +0,0 @@
import _ from 'lodash';
import registry from 'ui/registry/settings_sections';
registry.register(_.constant({
order: 1000,
name: 'status',
display: 'Status',
url: '/status'
}));

View file

@ -0,0 +1,121 @@
import angular from 'angular';
import sinon from 'sinon';
import expect from 'expect.js';
import ngMock from 'ng_mock';
let init;
let $rootScope;
let $compile;
describe('draggable_* directives', function () {
beforeEach(ngMock.module('kibana'));
beforeEach(ngMock.inject(function ($injector) {
$rootScope = $injector.get('$rootScope');
$compile = $injector.get('$compile');
init = function init(markup = '') {
const $parentScope = $rootScope.$new();
$parentScope.items = [
{ name: 'item_1' },
{ name: 'item_2' },
{ name: 'item_3' }
];
// create the markup
const $elem = angular.element(`<div draggable-container="items">`);
$elem.html(markup);
// compile the directive
$compile($elem)($parentScope);
$parentScope.$apply();
const $scope = $elem.scope();
return { $parentScope, $scope, $elem };
};
}));
describe('draggable_container directive', function () {
it('should expose the drake', function () {
const { $scope } = init();
expect($scope.drake).to.be.an(Object);
});
it('should expose the controller', function () {
const { $scope } = init();
expect($scope.draggableContainerCtrl).to.be.an(Object);
});
it('should pull item list from directive attribute', function () {
const { $scope, $parentScope } = init();
expect($scope.draggableContainerCtrl.getList()).to.eql($parentScope.items);
});
it('should not be able to move extraneous DOM elements', function () {
const bare = angular.element(`<div>`);
const { $scope } = init();
expect($scope.drake.canMove(bare[0])).to.eql(false);
});
it('should not be able to move non-[draggable-item] elements', function () {
const bare = angular.element(`<div>`);
const { $scope, $elem } = init();
$elem.append(bare);
expect($scope.drake.canMove(bare[0])).to.eql(false);
});
it('shouldn\'t be able to move extraneous [draggable-item] elements', function () {
const anotherParent = angular.element(`<div draggable-container="items">`);
const item = angular.element(`<div draggable-item="items[0]">`);
const scope = $rootScope.$new();
anotherParent.append(item);
$compile(anotherParent)(scope);
$compile(item)(scope);
scope.$apply();
const { $scope } = init();
expect($scope.drake.canMove(item[0])).to.eql(false);
});
it('shouldn\'t be able to move [draggable-item] if it has a handle', function () {
const { $scope, $elem } = init(`
<div draggable-item="items[0]">
<div draggable-handle></div>
</div>
`);
const item = $elem.find(`[draggable-item]`);
expect($scope.drake.canMove(item[0])).to.eql(false);
});
it('should be able to move [draggable-item] by its handle', function () {
const { $scope, $elem } = init(`
<div draggable-item="items[0]">
<div draggable-handle></div>
</div>
`);
const handle = $elem.find(`[draggable-handle]`);
expect($scope.drake.canMove(handle[0])).to.eql(true);
});
});
describe('draggable_item', function () {
it('should be required to be a child to [draggable-container]', function () {
const item = angular.element(`<div draggable-item="items[0]">`);
const scope = $rootScope.$new();
expect(() => {
$compile(item)(scope);
scope.$apply();
}).to.throwException(/controller(.+)draggableContainer(.+)required/i);
});
});
describe('draggable_handle', function () {
it('should be required to be a child to [draggable-item]', function () {
const handle = angular.element(`<div draggable-handle>`);
const scope = $rootScope.$new();
expect(() => {
$compile(handle)(scope);
scope.$apply();
}).to.throwException(/controller(.+)draggableItem(.+)required/i);
});
});
});

View file

@ -27,30 +27,40 @@
<!-- controls !!!actually disabling buttons will break tooltips¡¡¡ -->
<div class="vis-editor-agg-header-controls btn-group">
<!-- up button -->
<!-- disable aggregation -->
<button
aria-label="Increase Priority"
ng-if="stats.count > 1"
ng-class="{ disabled: $first }"
ng-click="moveUp(agg)"
tooltip="Increase Priority"
ng-if="agg.enabled && canRemove(agg)"
ng-click="agg.enabled = false"
aria-label="Disable aggregation"
tooltip="Disable aggregation"
tooltip-append-to-body="true"
type="button"
class="btn btn-xs btn-primary">
<i aria-hidden="true" class="fa fa-caret-up"></i>
class="btn btn-xs">
<i aria-hidden="true" class="fa fa-toggle-on"></i>
</button>
<!-- down button -->
<!-- enable aggregation -->
<button
aria-label="Decrease Priority"
ng-if="stats.count > 1"
ng-class="{ disabled: $last }"
ng-click="moveDown(agg)"
tooltip="Decrease Priority"
ng-if="!agg.enabled"
ng-click="agg.enabled = true"
aria-label="Enable aggregation"
tooltip="Enable aggregation"
tooltip-append-to-body="true"
type="button"
class="btn btn-xs btn-primary">
<i aria-hidden="true" class="fa fa-caret-down"></i>
class="btn btn-xs">
<i aria-hidden="true" class="fa fa-toggle-off"></i>
</button>
<!-- drag handle -->
<button
draggable-handle
aria-label="Modify Priority by Dragging"
ng-if="stats.count > 1"
tooltip="Modify Priority by Dragging"
tooltip-append-to-body="true"
type="button"
class="btn btn-xs">
<i aria-hidden="true" class="fa fa-arrows-v"></i>
</button>
<!-- remove button -->
@ -79,5 +89,6 @@
<vis-editor-agg-add
ng-if="$index + 1 === stats.count"
ng-hide="dragging"
class="vis-editor-agg-add vis-editor-agg-add-subagg">
</vis-editor-agg-add>

View file

@ -21,7 +21,6 @@ uiModules
template: aggTemplate,
require: 'form',
link: function ($scope, $el, attrs, kbnForm) {
$scope.$bind('outputAgg', 'outputVis.aggs.byId[agg.id]', $scope);
$scope.editorOpen = !!$scope.agg.brandNew;
$scope.$watch('editorOpen', function (open) {
@ -47,13 +46,16 @@ uiModules
return label ? label : '';
};
function move(below, agg) {
_.move($scope.vis.aggs, agg, below, function (otherAgg) {
return otherAgg.schema.group === agg.schema.group;
});
}
$scope.moveUp = _.partial(move, false);
$scope.moveDown = _.partial(move, true);
$scope.$on('drag-start', e => {
$scope.editorWasOpen = $scope.editorOpen;
$scope.editorOpen = false;
$scope.$emit('agg-drag-start', $scope.agg);
});
$scope.$on('drag-end', e => {
$scope.editorOpen = $scope.editorWasOpen;
$scope.$emit('agg-drag-end', $scope.agg);
});
$scope.remove = function (agg) {
const aggs = $scope.vis.aggs;

View file

@ -3,9 +3,9 @@
{{ groupName }}
</div>
<div class="vis-editor-agg-group" ng-class="groupName">
<div ng-class="groupName" draggable-container="vis.aggs" class="vis-editor-agg-group">
<!-- wrapper needed for nesting-indicator -->
<div ng-repeat="agg in group" class="vis-editor-agg-wrapper">
<div ng-repeat="agg in group" draggable-item="agg" class="vis-editor-agg-wrapper">
<!-- agg.html - controls for aggregation -->
<ng-form vis-editor-agg name="aggForm" class="vis-editor-agg"></ng-form>
</div>

View file

@ -41,6 +41,9 @@ uiModules
if (count < schema.max) return true;
});
});
$scope.$on('agg-drag-start', e => $scope.dragging = true);
$scope.$on('agg-drag-end', e => $scope.dragging = false);
}
};

View file

@ -0,0 +1,88 @@
import _ from 'lodash';
import $ from 'jquery';
import dragula from 'dragula';
import uiModules from 'ui/modules';
uiModules
.get('app/visualize')
.directive('draggableContainer', function () {
return {
restrict: 'A',
scope: true,
controllerAs: 'draggableContainerCtrl',
controller($scope, $attrs, $parse) {
this.getList = () => $parse($attrs.draggableContainer)($scope);
},
link($scope, $el, attr) {
const drake = dragula({
containers: $el.toArray(),
moves(el, source, handle) {
const itemScope = $(el).scope();
if (!('draggableItemCtrl' in itemScope)) {
return; // only [draggable-item] is draggable
}
return itemScope.draggableItemCtrl.moves(handle);
}
});
const drakeEvents = [
'cancel',
'cloned',
'drag',
'dragend',
'drop',
'out',
'over',
'remove',
'shadow'
];
const prettifiedDrakeEvents = {
drag: 'start',
dragend: 'end'
};
drakeEvents.forEach(type => {
drake.on(type, (el, ...args) => forwardEvent(type, el, ...args));
});
drake.on('drag', markDragging(true));
drake.on('dragend', markDragging(false));
drake.on('drop', drop);
$scope.$on('$destroy', drake.destroy);
$scope.drake = drake;
function markDragging(isDragging) {
return el => {
const scope = $(el).scope();
scope.isDragging = isDragging;
scope.$apply();
};
}
function forwardEvent(type, el, ...args) {
const name = `drag-${prettifiedDrakeEvents[type] || type}`;
const scope = $(el).scope();
scope.$broadcast(name, el, ...args);
}
function drop(el, target, source, sibling) {
const list = $scope.draggableContainerCtrl.getList();
const itemScope = $(el).scope();
const item = itemScope.draggableItemCtrl.getItem();
const toIndex = getSiblingItemIndex(list, sibling);
_.move(list, item, toIndex);
}
function getSiblingItemIndex(list, sibling) {
if (!sibling) { // means the item was dropped at the end of the list
return list.length - 1;
}
const siblingScope = $(sibling).scope();
const siblingItem = siblingScope.draggableItemCtrl.getItem();
const siblingIndex = list.indexOf(siblingItem);
return siblingIndex;
}
}
};
});

View file

@ -0,0 +1,14 @@
import uiModules from 'ui/modules';
uiModules
.get('app/visualize')
.directive('draggableHandle', function () {
return {
restrict: 'A',
require: '^draggableItem',
link($scope, $el, attr, ctrl) {
ctrl.registerHandle($el);
$el.addClass('gu-handle');
}
};
});

View file

@ -0,0 +1,29 @@
import $ from 'jquery';
import uiModules from 'ui/modules';
uiModules
.get('app/visualize')
.directive('draggableItem', function () {
return {
restrict: 'A',
require: '^draggableContainer',
scope: true,
controllerAs: 'draggableItemCtrl',
controller($scope, $attrs, $parse) {
const dragHandles = $();
this.getItem = () => $parse($attrs.draggableItem)($scope);
this.registerHandle = $el => {
dragHandles.push(...$el);
};
this.moves = handle => {
const $handle = $(handle);
const $anywhereInParentChain = $handle.parents().addBack();
const movable = dragHandles.is($anywhereInParentChain);
return movable;
};
},
link($scope, $el, attr) {
}
};
});

View file

@ -43,9 +43,9 @@ uiRoutes
return savedVisualizations.get($route.current.params.id)
.catch(courier.redirectWhenMissing({
'visualization': '/visualize',
'search': '/settings/objects/savedVisualizations/' + $route.current.params.id,
'index-pattern': '/settings/objects/savedVisualizations/' + $route.current.params.id,
'index-pattern-field': '/settings/objects/savedVisualizations/' + $route.current.params.id
'search': '/management/kibana/objects/savedVisualizations/' + $route.current.params.id,
'index-pattern': '/management/kibana/objects/savedVisualizations/' + $route.current.params.id,
'index-pattern-field': '/management/kibana/objects/savedVisualizations/' + $route.current.params.id
}));
}
}
@ -119,8 +119,8 @@ uiModules
if (!angular.equals($state.vis, savedVisState)) {
Promise.try(function () {
vis.setState($state.vis);
editableVis.setState($state.vis);
vis.setState(editableVis.getEnabledState());
})
.catch(courier.redirectWhenMissing({
'index-pattern-field': '/visualize'
@ -139,6 +139,7 @@ uiModules
$scope.editableVis = editableVis;
$scope.state = $state;
$scope.uiState = $state.makeStateful('uiState');
vis.setUiState($scope.uiState);
$scope.timefilter = timefilter;
$scope.opts = _.pick($scope, 'doSave', 'savedVis', 'shareData', 'timefilter');
@ -149,9 +150,9 @@ uiModules
$scope.stageEditableVis = transferVisState(editableVis, vis, true);
$scope.resetEditableVis = transferVisState(vis, editableVis);
$scope.$watch(function () {
return editableVis.getState();
return editableVis.getEnabledState();
}, function (newState) {
editableVis.dirty = !angular.equals(newState, vis.getState());
editableVis.dirty = !angular.equals(newState, vis.getEnabledState());
$scope.responseValueAggs = null;
try {
@ -291,14 +292,16 @@ uiModules
}
};
function transferVisState(fromVis, toVis, fetch) {
function transferVisState(fromVis, toVis, stage) {
return function () {
toVis.setState(fromVis.getState());
const view = fromVis.getEnabledState();
const full = fromVis.getState();
toVis.setState(view);
editableVis.dirty = false;
$state.vis = vis.getState();
$state.vis = full;
$state.save();
if (fetch) $scope.fetch();
if (stage) $scope.fetch();
};
}

View file

@ -15,7 +15,6 @@ uiModules
controllerAs: 'sidebar',
controller: function ($scope) {
$scope.$bind('vis', 'editableVis');
$scope.$bind('outputVis', 'vis');
$scope.$watch('vis.type', (visType) => {
if (visType) {
this.showData = visType.schemas.buckets || visType.schemas.metrics;

Some files were not shown because too many files have changed in this diff Show more