From 6e72d97a7ac2454b1463dd976a31c9b220ab0e68 Mon Sep 17 00:00:00 2001 From: Bill Murrin Date: Fri, 12 Jan 2018 18:45:33 -1000 Subject: [PATCH] bump version, support config toggle of add/remove search, exclude query, term hyperlinks and new window --- CHANGELOG.md | 12 ++ README.md | 88 +++++------ package.json | 2 +- pom.xml | 2 +- ...QuickValuesPlusDefaultValuesMigration.java | 22 ++- .../QuickValuesPlusPluginConfiguration.java | 12 +- ...QuickValuesPlusPluginConfiguration3_1.java | 98 ++++++++++++ .../QuickValuesPlusWidgetModule.java | 2 - src/web/components/FieldQuickValuesPlus.jsx | 105 +++++++------ .../QuickValuesPlusDefaultConfig.css | 12 ++ .../QuickValuesPlusDefaultConfig.jsx | 145 ++++++++++++++++-- .../QuickValuesPlusVisualization.jsx | 126 +++++++++++---- ...ickValuesPlusWidgetCreateConfiguration.jsx | 44 +++--- ...QuickValuesPlusWidgetEditConfiguration.jsx | 25 +-- src/web/index.jsx | 2 +- 15 files changed, 509 insertions(+), 188 deletions(-) create mode 100644 src/main/java/org/graylog/plugins/quickvaluesplus/QuickValuesPlusPluginConfiguration3_1.java create mode 100644 src/web/components/QuickValuesPlusDefaultConfig.css diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e02bcf..ae3fce8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +## [3.1.0](https://github.com/billmurrin/graylog-plugin-quickvaluesplus-widget/tree/3.1.0) (2018-01-12) +* Merged #32 (@pbr0ck3r) - Add options to show/hide search buttons when creating a widget +* Now supports the configuration of defaults for the following: + - Add / Remove to Search Bar (Field Analyzer) + - Pie Chart Visibility + - Show Terms with Hyperlinks (Only applies to Dashboards) + - Exclude Term from Widget Search query button (Only applies to Dashboards) + - Open Search Term Query in New Window (Only applies to Dashboards) + - *Data Table Visibility is not a global configuration, only on widgets* +* Fixed #31 - Quick Values in Dashboard keep changing colors +* Removed debug code added in version 3.0. Not needed. + ## [3.0.3](https://github.com/billmurrin/graylog-plugin-quickvaluesplus-widget/tree/3.0.2) (2018-01-06) * Fixed *Open Search in New Window* button for `undefined` stream_id. 3.0.2 only fixed the links, not the button. * Fixed bug - Global config value wasn't being sent to the widget upon the initial widget load - it required a menu selection first. diff --git a/README.md b/README.md index 9d66cf6..be00d4d 100644 --- a/README.md +++ b/README.md @@ -3,96 +3,98 @@ [![GitHub Release](https://img.shields.io/github/release/billmurrin/graylog-plugin-quickvaluesplus-widget.svg)](https://github.com/billmurrin/graylog-plugin-quickvaluesplus-widget/releases) [![Build Status](https://travis-ci.org/billmurrin/graylog-plugin-quickvaluesplus-widget.svg?branch=master)](https://travis-ci.org/billmurrin/graylog-plugin-quickvaluesplus-widget) -The QuickValuesPlus Widget is an enhancement to the Quick Values Widget that ships with GrayLog2. The QuickValuesPlus Widget can be added to a Stream as a Field Analyzer and to Dashboards as a widget. - -Several **NEW** features have been added to version 3.0.0 (See below). Version 3.0.0 is only compatible with Graylog 2.3.0 and above. +The QuickValuesPlus Widget is an enhancement to the original Quick Values Widget that shipped with GrayLog2. It can be added to a Stream as a Field Analyzer and to Dashboards as a widget. +Several **NEW** features have been added to version 3.1.0 (See below). Version 3.1.0 is only compatible with Graylog 2.3 (not 2.4 yet). Field Analyzer Features ----------- - "Customize" menu supports modifying the Sort Order (Ascending or Descending), Table Size and number of Top Values displayed by the widget. - Exclude from search button - Add negated search directly to the Search Bar (E.g. !field_name:foo). +- **New in 3.1** - Can prevent Add to Search/Remove from search buttons via global configuration. *Field Analyzer Features* ![alt text](http://i.imgur.com/H9SRkRo.png "Field Analyzer Features") - -New Dashboard Features +Dashboard Features ----------- -- Widgets now contain links so that you can drill into a Term search and view the results. -- Widgets now contain a button to open the Term search inside of a new window. -- Added Exclude Term from Query button - Negate a search term directly from the widget (Requires dashboard edit permissions) +- Terms can be hyperlinked - drill-down into a Term search and view the results. +- Term searches can be opened inside of a new window. +- Can exclude a Term from the Query using a button - Negate a search term directly from the widget (*Requires dashboard edit permissions*) +- **New in 3.1** - Term hyperlinks, Remove from query, and Open Term search in new window buttons can be hidden - very customizable! -*NEW - Version 3 Dashboard Features* -![alt text](http://i.imgur.com/viZ3AoK.png "Version 3 Dashboard Features") +*Customize your dashboards* +![alt text](https://i.imgur.com/GSm9Yb1.png "Dashboard customization") -New Dashboard Configuration Feature +Dashboard Configuration Feature ----------- -- Can now customize the field name used by the widget (Requires dashboard edit permissions) - +- Can customize the field name used by the widget (Requires dashboard edit permissions) +- **New in 3.1** Can now control display of term hyperlinks, remove from query, open term in new window buttons in dashboards. -*NEW - Version 3 Editable Field Name* -![alt text](http://i.imgur.com/Ezs7DL3.png "Edit Field Name") - -New Global System Configuration +Global System Configuration ----------- -- Can now Customize the Quick Values Plus default values in the System/Configuration page. - -*NEW - Version 3 Globally Configurable Default Values* -![alt text](http://i.imgur.com/DZHbWzh.png "System Global Default Values") +Customize the default display values in the System/Configuration page. +- Pie Chart +- Add to Search Bar button +- Remove from Search Bar button +- Term Hyperlinks (Dashboards) +- Exclude From Search Button (Dashboards) +- Open Term Search in New Window Button (Dashboards) Supported Graylog Versions ----------- -* Version 3.0.1 was tested and is compatible with Graylog version 2.3.0 and above. -* Version 2.1.0 was tested and is compatible with Graylog versions 2.2.1, 2.2.2, and 2.2.3. -* Version 1.0.0 was tested and is compatible with Graylog version 2.1.3. +* **Not currently compatible with Graylog 2.4.0 - *coming soon!*** +* Version 3.1.0 - Tested and compatible with Graylog version 2.3 - not currently compatible with 2.4.0. +* Version 2.1.0 - Tested and compatible with Graylog versions 2.2.1, 2.2.2, and 2.2.3. +* Version 1.0.0 - Tested and compatible with Graylog version 2.1.3. +Installation +------------ +* [Download the plugin](https://github.com/billmurrin/graylog-plugin-quickvaluesplus-widget/releases/) +and place the `.jar` file in your Graylog plugin directory. The plugin directory is the `plugins/` folder relative from your `graylog-server` directory by default and can be configured in your `graylog.conf` file. +* Restart `graylog-server`. +* In your web browser, force refresh (Ctrl + F5) a couple of times or clear your cache. + Features and Bugs ----------- If you come across a bug, require further assistance, or have a great feature request, please file an [Issue](https://github.com/billmurrin/graylog-plugin-quickvaluesplus-widget/issues) providing as much detail as possible. -Contributions to the code-base are greatly appreciated. +Contributions to the code-base are greatly appreciated! + +Way Ahead - Version 4.0.0 +----------- +* Update codebase to work with Graylog 2.4.0 Related Graylog Issues ----------- -The following are Graylog issues that the QuickValuesPlus widget currently attempts to resolve. +The following are Graylog issues that the QuickValuesPlus widget attempts to resolve. * [#2459](https://github.com/Graylog2/graylog2-server/issues/2459) - Reverse quick values/Bottom-N * [#2631](https://github.com/Graylog2/graylog2-server/issues/2631) - Feature request: rare values #2631 * [#1684](https://github.com/Graylog2/graylog2-server/issues/1684) - Make number of terms in quick values widget configurable * [#3694](https://github.com/Graylog2/graylog2-server/issues/3694) - Add hyperlinks to "Quick Values" dashboard widgets * [#3394](https://github.com/Graylog2/graylog2-server/issues/3394) - Widgets has no way to edit all properties after creation - -Installation ------------- -[Download the plugin](https://github.com/billmurrin/graylog-plugin-quickvaluesplus-widget/releases/) -and place the `.jar` file in your Graylog plugin directory. The plugin directory is the `plugins/` folder relative from your `graylog-server` directory by default and can be configured in your `graylog.conf` file. - -Restart `graylog-server` and you are done. - -Way Ahead - Version 3.1.0 ------------ - * Add additional Default Configuration options (show table, show pie chart, links, exclude query button) - * Add ability to Turn Off links and/or exclude from Query buttons for each individual widget in the Widget Configuration. - * Add debug configuration option to enable console messaging in order to help in troubleshooting issues with the plugin. Development ----------- You can improve your development experience for the web interface part of your plugin dramatically by making use of hot reloading. -To hot reload using Graylog 2.3.0, your plugin directory should be located two directories above your graylog2-web-server directory (../../) and the folder name of your plugin should be begin with graylog-plugin (More info[HERE](https://github.com/Graylog2/graylog2-server/blob/2.3/graylog2-web-interface/webpack.combined.config.js#L11)) +To hot reload using Graylog 2.3, your plugin directory should be located two directories above your graylog2-web-server directory (../../) and the folder name of your plugin should be begin with graylog-plugin (More info[HERE](https://github.com/Graylog2/graylog2-server/blob/2.3/graylog2-web-interface/webpack.combined.config.js#L11)) + +##### Hot-loading setup with the plugin. -#####Steps for hot-loading setup with the plugin. * Clone the Repositories ``` -git clone -b "2.3.1" https://github.com/Graylog2/graylog2-server.git +git clone -b "2.3.2" https://github.com/Graylog2/graylog2-server.git git clone https://github.com/billmurrin/graylog-plugin-quickvaluesplus-widget.git ``` + * Install the `graylog2-web-interface` node modules and build the Vendor Manifest ``` cd graylog2-server/graylog2-web-interface npm install webpack --config webpack.vendor.js ``` + * Install the `graylog-plugin-quickvaluesplus-widget` node modules ``` cd graylog-plugin-quickvaluesplus-widget @@ -105,7 +107,7 @@ cd graylog2-server/graylog2-web-interface npm start ``` -#####Steps to build the plugin. +##### Building the plugin. * Follow the steps above for hot-loading, but **DO NOT** run the `npm start` command. (no need to start the dev web-server) * Run `mvn package` * Copy the generated JAR file located in the `/target` folder to the Graylog plugin directory. diff --git a/package.json b/package.json index 0660012..5e6466a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "QuickValuesPlusWidget", - "version": "3.0.3", + "version": "3.1.0", "description": "GrayLog2 QuickValuesPlus Widget Plugin", "repository": { "type": "git", diff --git a/pom.xml b/pom.xml index 65130d4..99e54fa 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ org.graylog.plugins graylog-plugin-quickvaluesplus-widget - 3.0.3 + 3.1.0 jar ${project.artifactId} diff --git a/src/main/java/org/graylog/plugins/quickvaluesplus/QuickValuesPlusDefaultValuesMigration.java b/src/main/java/org/graylog/plugins/quickvaluesplus/QuickValuesPlusDefaultValuesMigration.java index 87af971..681dded 100644 --- a/src/main/java/org/graylog/plugins/quickvaluesplus/QuickValuesPlusDefaultValuesMigration.java +++ b/src/main/java/org/graylog/plugins/quickvaluesplus/QuickValuesPlusDefaultValuesMigration.java @@ -30,13 +30,25 @@ public ZonedDateTime createdAt() { @Override @SuppressWarnings("unchecked") public void upgrade() { - if (clusterConfigService.get(QuickValuesPlusPluginConfiguration.class) != null) { - LOG.debug("Migration already done."); + + final QuickValuesPlusPluginConfiguration quickValuesPlusPluginConfiguration = clusterConfigService.get(QuickValuesPlusPluginConfiguration.class); + final QuickValuesPlusPluginConfiguration3_1 quickValuesPlusPluginConfiguration3_1 = clusterConfigService.get(QuickValuesPlusPluginConfiguration3_1.class); + + if (quickValuesPlusPluginConfiguration3_1 == null) { + if (quickValuesPlusPluginConfiguration == null) { + LOG.info("No Migration Found. Writing values for Quick Values Plugin Configuration"); + clusterConfigService.write(QuickValuesPlusPluginConfiguration3_1.create(25, 5, "descending", true, true,true,true, true, true,"3.1.0")); + } else { + LOG.info("3.0 Migration Found. Updating to 3.1"); + clusterConfigService.write(QuickValuesPlusPluginConfiguration3_1.create(quickValuesPlusPluginConfiguration.tableSize(), quickValuesPlusPluginConfiguration.topValues(), quickValuesPlusPluginConfiguration.sortOrder(), true, true, true, true, true,true, "3.1.0")); + LOG.info("Removing 3.0 migration information"); + clusterConfigService.remove(QuickValuesPlusPluginConfiguration.class); + } + } else { + LOG.info("Migration has already completed. Exiting."); return; - } - LOG.info("Writing values for Quick Values Plugin Configuration"); - clusterConfigService.write(QuickValuesPlusPluginConfiguration.create(25, 5, "descending")); + } } } \ No newline at end of file diff --git a/src/main/java/org/graylog/plugins/quickvaluesplus/QuickValuesPlusPluginConfiguration.java b/src/main/java/org/graylog/plugins/quickvaluesplus/QuickValuesPlusPluginConfiguration.java index 2d53a55..dad14a2 100644 --- a/src/main/java/org/graylog/plugins/quickvaluesplus/QuickValuesPlusPluginConfiguration.java +++ b/src/main/java/org/graylog/plugins/quickvaluesplus/QuickValuesPlusPluginConfiguration.java @@ -22,13 +22,13 @@ public abstract class QuickValuesPlusPluginConfiguration { @JsonCreator public static QuickValuesPlusPluginConfiguration create(@JsonProperty("table_size") Number tableSize, - @JsonProperty("top_values") Number topValues, - @JsonProperty("sort_order") String sortOrder) { + @JsonProperty("top_values") Number topValues, + @JsonProperty("sort_order") String sortOrder) { return builder() - .tableSize(tableSize) - .topValues(topValues) - .sortOrder(sortOrder) - .build(); + .tableSize(tableSize) + .topValues(topValues) + .sortOrder(sortOrder) + .build(); } public static Builder builder() { diff --git a/src/main/java/org/graylog/plugins/quickvaluesplus/QuickValuesPlusPluginConfiguration3_1.java b/src/main/java/org/graylog/plugins/quickvaluesplus/QuickValuesPlusPluginConfiguration3_1.java new file mode 100644 index 0000000..3b8b41f --- /dev/null +++ b/src/main/java/org/graylog/plugins/quickvaluesplus/QuickValuesPlusPluginConfiguration3_1.java @@ -0,0 +1,98 @@ +package org.graylog.plugins.quickvaluesplus; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.auto.value.AutoValue; + +@JsonAutoDetect +@JsonIgnoreProperties(ignoreUnknown = true) +@AutoValue +public abstract class QuickValuesPlusPluginConfiguration3_1 { + + @JsonProperty("table_size") + public abstract Number tableSize(); + + @JsonProperty("top_values") + public abstract Number topValues(); + + @JsonProperty("sort_order") + public abstract String sortOrder(); + + @JsonProperty("show_pie_chart") + public abstract Boolean showPieChart(); + + @JsonProperty("display_add_to_search_button") + public abstract Boolean addToSearch(); + + @JsonProperty("display_remove_from_search_button") + public abstract Boolean removeFromSearch(); + + @JsonProperty("display_term_hyperlinks") + public abstract Boolean termHyperlinks(); + + @JsonProperty("display_exclude_from_query_button") + public abstract Boolean excludeQuery(); + + @JsonProperty("display_get_term_reply_in_new_window_button") + public abstract Boolean termNewWindow(); + + @JsonProperty("version") + public abstract String version(); + + @JsonCreator + public static QuickValuesPlusPluginConfiguration3_1 create(@JsonProperty("table_size") Number tableSize, + @JsonProperty("top_values") Number topValues, + @JsonProperty("sort_order") String sortOrder, + @JsonProperty("show_pie_chart") Boolean showPieChart, + @JsonProperty("display_add_to_search_button") Boolean addToSearch, + @JsonProperty("display_remove_from_search_button") Boolean removeFromSearch, + @JsonProperty("display_term_hyperlinks") Boolean termHyperlinks, + @JsonProperty("display_exclude_from_query_button") Boolean excludeQuery, + @JsonProperty("display_get_term_reply_in_new_window_button") Boolean termNewWindow, + @JsonProperty("version") String version) { + return builder() + .tableSize(tableSize) + .topValues(topValues) + .sortOrder(sortOrder) + .showPieChart(showPieChart) + .addToSearch(addToSearch) + .removeFromSearch(removeFromSearch) + .termHyperlinks(termHyperlinks) + .excludeQuery(excludeQuery) + .termNewWindow(termNewWindow) + .version(version) + .build(); + } + + public static Builder builder() { + return new AutoValue_QuickValuesPlusPluginConfiguration3_1.Builder(); + } + + @AutoValue.Builder + public static abstract class Builder { + public abstract Builder tableSize(Number tableSize); + + public abstract Builder topValues(Number topValues); + + public abstract Builder sortOrder(String sortOrder); + + public abstract Builder showPieChart(Boolean showPieChart); + + public abstract Builder addToSearch(Boolean addToSearch); + + public abstract Builder removeFromSearch(Boolean removeFromSearch); + + public abstract Builder termHyperlinks(Boolean termHyperlinks); + + public abstract Builder excludeQuery(Boolean excludeQuery); + + public abstract Builder termNewWindow(Boolean termNewWindow); + + public abstract Builder version(String version); + + public abstract QuickValuesPlusPluginConfiguration3_1 build(); + } + +} \ No newline at end of file diff --git a/src/main/java/org/graylog/plugins/quickvaluesplus/QuickValuesPlusWidgetModule.java b/src/main/java/org/graylog/plugins/quickvaluesplus/QuickValuesPlusWidgetModule.java index 26ed1b5..43c3911 100644 --- a/src/main/java/org/graylog/plugins/quickvaluesplus/QuickValuesPlusWidgetModule.java +++ b/src/main/java/org/graylog/plugins/quickvaluesplus/QuickValuesPlusWidgetModule.java @@ -3,10 +3,8 @@ import org.graylog2.plugin.PluginModule; import org.graylog2.plugin.PluginConfigBean; import org.graylog.plugins.quickvaluesplus.widget.strategy.QuickValuesPlusWidgetStrategy; -import com.github.joschi.jadconfig.Parameter; import com.google.inject.multibindings.Multibinder; import org.graylog2.migrations.Migration; -import org.graylog.plugins.quickvaluesplus.QuickValuesPlusDefaultValuesMigration; import java.util.Collections; import java.util.Set; diff --git a/src/web/components/FieldQuickValuesPlus.jsx b/src/web/components/FieldQuickValuesPlus.jsx index 831cc49..cadb0a9 100644 --- a/src/web/components/FieldQuickValuesPlus.jsx +++ b/src/web/components/FieldQuickValuesPlus.jsx @@ -30,13 +30,33 @@ const FieldQuickValuesPlus = React.createClass({ ], getInitialState() { return { - debug: false, field: undefined, dropdownIsOpen: false, loaded: false, data: [], - defaults: {top_values: 5, sort_order: "descending", table_size: 25, show_pie_chart: true, show_data_table: true, display_add_to_search_button: true, display_remove_from_search_button: true, display_exclude_from_query_button: true, display_get_term_reply_in_new_window_button: true}, - quickValuesOptions: {top_values: 5, sort_order: "descending", table_size: 25, show_pie_chart: true, show_data_table: true, display_add_to_search_button: true, display_remove_from_search_button: true, display_exclude_from_query_button: true, display_get_term_reply_in_new_window_button: true} + defaults: { + top_values: 5, + sort_order: "descending", + table_size: 25, + show_pie_chart: true, + show_data_table: true, + display_add_to_search_button: true, + display_remove_from_search_button: true, + display_term_hyperlinks: true, + display_exclude_from_query_button: true, + display_get_term_reply_in_new_window_button: true + }, + quickValuesOptions: { + top_values: 5, + sort_order: "descending", + table_size: 25, + show_pie_chart: true, + show_data_table: true, + display_add_to_search_button: true, + display_remove_from_search_button: true, + display_term_hyperlinks: true, + display_exclude_from_query_button: true, + display_get_term_reply_in_new_window_button: true} }; }, style: style, @@ -44,14 +64,23 @@ const FieldQuickValuesPlus = React.createClass({ this.setState({dropdownIsOpen: !this.state.dropdownIsOpen}); }, componentWillMount() { - if (this.state.debug) console.log("In componentWillMount"); this.setState({ dropdownIsOpen: false }); - this.setState({quickValuesOptions: {top_values: 5, sort_order: "descending", table_size: 25, show_pie_chart: true, show_data_table: true, display_add_to_search_button: true, display_remove_from_search_button: true, display_exclude_from_query_button: true, display_get_term_reply_in_new_window_button: true}}); + this.setState({quickValuesOptions: { + top_values: 5, + sort_order: "descending", + table_size: 25, + show_pie_chart: true, + show_data_table: true, + display_add_to_search_button: true, + display_remove_from_search_button: true, + display_term_hyperlinks: true, + display_exclude_from_query_button: true, + display_get_term_reply_in_new_window_button: true} + }); }, componentDidMount() { - if (this.state.debug) console.log("In componentDidMount"); - ConfigurationActions.list("org.graylog.plugins.quickvaluesplus.QuickValuesPlusPluginConfiguration"); + ConfigurationActions.list("org.graylog.plugins.quickvaluesplus.QuickValuesPlusPluginConfiguration3_1"); this.style.use(); this._loadQuickValuesData(); }, @@ -90,7 +119,6 @@ const FieldQuickValuesPlus = React.createClass({ }, componentWillUnmount() { - if (this.state.debug) console.log("In componentWillUnmount"); this.style.unuse(); this._stopTimer(); }, @@ -112,61 +140,56 @@ const FieldQuickValuesPlus = React.createClass({ this.setState({field: field}, () => this._loadQuickValuesData(false)); }, _loadQuickValuesData() { - if (this.state.debug) console.log("Global Configuration value is"); - if (this.state.debug) console.log(this.state.configuration); - - if (this.state.debug) console.log("Is loaded: " + this.state.loaded); if (!this.state.loaded) { if (this.state.configuration !== undefined) { - if (this.state.debug) console.log("Global config loaded. QVP Options using global configuration settings."); this.setState({ quickValuesOptions: { - top_values: this.state.configuration['org.graylog.plugins.quickvaluesplus.QuickValuesPlusPluginConfiguration'].top_values, - sort_order: this.state.configuration['org.graylog.plugins.quickvaluesplus.QuickValuesPlusPluginConfiguration'].sort_order, - table_size: this.state.configuration['org.graylog.plugins.quickvaluesplus.QuickValuesPlusPluginConfiguration'].table_size, - show_pie_chart: true, + top_values: this.state.configuration['org.graylog.plugins.quickvaluesplus.QuickValuesPlusPluginConfiguration3_1'].top_values, + sort_order: this.state.configuration['org.graylog.plugins.quickvaluesplus.QuickValuesPlusPluginConfiguration3_1'].sort_order, + table_size: this.state.configuration['org.graylog.plugins.quickvaluesplus.QuickValuesPlusPluginConfiguration3_1'].table_size, + show_pie_chart: this.state.configuration['org.graylog.plugins.quickvaluesplus.QuickValuesPlusPluginConfiguration3_1'].show_pie_chart, show_data_table: true, - display_add_to_search_button: true, - display_remove_from_search_button: true, - display_exclude_from_query_button: true, - display_get_term_reply_in_new_window_button: true - + display_add_to_search_button: this.state.configuration['org.graylog.plugins.quickvaluesplus.QuickValuesPlusPluginConfiguration3_1'].display_add_to_search_button, + display_remove_from_search_button: this.state.configuration['org.graylog.plugins.quickvaluesplus.QuickValuesPlusPluginConfiguration3_1'].display_remove_from_search_button, + display_term_hyperlinks: this.state.configuration['org.graylog.plugins.quickvaluesplus.QuickValuesPlusPluginConfiguration3_1'].display_term_hyperlinks, + display_exclude_from_query_button: this.state.configuration['org.graylog.plugins.quickvaluesplus.QuickValuesPlusPluginConfiguration3_1'].display_exclude_from_query_button, + display_get_term_reply_in_new_window_button: this.state.configuration['org.graylog.plugins.quickvaluesplus.QuickValuesPlusPluginConfiguration3_1'].display_get_term_reply_in_new_window_button }, loaded: true, }); if (this.refs.thedash !== undefined) { this.refs.thedash.refs.widgetModal.setState({ config: { - top_values: this.state.configuration['org.graylog.plugins.quickvaluesplus.QuickValuesPlusPluginConfiguration'].top_values, - sort_order: this.state.configuration['org.graylog.plugins.quickvaluesplus.QuickValuesPlusPluginConfiguration'].sort_order, - table_size: this.state.configuration['org.graylog.plugins.quickvaluesplus.QuickValuesPlusPluginConfiguration'].table_size, - show_pie_chart: true, - show_data_table: true + top_values: this.state.configuration['org.graylog.plugins.quickvaluesplus.QuickValuesPlusPluginConfiguration3_1'].top_values, + sort_order: this.state.configuration['org.graylog.plugins.quickvaluesplus.QuickValuesPlusPluginConfiguration3_1'].sort_order, + table_size: this.state.configuration['org.graylog.plugins.quickvaluesplus.QuickValuesPlusPluginConfiguration3_1'].table_size, + show_pie_chart: this.state.configuration['org.graylog.plugins.quickvaluesplus.QuickValuesPlusPluginConfiguration3_1'].show_pie_chart, + show_data_table: true, + display_term_hyperlinks: this.state.configuration['org.graylog.plugins.quickvaluesplus.QuickValuesPlusPluginConfiguration3_1'].display_term_hyperlinks, + display_exclude_from_query_button: this.state.configuration['org.graylog.plugins.quickvaluesplus.QuickValuesPlusPluginConfiguration3_1'].display_exclude_from_query_button, + display_get_term_reply_in_new_window_button: this.state.configuration['org.graylog.plugins.quickvaluesplus.QuickValuesPlusPluginConfiguration3_1'].display_get_term_reply_in_new_window_button } }); } } else { - if (this.state.debug) console.log("Global config not loaded. QVP Options using internal default values."); this.setState({ quickValuesOptions: { top_values: this.state.defaults.top_values, sort_order: this.state.defaults.sort_order, table_size: this.state.defaults.table_size, - show_pie_chart: true, + show_pie_chart: this.state.defaults.show_pie_chart, show_data_table: true, - display_add_to_search_button: true, - display_remove_from_search_button: true, - display_exclude_from_query_button: true, - display_get_term_reply_in_new_window_button: true + display_add_to_search_button: this.state.defaults.display_add_to_search_button, + display_remove_from_search_button: this.state.defaults.display_remove_from_search_button, + display_term_hyperlinks: this.state.defaults.display_term_hyperlinks, + display_exclude_from_query_button: this.state.defaults.display_exclude_from_query_button, + display_get_term_reply_in_new_window_button: this.state.defaults.display_get_term_reply_in_new_window_button }, }); } } - if (this.state.debug) console.log("QVP Options - _loadQuickValuesData"); - if (this.state.debug) console.log(this.state.quickValuesOptions); - if (this.state.field !== undefined) { this.setState({loadPending: true}); const promise = QuickValuesPlusActions.getQuickValues( @@ -177,10 +200,7 @@ const FieldQuickValuesPlus = React.createClass({ } }, _resetStatus() { - if (this.state.debug) console.log("In resetStatus method. Get Initial State"); this.setState(this.getInitialState()); - if (this.state.debug) console.log("QVP Options - _resetStatus"); - if (this.state.debug) console.log(this.state.quickValuesOptions); }, sortordermenu: ['ascending', 'descending'], topvaluesmenu: [5,10,15,20,25], @@ -190,12 +210,9 @@ const FieldQuickValuesPlus = React.createClass({ return this.state.quickValuesOptions[configKey] === value ? 'selected' : ''; }, _updateOptionState(configKey, value) { - if (this.state.debug) console.log("In _updateOptionState method. Updating Options"); let newOptions = Object.assign({}, this.state.quickValuesOptions, {[configKey]: value}); this.refs.thedash.refs.widgetModal.setState({config: newOptions}); this.setState({quickValuesOptions: newOptions}); - if (this.state.debug) console.log("QVP Options - _updateOptionState"); - if (this.state.debug) console.log(this.state.quickValuesOptions); const promise = QuickValuesPlusActions.getQuickValues(this.state.field, newOptions['table_size'], newOptions['sort_order']); promise.then((data) => this.setState({data: data, loadPending: false})); }, @@ -242,9 +259,9 @@ const FieldQuickValuesPlus = React.createClass({ config={this.state.quickValuesOptions} data={this.state.data} horizontal - displayAddToSearchButton + displayAddToSearchButton={this.state.quickValuesOptions.display_add_to_search_button} displayRemoveFromSearchButton={this.state.quickValuesOptions.display_remove_from_search_button} - displayAnalysisInformation={this.state.quickValuesOptions.display_remove_from_search_button}/> + displayAnalysisInformation/> ); } diff --git a/src/web/components/QuickValuesPlusDefaultConfig.css b/src/web/components/QuickValuesPlusDefaultConfig.css new file mode 100644 index 0000000..6ca4732 --- /dev/null +++ b/src/web/components/QuickValuesPlusDefaultConfig.css @@ -0,0 +1,12 @@ +#qvp .deflist dt { + width: 60%; +} + +#qvp .qvp-conf-btn { + margin-top: 7px; +} + +.qvp-config-modal div.radio { + margin-top: -3px; + margin-bottom: 15px; +} \ No newline at end of file diff --git a/src/web/components/QuickValuesPlusDefaultConfig.jsx b/src/web/components/QuickValuesPlusDefaultConfig.jsx index c734128..d3d9f4d 100644 --- a/src/web/components/QuickValuesPlusDefaultConfig.jsx +++ b/src/web/components/QuickValuesPlusDefaultConfig.jsx @@ -4,6 +4,7 @@ import { Button } from 'react-bootstrap'; import BootstrapModalForm from 'components/bootstrap/BootstrapModalForm'; import { IfPermitted, Select } from 'components/common'; import ObjectUtils from 'util/ObjectUtils'; +import './QuickValuesPlusDefaultConfig.css'; const QuickValuesPlusDefaultConfig = React.createClass({ propTypes: { @@ -17,6 +18,13 @@ const QuickValuesPlusDefaultConfig = React.createClass({ sort_order: "descending", table_size: 50, top_values: 5, + show_pie_chart: true, + show_data_table: true, + display_add_to_search_button: true, + display_remove_from_search_button: true, + display_term_hyperlinks: true, + display_exclude_from_query_button: true, + display_get_term_reply_in_new_window_button: true }, }; }, @@ -33,6 +41,13 @@ const QuickValuesPlusDefaultConfig = React.createClass({ _updateConfigField(field, value) { const update = ObjectUtils.clone(this.state.config); + + if (value === "true") { + value = true; + } else if (value === "false") { + value = false + } + update[field] = value; this.setState({config: update}); }, @@ -76,11 +91,11 @@ const QuickValuesPlusDefaultConfig = React.createClass({ render() { return ( -
+

Quick Values Plus Configuration

- Defaults Configuration for the Quick Values Plus Field Analyzer and Widget + Default Settings for the Quick Values Plus Field Analyzer and Widget

@@ -92,10 +107,28 @@ const QuickValuesPlusDefaultConfig = React.createClass({
Default Sort Order
{this.state.config.sort_order === "descending" ? 'Descending' : 'Ascending'}
+ +
Show Pie Charts by Default?
+
{this.state.config.show_pie_chart === true ? 'Yes' : 'No'}
+ +
Show Add Term To Search Button?
+
{this.state.config.display_add_to_search_button === true ? 'Yes' : 'No'}
+ +
Show Remove Term From Search Button?
+
{this.state.config.display_remove_from_search_button === true ? 'Yes' : 'No'}
+ +
Show Term Hyperlinks? (Dashboards)
+
{this.state.config.display_term_hyperlinks === true ? 'Yes' : 'No'}
+ +
Show Exclude From Query Button? (Dashboards)
+
{this.state.config.display_exclude_from_query_button === true ? 'Yes' : 'No'}
+ +
Show Open Search Term Query in New Window Button? (Dashboards)
+
{this.state.config.display_get_term_reply_in_new_window_button === true ? 'Yes' : 'No'}
- + -
+
- +
-
-
-
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
diff --git a/src/web/components/QuickValuesPlusVisualization.jsx b/src/web/components/QuickValuesPlusVisualization.jsx index 68cef23..bd16af2 100644 --- a/src/web/components/QuickValuesPlusVisualization.jsx +++ b/src/web/components/QuickValuesPlusVisualization.jsx @@ -29,8 +29,16 @@ const QuickValuesPlusVisualization = React.createClass({ widget: PropTypes.object, height: PropTypes.any, horizontal: PropTypes.bool, + displayAddToSearchButton: PropTypes.bool, + displayRemoveFromSearchButton: PropTypes.bool, displayAnalysisInformation: PropTypes.bool, }, + getDefaultProps() { + return { + displayAddToSearchButton: false, + displayRemoveFromSearchButton: false, + } + }, getInitialState() { this.filters = []; this.triggerRender = true; @@ -43,7 +51,8 @@ const QuickValuesPlusVisualization = React.createClass({ this.topValues = 5; this.dimension = this.quickValuesData.dimension((d) => d.term); this.group = this.dimension.group().reduceSum((d) => d.count); - this.currentConfig = {}; + this.exclude_search_hidden = false; + this.new_window_hidden = false; return { total: undefined, @@ -53,12 +62,6 @@ const QuickValuesPlusVisualization = React.createClass({ }; }, componentDidMount() { - if (this.props.config.sort_order === "descending") { - this.sortOrder = d3.descending; - } else { - this.sortOrder = d3.ascending; - } - if (this.props.config.dashboardID) { this.setState({ currentConfig: { @@ -72,19 +75,37 @@ const QuickValuesPlusVisualization = React.createClass({ table_size: this.props.config.table_size, timerange: this.props.config.timerange, top_values: this.props.config.top_values, + display_term_hyperlinks: this.props.config.display_term_hyperlinks, + display_exclude_from_query_button: this.props.config.display_exclude_from_query_button, + display_get_term_reply_in_new_window_button: this.props.config.display_get_term_reply_in_new_window_button } }); + + this.setState({exclude_search_hidden: this.props.config.display_exclude_from_query_button}); + this.setState({new_window_hidden: this.props.config.display_get_term_reply_in_new_window_button}); } else { - this.setState({currentConfig: { + this.setState({ + currentConfig: { show_data_table: this.props.config.show_data_table, show_pie_chart: this.props.config.show_pie_chart, sort_order: this.props.config.sort_order, table_size: this.props.config.table_size, top_values: this.props.config.top_values, + display_add_to_search_button: this.props.config.display_add_to_search_button, + display_remove_from_search_button: this.props.config.display_remove_from_search_button, + display_term_hyperlinks: this.props.config.display_term_hyperlinks, + display_exclude_from_query_button: this.props.config.display_exclude_from_query_button, + display_get_term_reply_in_new_window_button: this.props.config.display_get_term_reply_in_new_window_button, } }); } + if (this.props.config.sort_order === "descending") { + this.sortOrder = d3.descending; + } else { + this.sortOrder = d3.ascending; + } + this.tableChanged = false; this.tableSize = this.props.config.table_size; this.topValues = this.props.config.top_values; @@ -110,7 +131,7 @@ const QuickValuesPlusVisualization = React.createClass({ this.tableChanged = true; this.tableSize = nextProps.config.table_size; this.topValues = nextProps.config.top_values; - + this.setState({exclude_search_hidden: nextProps.config.display_exclude_from_query_button}); this._resizeVisualization(nextProps.width, nextProps.height, nextProps.config.show_data_table); this._formatProps(nextProps); @@ -127,8 +148,13 @@ const QuickValuesPlusVisualization = React.createClass({ table_size: nextProps.config.table_size, timerange: nextProps.config.timerange, top_values: nextProps.config.top_values, + display_term_hyperlinks: nextProps.config.display_term_hyperlinks, + display_exclude_from_query_button: nextProps.config.display_exclude_from_query_button, + display_get_term_reply_in_new_window_button: nextProps.config.display_get_term_reply_in_new_window_button } }); + this.setState({exclude_search_hidden: nextProps.config.display_exclude_from_query_button}); + this.setState({new_window_hidden: nextProps.config.display_get_term_reply_in_new_window_button}); } else { this.setState({currentConfig: { show_data_table: nextProps.config.show_data_table, @@ -136,9 +162,15 @@ const QuickValuesPlusVisualization = React.createClass({ sort_order: nextProps.config.sort_order, table_size: nextProps.config.table_size, top_values: nextProps.config.top_values, + display_add_to_search_button: nextProps.config.display_add_to_search_button, + display_remove_from_search_button: nextProps.config.display_remove_from_search_button, + display_term_hyperlinks: nextProps.config.display_term_hyperlinks, + display_exclude_from_query_button: nextProps.config.display_exclude_from_query_button, + display_get_term_reply_in_new_window_button: nextProps.config.display_get_term_reply_in_new_window_button, } }); } + this._renderDataTable(); this._renderPieChart(); } @@ -285,14 +317,13 @@ const QuickValuesPlusVisualization = React.createClass({ colourBadge = ``; } - if (this.props.config.field) { + if (this.props.config.field && this.props.config.display_term_hyperlinks) { //Properly format strings containing spaces, backslashes and colons. let escTerm = this.escape(`${d.term}`); let appendQuery = (this.props.config.query == "") ? this.props.config.field + ":" + escTerm : this.props.config.query + " AND " + this.props.config.field + ":" + escTerm; let replayURL = (this.props.config.stream_id == undefined) ? Routes.search(appendQuery, this._getTimeRange(), this.props.config.interval) : Routes.stream_search(this.props.config.stream_id, appendQuery, this._getTimeRange(), this.props.config.interval); return `${colourBadge} ${d.term}`; - } else { @@ -306,21 +337,19 @@ const QuickValuesPlusVisualization = React.createClass({ (d) => NumberUtils.formatNumber(d.count), ]; - if (this.props.config.display_add_to_search_button == true ) { - columns.push((d) => this._getAddToSearchButton(d.term)); - } - - if (this.props.config.display_remove_from_search_button == true) { - columns.push((d) => this._getRemoveFromSearchButton(d.term)); - } + if (!this.props.config.dashboardID) { + if (this.props.config.display_add_to_search_button) { + columns.push((d) => this._getAddToSearchButton(d.term)); + } - if (this.props.config.dashboardID) { - if (this.isPermitted(this.state.currentUser.permissions, [`dashboards:edit:${this.props.config.dashboardID}`]) && this.props.config.display_exclude_from_query_button) { + if (this.props.config.display_remove_from_search_button) { + columns.push((d) => this._getRemoveFromSearchButton(d.term)); + } + } else { + if (this.isPermitted(this.state.currentUser.permissions, [`dashboards:edit:${this.props.config.dashboardID}`])) { columns.push((d) => this._getExcludeFromQueryButton(d.term)); } - } - if (this.props.config.field && this.props.config.display_get_term_reply_in_new_window_button) { columns.push((d) => { //Properly format strings containing spaces, backslashes and colons. let escTerm = this.escape(`${d.term}`); @@ -335,6 +364,7 @@ const QuickValuesPlusVisualization = React.createClass({ const tableDomNode = this.refs.table; this.dataTable = dc.dataTable(tableDomNode, this.dcGroupName); + if (this.sortOrder == d3.descending){ const descGroup = this._getTop(this.group); this.dataTable @@ -355,11 +385,24 @@ const QuickValuesPlusVisualization = React.createClass({ const term = $(d3.event.target).closest('button').data('term'); SearchStore.addSearchTerm(this.props.id, term); }); + table.selectAll('td.dc-table-column button#removeSearchTerm').on('click', () => { // noinspection Eslint const term = $(d3.event.target).closest('button').data('term'); SearchStore.addSearchTerm("!" + this.props.id, term); }); + + if (this.props.config.dashboardID) { + let query_hidden = this.state.exclude_search_hidden ? null : 'none'; + table.selectAll('td.dc-table-column._3').style("display", function(x){ + return query_hidden; + }); + + let new_window_hidden = this.state.new_window_hidden ? null : 'none'; + table.selectAll('td.dc-table-column._4').style("display", function(x){ + return new_window_hidden; + }); + } table.selectAll('td.dc-table-column button#excludeTermFromQuery').on('click', () => { if (this.isPermitted(this.state.currentUser.permissions, [`dashboards:edit:${this.props.config.dashboardID}`])) { const term = $(d3.event.target).closest('button').data('term'); @@ -388,14 +431,27 @@ const QuickValuesPlusVisualization = React.createClass({ const term = $(d3.event.target).closest('button').data('term'); SearchStore.addSearchTerm(this.props.id, term); }); + table.selectAll('td.dc-table-column button#removeSearchTerm').on('click', () => { // noinspection Eslint const term = $(d3.event.target).closest('button').data('term'); SearchStore.addSearchTerm("!" + this.props.id, term); }); + + if (this.props.config.dashboardID) { + let query_hidden = this.state.exclude_search_hidden ? null : 'none'; + table.selectAll('td.dc-table-column._3').style("display", function(x){ + return query_hidden; + }); + + let new_window_hidden = this.state.new_window_hidden ? null : 'none'; + table.selectAll('td.dc-table-column._4').style("display", function(x){ + return new_window_hidden; + }); + } + table.selectAll('td.dc-table-column button#excludeTermFromQuery').on('click', () => { // noinspection Eslint - const term = $(d3.event.target).closest('button').data('term'); this._excludeQueryClick(term); }); @@ -554,7 +610,11 @@ const QuickValuesPlusVisualization = React.createClass({ * Ensure we always render the data table when quickvalues config was created before introducing pie charts, * or when neither the data table or the pie chart are selected for rendering. */ - if (this.props.config.show_data_table || !this.props.config.show_pie_chart) { + if (!this.props.config.show_pie_chart) { + dataTableClassName = 'col-md-12'; + } else if (this.props.config.show_pie_chart && !this.props.config.show_data_table) { + dataTableClassName = 'hidden'; + } else if(this.props.config.show_pie_chart && this.props.config.show_data_table) { dataTableClassName = this.props.horizontal ? 'col-md-8' : 'col-md-12'; } else { dataTableClassName = 'hidden'; @@ -582,7 +642,7 @@ const QuickValuesPlusVisualization = React.createClass({ if (this.props.config.dashboardID) { if (this.isPermitted(this.state.currentUser.permissions, [`dashboards:edit:${this.props.config.dashboardID}`])) { excludeQueryButton = ( -   +   ); } else { excludeQueryButton = ''; @@ -602,19 +662,19 @@ const QuickValuesPlusVisualization = React.createClass({ - - - - {this.props.config.display_add_to_search_button && + + + + {!this.props.config.dashboardID && this.props.config.display_add_to_search_button && } - {this.props.config.display_remove_from_search_button && + {!this.props.config.dashboardID && this.props.config.display_remove_from_search_button && } - {this.props.config.display_exclude_from_query_button && + {this.props.config.dashboardID && this.state.exclude_search_hidden && excludeQueryButton} - {(this.props.config.field && this.props.config.display_get_term_reply_in_new_window_button) && - + {this.props.config.dashboardID && this.props.config.field && this.state.new_window_hidden && + } diff --git a/src/web/components/QuickValuesPlusWidgetCreateConfiguration.jsx b/src/web/components/QuickValuesPlusWidgetCreateConfiguration.jsx index 6f1373c..794ea1b 100644 --- a/src/web/components/QuickValuesPlusWidgetCreateConfiguration.jsx +++ b/src/web/components/QuickValuesPlusWidgetCreateConfiguration.jsx @@ -9,15 +9,16 @@ const QuickValuesPlusWidgetCreateConfiguration = React.createClass({ getInitialConfiguration() { return { - top_values: 5, - table_size: 50, - sort_order: 'descending', - show_pie_chart: true, - show_data_table: true, - display_add_to_search_button: true, - display_remove_from_search_button: true, - display_exclude_from_query_button: true, - display_get_term_reply_in_new_window_button: true, + top_values: 5, + table_size: 50, + sort_order: 'descending', + show_pie_chart: true, + show_data_table: true, + display_add_to_search_button: true, + display_remove_from_search_button: true, + display_term_hyperlinks: true, + display_exclude_from_query_button: true, + display_get_term_reply_in_new_window_button: true, }; }, @@ -78,23 +79,14 @@ const QuickValuesPlusWidgetCreateConfiguration = React.createClass({ onChange={this.props.onChange} help="Include a table with quantitative information." /> - - - + - - - +
Value%#Value%#