Compare commits

..

94 Commits

Author SHA1 Message Date
Torkel Ödegaard 917fabbc9b docs(): updated links again 2016-04-12 09:52:12 -04:00
Torkel Ödegaard 782c484cad docs(): updated release links 2016-04-12 09:20:27 -04:00
Torkel Ödegaard 859ef48e51 fix(): changed graph panel tooltip higlight style 2016-04-11 21:32:18 -04:00
Torkel Ödegaard 51aed2dbc6 Merge branch 'master' of github.com:grafana/grafana 2016-04-11 21:11:08 -04:00
Torkel Ödegaard 497e9343aa Merge branch 'pluginlist' 2016-04-11 21:10:54 -04:00
Torkel Ödegaard 6ce934f1dd feat(pluginlist): worked on plugin list 2016-04-11 20:33:58 -04:00
Matt Toback 3a64a11745 Merge branch 'pluginlist' of https://github.com/grafana/grafana into matt-plugin-list 2016-04-11 19:02:55 -04:00
Matt Toback f4fc3f48a7 New modal for upgrades and updated styles on list 2016-04-11 19:01:18 -04:00
Torkel Ödegaard 9e15320fd0 Merge branch 'master' of github.com:grafana/grafana 2016-04-11 18:54:08 -04:00
Torkel Ödegaard 94729745dc change(apps): config tab is now default for app plugins, fixes #4573 2016-04-11 18:54:00 -04:00
Torkel Ödegaard 7031645f4b fix(): fixed sass lint warning 2016-04-11 18:27:53 -04:00
Torkel Ödegaard f1ff044967 Merge branch 'master' of github.com:grafana/grafana 2016-04-11 18:16:57 -04:00
Torkel Ödegaard 9c14b9672f fix(): admin stats and postgres 2016-04-11 18:16:35 -04:00
Torkel Ödegaard f93b039e42 change(grafana-cli): changed upgrade to update in command line, upgrade and upgrade-all will still work as aliases 2016-04-11 18:05:28 -04:00
Torkel Ödegaard 3849b59627 feat(updates): changed to new api 2016-04-11 17:16:52 -04:00
bergquist a5eda6a87b feat(cli): detects plugin folder for dev env
closes #4572
2016-04-11 22:32:46 +02:00
Torkel Ödegaard 31441f0b43 feat(dashlist): updated dashlist 2016-04-11 16:21:25 -04:00
Torkel Ödegaard 451a8bef39 feat(pluginslist): minor update 2016-04-11 14:17:38 -04:00
Torkel Ödegaard c518fdc155 Merge branch 'updatecheck' into pluginlist 2016-04-11 13:43:29 -04:00
Torkel Ödegaard ab34b174f9 Added update notice to plugin list 2016-04-11 13:42:52 -04:00
Torkel Ödegaard 6ac3bd4c7c feat(): update checks starting to work 2016-04-11 12:47:04 -04:00
Torkel Ödegaard b4a8c227cc feat(update checks): started work on update checks 2016-04-11 12:21:48 -04:00
Torkel Ödegaard 01c6b56685 Update latest.json 2016-04-11 11:54:07 -04:00
Torkel Ödegaard 5a81cf6c17 Merge branch 'master' of github.com:grafana/grafana 2016-04-11 09:27:30 -04:00
bergquist eeb7524c0f feat(cli): improve helptext for cli 2016-04-11 14:50:34 +02:00
bergquist 2fd25f0093 feat(cli): add uninstall alias for remove 2016-04-11 14:49:12 +02:00
Torkel Ödegaard 8047f902a5 stats(): updated stats 2016-04-10 18:36:54 -04:00
Torkel Ödegaard 788a0154d3 Merge branch 'master' of github.com:grafana/grafana 2016-04-10 14:59:03 -04:00
Torkel Ödegaard a8f4b0227f fix(graph): Fixed issue with legend height in table mode with few series, affected iframe embedding as well, fixes #4640 2016-04-10 14:58:52 -04:00
Torkel Ödegaard cdf8c0fac9 Merge pull request #4635 from idanz/patch-2
Table module calls renderingCompleted
2016-04-10 14:09:34 -04:00
Torkel Ödegaard 8b5f71fb55 Merge pull request #4637 from utkarshcmu/version
Consistency in options for future support
2016-04-10 14:08:46 -04:00
utkarshcmu c100df412d Consistency in options for future support 2016-04-10 01:30:58 -07:00
Idan Zalzberg cf00775bb8 Table module calls renderingCompleted 2016-04-10 11:00:25 +07:00
Torkel Ödegaard 97be3c05e6 feat(pluginlist panel): updates to pluginlist panel, hooked up plugin type categories 2016-04-09 15:32:41 -04:00
Torkel Ödegaard 4fd5552d3d Merge branch 'pluginlist' of github.com:grafana/grafana into pluginlist 2016-04-09 15:02:01 -04:00
Torkel Ödegaard 12800f831a Merge branch 'master' into pluginlist 2016-04-09 15:01:39 -04:00
Torkel Ödegaard e8a209c9b5 fix(): removed obsolete call to setTimeQueryStart 2016-04-09 15:00:47 -04:00
Torkel Ödegaard 781fed1079 fix(mixed data source): fixed issue with mixed data sources in same graph, fixes #4604 2016-04-09 14:44:50 -04:00
Torkel Ödegaard ebb373eedb fix(graph): fixed issue with toggling series on and off would loose stacking, fixes #4557 2016-04-09 14:37:30 -04:00
Torkel Ödegaard 9f75bda8be refactor(): minor cleanup in sidemenu component 2016-04-09 13:28:34 -04:00
Torkel Ödegaard 370589d6c0 fix(org switch): fixed issue with switching orgs, fixes #4614 2016-04-09 13:27:06 -04:00
Torkel Ödegaard 262c341bdc fix(influxdb): fixed annotation queries containting template variables, fixes #4602 2016-04-09 13:09:03 -04:00
Torkel Ödegaard 22b11d7d4d feat(graph panel): changed to PR #4493 and other polish, removed the relative time from graph tooltip, did not think it worked that well 2016-04-09 12:45:35 -04:00
Torkel Ödegaard 034244217f Merge pull request #4493 from mtanda/highlight_tooltip
highlight series name in tooltip when shared tooltip enabled
2016-04-09 11:29:05 -04:00
Torkel Ödegaard 5d9c3d595f fix(graph): fixed issue with y-axis labels overlapping with the yaxis, fixes #4626 2016-04-09 11:26:48 -04:00
Torkel Ödegaard ed62822d44 feat(influxdb): changed multi query encoding in order to support InfluxDB >v0.11, closes #4533 2016-04-09 11:00:35 -04:00
Matt Toback cf750c53fd Few more style adjustments 2016-04-08 19:07:20 -04:00
Matt Toback d2421d964a New styles almost ready 2016-04-08 18:46:45 -04:00
Torkel Ödegaard 52e2091f2c fix(): fixed issue with series override color selector when using the mouse to select color option, fixes #4620 2016-04-08 18:12:08 -04:00
Torkel Ödegaard 1a29a769c6 fix(series overrides): graph no rerenders with new styles after updating series override, fixes #4621 2016-04-08 17:43:13 -04:00
Torkel Ödegaard d66d25ff14 feat(pluginlist): added plugin logo 2016-04-08 17:01:17 -04:00
Torkel Ödegaard d70ef90bdd feat(): plugin list panel 2016-04-08 16:42:33 -04:00
Torkel Ödegaard f772a5cf38 Merge branch 'event-changes' 2016-04-08 09:28:34 -04:00
bergquist 583c0f09a8 feat(cli): adds better support for plugin folder in dev 2016-04-08 11:59:45 +02:00
Torkel Ödegaard 4d63b576f7 tech(): put in a real event emitter instead of rxjs based on that was broken 2016-04-07 19:47:48 -04:00
Torkel Ödegaard 44cf66d7ff refactoring(): fixing event things 2016-04-07 19:23:20 -04:00
Matt Toback 469c43ca25 Merge pull request #4607 from grafana/new-styles
Updated font and a new style
2016-04-07 17:13:20 -04:00
Matt Toback bb7dc0fd93 Updated font and a new style 2016-04-07 15:11:54 -04:00
Torkel Ödegaard b8baeac860 Merge branch 'master' of github.com:grafana/grafana 2016-04-07 11:00:56 -04:00
Torkel Ödegaard a425ce0826 fix(graph panel): Fixed legend option max not updating, fixes #4601 2016-04-07 10:57:55 -04:00
bergquist 513aa61d17 fix(singlestat): start timer for refreshs 2016-04-07 12:01:43 +02:00
Torkel Ödegaard c3d88aa53d fix(graph panel): fixed issue where newly added graph panels shared axes config binding, fixes #4582 2016-04-06 15:27:29 -04:00
Torkel Ödegaard cb5b6af9db docs(): updated readme, again 2016-04-06 13:19:06 -04:00
Torkel Ödegaard b188a02956 docs(): updated 2016-04-06 13:11:43 -04:00
Torkel Ödegaard 604e21e32c docs(): updated readme 2016-04-06 13:10:08 -04:00
Torkel Ödegaard 7dbebef3ff docs(): updated readme again 2016-04-06 12:55:59 -04:00
Torkel Ödegaard f8a4ebd4a8 docs(): updated readme again 2016-04-06 12:53:30 -04:00
Torkel Ödegaard 0117d9eedb Merge branch 'master' of github.com:grafana/grafana 2016-04-06 12:52:19 -04:00
Torkel Ödegaard cdb23cafa6 docs(): updated readme 2016-04-06 12:52:05 -04:00
Torkel Ödegaard 727deaf1a6 Merge pull request #4589 from grafana/mention-just-example
clarify that this is only an example. fix 2 of #4576
2016-04-06 08:04:58 -07:00
Dieter Plaetinck b7410ee718 clarify that this is only an example. fix 2 of #4576 2016-04-06 11:03:15 -04:00
Carl Bergquist 583bfdf9bb Merge pull request #4588 from utkarshcmu/unused
Fixed typos, lang
2016-04-06 16:30:55 +02:00
utkarshcmu 41dcefb174 Fixed typos, lang 2016-04-06 07:26:22 -07:00
Torkel Ödegaard 91bdb2f344 Merge pull request #4587 from utkarshcmu/unused
Removed unused option
2016-04-06 07:22:31 -07:00
utkarshcmu 58b76bac0d Removed unused option 2016-04-06 07:12:38 -07:00
Torkel Ödegaard f8efee6c2a change(): updated placeholder text in data source edit view 2016-04-06 09:21:34 -04:00
Torkel Ödegaard 1666415620 fix(): fixed minor issue in data source info popover, fixes #4576 2016-04-06 09:13:49 -04:00
Torkel Ödegaard 7f8f5f3498 fix(): minor fix for plugin page, fixes #4578 2016-04-06 09:08:49 -04:00
Torkel Ödegaard a27d72b437 fix(): fixed issue with scripted dashboard example, fixes #4579 2016-04-06 08:49:51 -04:00
Torkel Ödegaard 22126e99c1 Merge branch 'master' of github.com:grafana/grafana 2016-04-06 08:44:24 -04:00
Torkel Ödegaard dc7f8014a2 fix(app config): fixed issue with app config tab, fixes #4575 2016-04-06 08:43:06 -04:00
Torkel Ödegaard e112175abe Merge pull request #4570 from Dieterbe/document-godep
document godep build dependency
2016-04-06 05:25:18 -07:00
Torkel Ödegaard f7fd1f2a89 Merge pull request #4585 from vitorboschi/fix4584
Fix percentage checkbox naming. Fix #4584
2016-04-06 05:24:05 -07:00
Vitor Boschi ed1fe93648 Fix percentage checkbox naming. Fix #4584 2016-04-06 08:53:20 -03:00
Dieter Plaetinck 616b8f82ee document godep build dependency 2016-04-05 13:24:19 -04:00
Torkel Ödegaard 43896dc416 fix(): updated changelog and version to beta3 2016-04-05 11:29:12 -04:00
Torkel Ödegaard 9b30634217 fix(postgres): fixed issue with postgres caused by wrong prefrences table types, dropped table and created it with correct column types, fixes #4558 2016-04-05 11:24:21 -04:00
bergquist 676e950fa3 fix(cli): improve error message for not using sudo
closes #4562
2016-04-05 15:37:09 +02:00
Torkel Ödegaard 90b86313db Merge branch 'master' of github.com:grafana/grafana 2016-04-05 09:05:43 -04:00
Torkel Ödegaard 3c1e9e9a25 fix(table): fixed issue with table panel and annotations, fixes #4563 2016-04-05 09:05:34 -04:00
Daniel Lee b39aa02b52 fix(plugins): Fix broken link in plugins readme
Another link to setup instructions that it is broken.
2016-04-05 12:05:14 +02:00
Daniel Lee 860e771493 fix(plugins): Fix broken link in plugins readme
Can't find the DEVELOPMENT.md file so linked to 
http://docs.grafana.org/project/building_from_source/ instead.
2016-04-05 12:01:13 +02:00
Torkel Ödegaard e027770213 docs(): updated links to beta2 2016-04-04 17:49:33 -04:00
Mitsuhiro Tanda 0a13b7c559 highlight series name in tooltip when shared tooltip active 2016-03-30 22:46:46 +09:00
104 changed files with 1194 additions and 477 deletions
+18 -3
View File
@@ -1,4 +1,20 @@
# 3.0.0-beta2 (unreleased)
# 3.0.0-beta3 (unreleased)
### Enhancements
* **InfluxDB**: Changed multi query encoding to work with InfluxDB 0.11 & 0.12, closes [#4533](https://github.com/grafana/grafana/issues/4533)
### Bug fixes
* **Postgres**: Fixed page render crash when using postgres, fixes [#4558](https://github.com/grafana/grafana/issues/4558)
* **Table panel**: Fixed table panel bug when trying to show annotations in table panel, fixes [#4563](https://github.com/grafana/grafana/issues/4563)
* **App Config**: Fixed app config issue showing content of other app config, fixes [#4575](https://github.com/grafana/grafana/issues/4575)
* **Graph Panel**: Fixed legend option max not updating, fixes [#4601](https://github.com/grafana/grafana/issues/4601)
* **Graph Panel**: Fixed issue where newly added graph panels shared same axes config, fixes [#4582](https://github.com/grafana/grafana/issues/4582)
* **Graph Panel**: Fixed issue with axis labels overlapping Y-axis, fixes [#4626](https://github.com/grafana/grafana/issues/4626)
* **InfluxDB**: Fixed issue with templating query containing template variable, fixes [#4602](https://github.com/grafana/grafana/issues/4602)
* **Graph Panel**: Fixed issue with hiding series and stacking, fixes [#4557](https://github.com/grafana/grafana/issues/4557)
* **Graph Panel**: Fixed issue with legend height in table mode with few series, affected iframe embedding as well, fixes [#4640](https://github.com/grafana/grafana/issues/4640)
# 3.0.0-beta2 (2016-04-04)
### New Features (introduces since 3.0-beta1)
* **Preferences**: Set home dashboard on user and org level, closes [#1678](https://github.com/grafana/grafana/issues/1678)
@@ -9,9 +25,8 @@
* **Dashboard**: Fixed dashboard panel layout for mobile devices, fixes [#4529](https://github.com/grafana/grafana/issues/4529)
* **Table Panel**: Fixed issue with table panel sort, fixes [#4532](https://github.com/grafana/grafana/issues/4532)
* **Page Load Crash**: A Datasource with null jsonData would make Grafana fail to load page, fixes [#4536](https://github.com/grafana/grafana/issues/4536)
* **Metrics tab**: Fix for missing datasource name in datasource selector, fixes [#4540](https://github.com/grafana/grafana/issues/4540)
* **Metrics tab**: Fix for missing datasource name in datasource selector, fixes [#4541](https://github.com/grafana/grafana/issues/4540)
* **Graph**: Fix legend in table mode with series on right-y axis, fixes [#4551](https://github.com/grafana/grafana/issues/4551), [#1145](https://github.com/grafana/grafana/issues/1145)
* **Password**: Password reset link/page did not work, fixes [#4542](https://github.com/grafana/grafana/issues/4542)
# 3.0.0-beta1 (2016-03-31)
+4 -1
View File
@@ -1,8 +1,10 @@
[Grafana](http://grafana.org) [![Circle CI](https://circleci.com/gh/grafana/grafana.svg?style=svg)](https://circleci.com/gh/grafana/grafana) [![Coverage Status](https://coveralls.io/repos/grafana/grafana/badge.png)](https://coveralls.io/r/grafana/grafana) [![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/grafana/grafana?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[Grafana](http://grafana.org) [![Circle CI](https://circleci.com/gh/grafana/grafana.svg?style=svg)](https://circleci.com/gh/grafana/grafana) [![Coverage Status](https://coveralls.io/repos/grafana/grafana/badge.png)](https://coveralls.io/r/grafana/grafana)
================
[Website](http://grafana.org) |
[Twitter](https://twitter.com/grafana) |
[IRC](https://webchat.freenode.net/?channels=grafana) |
![](https://brandfolder.com/api/favicon/icon?size=16&domain=www.slack.com)
[Slack](http://slack.raintank.io) |
[Email](mailto:contact@grafana.org)
Grafana is an open source, feature rich metrics dashboard and graph editor for
@@ -77,6 +79,7 @@ the latest master builds [here](http://grafana.org/download/builds)
- Go 1.5
- NodeJS
- [Godep](https://github.com/tools/godep)
### Get Code
+7
View File
@@ -111,6 +111,13 @@ gc_interval_time = 86400
# Change this option to false to disable reporting.
reporting_enabled = true
# Set to false to disable all checks to https://grafana.net
# for new vesions (grafana itself and plugins), check is used
# in some UI views to notify that grafana or plugin update exists
# This option does not cause any auto updates, nor send any information
# only a GET request to http://grafana.net to get latest versions
check_for_updates = true
# Google Analytics universal tracking code, only enabled if you specify an id here
google_analytics_ua_id =
+7
View File
@@ -100,6 +100,13 @@
# Change this option to false to disable reporting.
;reporting_enabled = true
# Set to false to disable all checks to https://grafana.net
# for new vesions (grafana itself and plugins), check is used
# in some UI views to notify that grafana or plugin update exists
# This option does not cause any auto updates, nor send any information
# only a GET request to http://grafana.net to get latest versions
check_for_updates = true
# Google Analytics universal tracking code, only enabled if you specify an id here
;google_analytics_ua_id =
+3 -3
View File
@@ -11,7 +11,7 @@ page_keywords: grafana, installation, debian, ubuntu, guide
Description | Download
------------ | -------------
Stable .deb for Debian-based Linux | [grafana_2.6.0_amd64.deb](https://grafanarel.s3.amazonaws.com/builds/grafana_2.6.0_amd64.deb)
Beta .deb for Debian-based Linux | [grafana_3.0.0-beta11459429091_amd64.deb](https://grafanarel.s3.amazonaws.com/builds/grafana_3.0.0-beta11459429091_amd64.deb)
Beta .deb for Debian-based Linux | [grafana_3.0.0-beta31460467884_amd64.deb](https://grafanarel.s3.amazonaws.com/builds/grafana_3.0.0-beta31460467884_amd64.deb)
## Install Stable
@@ -21,9 +21,9 @@ Beta .deb for Debian-based Linux | [grafana_3.0.0-beta11459429091_amd64.deb](h
## Install 3.0 Beta
$ wget https://grafanarel.s3.amazonaws.com/builds/grafana_3.0.0-beta11459429091_amd64.deb
$ wget https://grafanarel.s3.amazonaws.com/builds/grafana_3.0.0-beta31460467884_amd64.deb
$ sudo apt-get install -y adduser libfontconfig
$ sudo dpkg -i grafana_3.0.0-beta11459429091_amd64.deb
$ sudo dpkg -i grafana_3.0.0-beta31460467884_amd64.deb
## APT Repository
+4 -4
View File
@@ -11,7 +11,7 @@ page_keywords: grafana, installation, centos, fedora, opensuse, redhat, guide
Description | Download
------------ | -------------
Stable .RPM for CentOS / Fedora / OpenSuse / Redhat Linux | [grafana-2.6.0-1.x86_64.rpm](https://grafanarel.s3.amazonaws.com/builds/grafana-2.6.0-1.x86_64.rpm)
Beta .RPM for CentOS / Fedor / OpenSuse / Redhat Linux | [grafana-3.0.0-beta11459429091.x86_64.rpm](https://grafanarel.s3.amazonaws.com/builds/grafana-3.0.0-beta11459429091.x86_64.rpm)
Beta .RPM for CentOS / Fedor / OpenSuse / Redhat Linux | [grafana-3.0.0-beta31460467884.x86_64.rpm](https://grafanarel.s3.amazonaws.com/builds/grafana-3.0.0-beta31460467884§.x86_64.rpm)
## Install Stable Release from package file
@@ -34,18 +34,18 @@ Or install manually using `rpm`.
You can install Grafana using Yum directly.
$ sudo yum install https://grafanarel.s3.amazonaws.com/builds/grafana-3.0.0-beta11459429091.x86_64.rpm
$ sudo yum install https://grafanarel.s3.amazonaws.com/builds/grafana-3.0.0-beta31460467884.x86_64.rpm
Or install manually using `rpm`.
#### On CentOS / Fedora / Redhat:
$ sudo yum install initscripts fontconfig
$ sudo rpm -Uvh grafana-3.0.0-beta11459429091.x86_64.rpm
$ sudo rpm -Uvh grafana-3.0.0-beta31460467884.x86_64.rpm
#### On OpenSuse:
$ sudo rpm -i --nodeps grafana-3.0.0-beta11459429091.x86_64.rpm
$ sudo rpm -i --nodeps grafana-3.0.0-beta31460467884.x86_64.rpm
## Install via YUM Repository
+1 -1
View File
@@ -10,7 +10,7 @@ page_keywords: grafana, installation, windows guide
Description | Download
------------ | -------------
Stable Zip package for Windows | [grafana.2.5.0.windows-x64.zip](https://grafanarel.s3.amazonaws.com/winbuilds/dist/grafana-2.5.0.windows-x64.zip)
Stable Zip package for Windows | [grafana.2.6.0.windows-x64.zip](https://grafanarel.s3.amazonaws.com/winbuilds/dist/grafana-2.5.0.windows-x64.zip)
## Configure
+2 -2
View File
@@ -10,7 +10,7 @@ From grafana 3.0 it's very easy to develop your own plugins and share them with
## Short version
1. [Setup grafana](https://github.com/grafana/grafana/blob/master/DEVELOPMENT.md)
1. [Setup grafana](http://docs.grafana.org/project/building_from_source/)
2. Clone an example plugin into ```/var/lib/grafana/plugins``` or `data/plugins` (relative to grafana git repo if your running development version from source dir)
3. Code away!
@@ -34,7 +34,7 @@ and [apps](./apps.md) plugins in the documentation.
## Start developing your plugin
There are two ways that you can start developing a Grafana plugin.
1. Setup a Grafana development environment. [(described here)](https://github.com/grafana/grafana/blob/master/DEVELOPMENT.md) and place your plugin in the ```data/plugins``` folder.
1. Setup a Grafana development environment. [(described here)](http://docs.grafana.org/project/building_from_source/) and place your plugin in the ```data/plugins``` folder.
2. Install Grafana and place your plugin in the plugins directory which is set in your [config file](../installation/configuration.md). By default this is `/var/lib/grafana/plugins` on Linux systems.
3. Place your plugin directory anywhere you like and specify it grafana.ini.
+1 -1
View File
@@ -9,7 +9,7 @@ page_keywords: grafana, plugins, documentation
From Grafana 3.0 not only datasource plugins are supported but also panel plugins and apps.
Having panels as plugins make it easy to create and add any kind of panel, to show your data
or improve your favorite dashboards. Apps is something new in Grafana that enables
bundling of datasources, panels, dashboards and Grafana pages into a cohesive experiance.
bundling of datasources, panels, dashboards and Grafana pages into a cohesive experience.
Grafana already have a strong community of contributors and plugin developers.
By making it easier to develop and install plugins we hope that the community
+4 -4
View File
@@ -28,14 +28,14 @@ List installed plugins
grafana-cli plugins ls
```
Upgrade all installed plugins
Update all installed plugins
```
grafana-cli plugins upgrade-all
grafana-cli plugins update-all
```
Upgrade one plugin
Update one plugin
```
grafana-cli plugins upgrade <plugin-id>
grafana-cli plugins update <plugin-id>
```
Remove one plugin
+1 -1
View File
@@ -7,7 +7,7 @@ page_keywords: grafana, plugins, documentation
# Panels
Panels are the main bulding block of dashboards.
Panels are the main building blocks of dashboards.
## Panel development
+2 -1
View File
@@ -1,3 +1,4 @@
{
"version": "2.1.1"
"stable": "2.6.0",
"testing": "3.0.0-beta2"
}
+3 -3
View File
@@ -4,13 +4,12 @@
"company": "Coding Instinct AB"
},
"name": "grafana",
"version": "3.0.0-beta2",
"version": "3.0.0-beta3",
"repository": {
"type": "git",
"url": "http://github.com/grafana/grafana.git"
},
"devDependencies": {
"angular2": "2.0.0-beta.12",
"zone.js": "^0.6.6",
"autoprefixer": "^6.3.3",
"es6-promise": "^3.0.2",
@@ -54,7 +53,7 @@
"mocha": "2.3.4",
"phantomjs-prebuilt": "^2.1.3",
"reflect-metadata": "0.1.2",
"rxjs": "5.0.0-beta.2",
"rxjs": "5.0.0-beta.4",
"sass-lint": "^1.5.0",
"systemjs": "0.19.24"
},
@@ -68,6 +67,7 @@
},
"license": "Apache-2.0",
"dependencies": {
"eventemitter3": "^1.2.0",
"grunt-jscs": "~1.5.x",
"grunt-sass-lint": "^0.1.0",
"grunt-sync": "^0.4.1",
+10 -10
View File
@@ -1,22 +1,22 @@
#! /usr/bin/env bash
deb_ver=3.0.0-beta11459429091
rpm_ver=3.0.0-beta11459429091
deb_ver=3.0.0-beta31460381868
rpm_ver=3.0.0-beta31460381868
#rpm_ver=3.0.0-1
wget https://grafanarel.s3.amazonaws.com/builds/grafana_${deb_ver}_amd64.deb
#wget https://grafanarel.s3.amazonaws.com/builds/grafana_${deb_ver}_amd64.deb
# package_cloud push grafana/stable/debian/jessie grafana_${deb_ver}_amd64.deb
# package_cloud push grafana/stable/debian/wheezy grafana_${deb_ver}_amd64.deb
#package_cloud push grafana/stable/debian/jessie grafana_${deb_ver}_amd64.deb
#package_cloud push grafana/stable/debian/wheezy grafana_${deb_ver}_amd64.deb
package_cloud push grafana/testing/debian/jessie grafana_${deb_ver}_amd64.deb
package_cloud push grafana/testing/debian/wheezy grafana_${deb_ver}_amd64.deb
#package_cloud push grafana/testing/debian/jessie grafana_${deb_ver}_amd64.deb
#package_cloud push grafana/testing/debian/wheezy grafana_${deb_ver}_amd64.deb
wget https://grafanarel.s3.amazonaws.com/builds/grafana-${rpm_ver}.x86_64.rpm
#wget https://grafanarel.s3.amazonaws.com/builds/grafana-${rpm_ver}.x86_64.rpm
package_cloud push grafana/testing/el/6 grafana-${rpm_ver}.x86_64.rpm
package_cloud push grafana/testing/el/7 grafana-${rpm_ver}.x86_64.rpm
#package_cloud push grafana/testing/el/6 grafana-${rpm_ver}.x86_64.rpm
#package_cloud push grafana/testing/el/7 grafana-${rpm_ver}.x86_64.rpm
# package_cloud push grafana/stable/el/7 grafana-${version}-1.x86_64.rpm
# package_cloud push grafana/stable/el/6 grafana-${version}-1.x86_64.rpm
+4
View File
@@ -30,6 +30,7 @@ func Register(r *macaron.Macaron) {
// authed views
r.Get("/profile/", reqSignedIn, Index)
r.Get("/profile/password", reqSignedIn, Index)
r.Get("/profile/switch-org/:id", reqSignedIn, ChangeActiveOrgAndRedirectToHome)
r.Get("/org/", reqSignedIn, Index)
r.Get("/org/new", reqSignedIn, Index)
r.Get("/datasources/", reqSignedIn, Index)
@@ -252,6 +253,9 @@ func Register(r *macaron.Macaron) {
// rendering
r.Get("/render/*", reqSignedIn, RenderToPng)
// grafana.net proxy
r.Any("/api/gnet/*", reqSignedIn, ProxyGnetRequest)
// Gravatar service.
avt := avatar.CacheServer()
r.Get("/avatar/:hash", avt.ServeHTTP)
+1
View File
@@ -116,6 +116,7 @@ func (this *service) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if avatar.Expired() {
if err := avatar.Update(); err != nil {
log.Trace("avatar update error: %v", err)
avatar = this.notFound
}
}
+11 -6
View File
@@ -15,15 +15,20 @@ type PluginSetting struct {
Dependencies *plugins.PluginDependencies `json:"dependencies"`
JsonData map[string]interface{} `json:"jsonData"`
DefaultNavUrl string `json:"defaultNavUrl"`
LatestVersion string `json:"latestVersion"`
HasUpdate bool `json:"hasUpdate"`
}
type PluginListItem struct {
Name string `json:"name"`
Type string `json:"type"`
Id string `json:"id"`
Enabled bool `json:"enabled"`
Pinned bool `json:"pinned"`
Info *plugins.PluginInfo `json:"info"`
Name string `json:"name"`
Type string `json:"type"`
Id string `json:"id"`
Enabled bool `json:"enabled"`
Pinned bool `json:"pinned"`
Info *plugins.PluginInfo `json:"info"`
LatestVersion string `json:"latestVersion"`
HasUpdate bool `json:"hasUpdate"`
}
type PluginList []PluginListItem
+5 -3
View File
@@ -137,9 +137,11 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro
"allowOrgCreate": (setting.AllowUserOrgCreate && c.IsSignedIn) || c.IsGrafanaAdmin,
"authProxyEnabled": setting.AuthProxyEnabled,
"buildInfo": map[string]interface{}{
"version": setting.BuildVersion,
"commit": setting.BuildCommit,
"buildstamp": setting.BuildStamp,
"version": setting.BuildVersion,
"commit": setting.BuildCommit,
"buildstamp": setting.BuildStamp,
"latestVersion": plugins.GrafanaLatestVersion,
"hasUpdate": plugins.GrafanaHasUpdate,
},
}
+46
View File
@@ -0,0 +1,46 @@
package api
import (
"crypto/tls"
"net"
"net/http"
"net/http/httputil"
"time"
"github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/util"
)
var gNetProxyTransport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: false},
Proxy: http.ProxyFromEnvironment,
Dial: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
TLSHandshakeTimeout: 10 * time.Second,
}
func ReverseProxyGnetReq(proxyPath string) *httputil.ReverseProxy {
director := func(req *http.Request) {
req.URL.Scheme = "https"
req.URL.Host = "grafana.net"
req.Host = "grafana.net"
req.URL.Path = util.JoinUrlFragments("https://grafana.net/api", proxyPath)
// clear cookie headers
req.Header.Del("Cookie")
req.Header.Del("Set-Cookie")
}
return &httputil.ReverseProxy{Director: director}
}
func ProxyGnetRequest(c *middleware.Context) {
proxyPath := c.Params("*")
proxy := ReverseProxyGnetReq(proxyPath)
proxy.Transport = gNetProxyTransport
proxy.ServeHTTP(c.Resp, c.Req.Request)
c.Resp.Header().Del("Set-Cookie")
}
+14 -4
View File
@@ -14,6 +14,7 @@ func GetPluginList(c *middleware.Context) Response {
typeFilter := c.Query("type")
enabledFilter := c.Query("enabled")
embeddedFilter := c.Query("embedded")
coreFilter := c.Query("core")
pluginSettingsMap, err := plugins.GetPluginSettings(c.OrgId)
@@ -28,16 +29,23 @@ func GetPluginList(c *middleware.Context) Response {
continue
}
// filter out core plugins
if coreFilter == "0" && pluginDef.IsCorePlugin {
continue
}
// filter on type
if typeFilter != "" && typeFilter != pluginDef.Type {
continue
}
listItem := dtos.PluginListItem{
Id: pluginDef.Id,
Name: pluginDef.Name,
Type: pluginDef.Type,
Info: &pluginDef.Info,
Id: pluginDef.Id,
Name: pluginDef.Name,
Type: pluginDef.Type,
Info: &pluginDef.Info,
LatestVersion: pluginDef.GrafanaNetVersion,
HasUpdate: pluginDef.GrafanaNetHasUpdate,
}
if pluginSetting, exists := pluginSettingsMap[pluginDef.Id]; exists {
@@ -81,6 +89,8 @@ func GetPluginSettingById(c *middleware.Context) Response {
BaseUrl: def.BaseUrl,
Module: def.Module,
DefaultNavUrl: def.DefaultNavUrl,
LatestVersion: def.GrafanaNetVersion,
HasUpdate: def.GrafanaNetHasUpdate,
}
query := m.GetPluginSettingByIdQuery{PluginId: pluginId, OrgId: c.OrgId}
+18
View File
@@ -4,6 +4,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
)
@@ -109,6 +110,23 @@ func UserSetUsingOrg(c *middleware.Context) Response {
return ApiSuccess("Active organization changed")
}
// GET /profile/switch-org/:id
func ChangeActiveOrgAndRedirectToHome(c *middleware.Context) {
orgId := c.ParamsInt64(":id")
if !validateUsingOrg(c.UserId, orgId) {
NotFoundHandler(c)
}
cmd := m.SetUsingOrgCommand{UserId: c.UserId, OrgId: orgId}
if err := bus.Dispatch(&cmd); err != nil {
NotFoundHandler(c)
}
c.Redirect(setting.AppSubUrl + "/")
}
func ChangeUserPassword(c *middleware.Context, cmd m.ChangeUserPasswordCommand) Response {
userQuery := m.GetUserByIdQuery{Id: c.UserId}
+16 -9
View File
@@ -1,9 +1,10 @@
package commands
import (
"os"
"github.com/codegangsta/cli"
"github.com/grafana/grafana/pkg/cmd/grafana-cli/log"
"os"
)
func runCommand(command func(commandLine CommandLine) error) func(context *cli.Context) {
@@ -25,27 +26,33 @@ func runCommand(command func(commandLine CommandLine) error) func(context *cli.C
var pluginCommands = []cli.Command{
{
Name: "install",
Usage: "install <plugin name>",
Usage: "install <plugin id>",
Action: runCommand(installCommand),
}, {
Name: "list-remote",
Usage: "list remote available plugins",
Action: runCommand(listremoteCommand),
}, {
Name: "upgrade",
Usage: "upgrade <plugin name>",
Action: runCommand(upgradeCommand),
Name: "update",
Usage: "update <plugin id>",
Aliases: []string{"upgrade"},
Action: runCommand(upgradeCommand),
}, {
Name: "upgrade-all",
Usage: "upgrades all your installed plugins",
Action: runCommand(upgradeAllCommand),
Name: "update-all",
Aliases: []string{"upgrade-all"},
Usage: "update all your installed plugins",
Action: runCommand(upgradeAllCommand),
}, {
Name: "ls",
Usage: "list all installed plugins",
Action: runCommand(lsCommand),
}, {
Name: "uninstall",
Usage: "uninstall <plugin id>",
Action: runCommand(removeCommand),
}, {
Name: "remove",
Usage: "remove <plugin name>",
Usage: "remove <plugin id>",
Action: runCommand(removeCommand),
},
}
@@ -120,6 +120,7 @@ func RemoveGitBuildFromName(pluginName, filename string) string {
}
var retryCount = 0
var permissionsDeniedMessage = "Could not create %s. Permission denied. Make sure you have write access to plugindir"
func downloadFile(pluginName, filePath, url string) (err error) {
defer func() {
@@ -153,16 +154,16 @@ func downloadFile(pluginName, filePath, url string) (err error) {
newFile := path.Join(filePath, RemoveGitBuildFromName(pluginName, zf.Name))
if zf.FileInfo().IsDir() {
os.Mkdir(newFile, 0777)
err := os.Mkdir(newFile, 0777)
if PermissionsError(err) {
return fmt.Errorf(permissionsDeniedMessage, newFile)
}
} else {
dst, err := os.Create(newFile)
if err != nil {
if strings.Contains(err.Error(), "permission denied") {
return fmt.Errorf(
"Could not create file %s. permission deined. Make sure you have write access to plugindir",
newFile)
}
if PermissionsError(err) {
return fmt.Errorf(permissionsDeniedMessage, newFile)
}
defer dst.Close()
src, err := zf.Open()
if err != nil {
@@ -176,3 +177,7 @@ func downloadFile(pluginName, filePath, url string) (err error) {
return nil
}
func PermissionsError(err error) bool {
return err != nil && strings.Contains(err.Error(), "permission denied")
}
@@ -51,7 +51,7 @@ func upgradeAllCommand(c CommandLine) error {
}
for _, p := range pluginsToUpgrade {
log.Infof("Upgrading %v \n", p.Id)
log.Infof("Updating %v \n", p.Id)
s.RemoveInstalledPlugin(pluginsDir, p.Id)
InstallPlugin(p.Id, "", c)
+24 -5
View File
@@ -13,12 +13,31 @@ import (
var version = "master"
func getGrafanaPluginDir() string {
os := runtime.GOOS
if os == "windows" {
return "C:\\opt\\grafana\\plugins"
} else {
return "/var/lib/grafana/plugins"
currentOS := runtime.GOOS
defaultNix := "/var/lib/grafana/plugins"
if currentOS == "windows" {
return "..\\data\\plugins"
}
pwd, err := os.Getwd()
if err != nil {
log.Error("Could not get current path. using default")
return defaultNix
}
if isDevenvironment(pwd) {
return "../data/plugins"
}
return defaultNix
}
func isDevenvironment(pwd string) bool {
// if ../conf/default.ini exists, grafana is not installed as package
_, err := os.Stat("../conf/default.ini")
return err != nil
}
func main() {
+1 -1
View File
@@ -24,7 +24,7 @@ import (
"github.com/grafana/grafana/pkg/social"
)
var version = "3.0.0-pre1"
var version = "3.0.0-beta2"
var commit = "NA"
var buildstamp string
var build_date string
+4
View File
@@ -10,6 +10,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/setting"
)
@@ -56,6 +57,9 @@ func sendUsageStats() {
metrics["stats.users.count"] = statsQuery.Result.UserCount
metrics["stats.orgs.count"] = statsQuery.Result.OrgCount
metrics["stats.playlist.count"] = statsQuery.Result.PlaylistCount
metrics["stats.plugins.apps.count"] = len(plugins.Apps)
metrics["stats.plugins.panels.count"] = len(plugins.Panels)
metrics["stats.plugins.datasources.count"] = len(plugins.DataSources)
dsStats := m.GetDataSourceStatsQuery{}
if err := bus.Dispatch(&dsStats); err != nil {
+8 -9
View File
@@ -21,15 +21,14 @@ type GetDataSourceStatsQuery struct {
}
type AdminStats struct {
UserCount int `json:"user_count"`
OrgCount int `json:"org_count"`
DashboardCount int `json:"dashboard_count"`
DbSnapshotCount int `json:"db_snapshot_count"`
DbTagCount int `json:"db_tag_count"`
DataSourceCount int `json:"data_source_count"`
PlaylistCount int `json:"playlist_count"`
StarredDbCount int `json:"starred_db_count"`
GrafanaAdminCount int `json:"grafana_admin_count"`
UserCount int `json:"user_count"`
OrgCount int `json:"org_count"`
DashboardCount int `json:"dashboard_count"`
DbSnapshotCount int `json:"db_snapshot_count"`
DbTagCount int `json:"db_tag_count"`
DataSourceCount int `json:"data_source_count"`
PlaylistCount int `json:"playlist_count"`
StarredDbCount int `json:"starred_db_count"`
}
type GetAdminStatsQuery struct {
+4 -3
View File
@@ -14,7 +14,7 @@ type FrontendPluginBase struct {
}
func (fp *FrontendPluginBase) initFrontendPlugin() {
if isInternalPlugin(fp.PluginDir) {
if isExternalPlugin(fp.PluginDir) {
StaticRoutes = append(StaticRoutes, &PluginStaticRoute{
Directory: fp.PluginDir,
PluginId: fp.Id,
@@ -48,17 +48,18 @@ func (fp *FrontendPluginBase) setPathsBasedOnApp(app *AppPlugin) {
func (fp *FrontendPluginBase) handleModuleDefaults() {
if isInternalPlugin(fp.PluginDir) {
if isExternalPlugin(fp.PluginDir) {
fp.Module = path.Join("plugins", fp.Id, "module")
fp.BaseUrl = path.Join("public/plugins", fp.Id)
return
}
fp.IsCorePlugin = true
fp.Module = path.Join("app/plugins", fp.Type, fp.Id, "module")
fp.BaseUrl = path.Join("public/app/plugins", fp.Type, fp.Id)
}
func isInternalPlugin(pluginDir string) bool {
func isExternalPlugin(pluginDir string) bool {
return !strings.Contains(pluginDir, setting.StaticRootPath)
}
+4
View File
@@ -43,6 +43,10 @@ type PluginBase struct {
IncludedInAppId string `json:"-"`
PluginDir string `json:"-"`
DefaultNavUrl string `json:"-"`
IsCorePlugin bool `json:"-"`
GrafanaNetVersion string `json:"-"`
GrafanaNetHasUpdate bool `json:"-"`
// cache for readme file contents
Readme []byte `json:"-"`
+4
View File
@@ -22,6 +22,9 @@ var (
Apps map[string]*AppPlugin
Plugins map[string]*PluginBase
PluginTypes map[string]interface{}
GrafanaLatestVersion string
GrafanaHasUpdate bool
)
type PluginScanner struct {
@@ -70,6 +73,7 @@ func Init() error {
app.initApp()
}
go StartPluginUpdateChecker()
return nil
}
+119
View File
@@ -0,0 +1,119 @@
package plugins
import (
"encoding/json"
"io/ioutil"
"net/http"
"strings"
"time"
"github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/setting"
)
type GrafanaNetPlugin struct {
Slug string `json:"slug"`
Version string `json:"version"`
}
type GithubLatest struct {
Stable string `json:"stable"`
Testing string `json:"testing"`
}
func StartPluginUpdateChecker() {
if !setting.CheckForUpdates {
return
}
// do one check directly
go checkForUpdates()
ticker := time.NewTicker(time.Minute * 10)
for {
select {
case <-ticker.C:
checkForUpdates()
}
}
}
func getAllExternalPluginSlugs() string {
str := ""
for _, plug := range Plugins {
if plug.IsCorePlugin {
continue
}
str += plug.Id + ","
}
return str
}
func checkForUpdates() {
log.Trace("Checking for updates")
client := http.Client{Timeout: time.Duration(5 * time.Second)}
pluginSlugs := getAllExternalPluginSlugs()
resp, err := client.Get("https://grafana.net/api/plugins/versioncheck?slugIn=" + pluginSlugs + "&grafanaVersion=" + setting.BuildVersion)
if err != nil {
log.Trace("Failed to get plugins repo from grafana.net, %v", err.Error())
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Trace("Update check failed, reading response from grafana.net, %v", err.Error())
return
}
gNetPlugins := []GrafanaNetPlugin{}
err = json.Unmarshal(body, &gNetPlugins)
if err != nil {
log.Trace("Failed to unmarshal plugin repo, reading response from grafana.net, %v", err.Error())
return
}
for _, plug := range Plugins {
for _, gplug := range gNetPlugins {
if gplug.Slug == plug.Id {
plug.GrafanaNetVersion = gplug.Version
plug.GrafanaNetHasUpdate = plug.Info.Version != plug.GrafanaNetVersion
}
}
}
resp2, err := client.Get("https://raw.githubusercontent.com/grafana/grafana/master/latest.json")
if err != nil {
log.Trace("Failed to get lates.json repo from github: %v", err.Error())
return
}
defer resp2.Body.Close()
body, err = ioutil.ReadAll(resp2.Body)
if err != nil {
log.Trace("Update check failed, reading response from github.net, %v", err.Error())
return
}
var githubLatest GithubLatest
err = json.Unmarshal(body, &githubLatest)
if err != nil {
log.Trace("Failed to unmarshal github latest, reading response from github: %v", err.Error())
return
}
if strings.Contains(setting.BuildVersion, "-") {
GrafanaLatestVersion = githubLatest.Testing
GrafanaHasUpdate = strings.HasPrefix(setting.BuildVersion, githubLatest.Testing)
} else {
GrafanaLatestVersion = githubLatest.Stable
GrafanaHasUpdate = githubLatest.Stable != setting.BuildVersion
}
}
@@ -10,12 +10,12 @@ func addPreferencesMigrations(mg *Migrator) {
Name: "preferences",
Columns: []*Column{
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
{Name: "org_id", Type: DB_Int, Nullable: true},
{Name: "user_id", Type: DB_NVarchar, Length: 255, Nullable: true},
{Name: "org_id", Type: DB_BigInt, Nullable: false},
{Name: "user_id", Type: DB_BigInt, Nullable: false},
{Name: "version", Type: DB_Int, Nullable: false},
{Name: "home_dashboard_id", Type: DB_BigInt, Nullable: true},
{Name: "timezone", Type: DB_NVarchar, Length: 50, Nullable: true},
{Name: "theme", Type: DB_NVarchar, Length: 20, Nullable: true},
{Name: "home_dashboard_id", Type: DB_BigInt, Nullable: false},
{Name: "timezone", Type: DB_NVarchar, Length: 50, Nullable: false},
{Name: "theme", Type: DB_NVarchar, Length: 20, Nullable: false},
{Name: "created", Type: DB_DateTime, Nullable: false},
{Name: "updated", Type: DB_DateTime, Nullable: false},
},
@@ -25,6 +25,8 @@ func addPreferencesMigrations(mg *Migrator) {
},
}
mg.AddMigration("drop preferences table v3", NewDropTableMigration("preferences"))
// create table
mg.AddMigration("create preferences table v2", NewAddTableMigration(preferencesV2))
mg.AddMigration("create preferences table v3", NewAddTableMigration(preferencesV2))
}
+1 -6
View File
@@ -85,12 +85,7 @@ func GetAdminStats(query *m.GetAdminStatsQuery) error {
(
SELECT COUNT(DISTINCT ` + dialect.Quote("dashboard_id") + ` )
FROM ` + dialect.Quote("star") + `
) AS starred_db_count,
(
SELECT COUNT(*)
FROM ` + dialect.Quote("user") + `
WHERE ` + dialect.Quote("is_admin") + ` = 1
) AS grafana_admin_count
) AS starred_db_count
`
var stats m.AdminStats
+2
View File
@@ -124,6 +124,7 @@ var (
appliedEnvOverrides []string
ReportingEnabled bool
CheckForUpdates bool
GoogleAnalyticsId string
GoogleTagManagerId string
@@ -475,6 +476,7 @@ func NewConfigContext(args *CommandLineArgs) error {
analytics := Cfg.Section("analytics")
ReportingEnabled = analytics.Key("reporting_enabled").MustBool(true)
CheckForUpdates = analytics.Key("check_for_updates").MustBool(true)
GoogleAnalyticsId = analytics.Key("google_analytics_ua_id").String()
GoogleTagManagerId = analytics.Key("google_tag_manager_id").String()
@@ -21,10 +21,6 @@
<i class="{{::menuItem.icon}}" ng-show="::menuItem.icon"></i>
{{::menuItem.text}}
</a>
<a ng-click="menuItem.click()" ng-show="::menuItem.click">
<i class="{{::menuItem.icon}}"></i>
{{::menuItem.text}}
</a>
</li>
</ul>
</li>
@@ -72,9 +72,8 @@ export class SideMenuCtrl {
this.orgMenu.push({
text: "Switch to " + org.name,
icon: "fa fa-fw fa-random",
click: () => {
this.switchOrg(org.orgId);
}
url: this.getUrl('/profile/switch-org/' + org.orgId),
target: '_self'
});
});
@@ -83,12 +82,6 @@ export class SideMenuCtrl {
}
});
}
switchOrg(orgId) {
this.backendSrv.post('/api/user/using/' + orgId).then(() => {
window.location.href = `${config.appSubUrl}/`;
});
};
}
export function sideMenuDirective() {
+3 -6
View File
@@ -21,17 +21,14 @@ export class SwitchCtrl {
id: any;
/** @ngInject */
constructor($scope) {
constructor($scope, private $timeout) {
this.show = true;
this.id = $scope.$id;
}
internalOnChange() {
return new Promise(resolve => {
setTimeout(() => {
this.onChange();
resolve();
});
return this.$timeout(() => {
return this.onChange();
});
}
+3 -1
View File
@@ -39,7 +39,9 @@ function (angular, coreModule, config) {
$scope.buildInfo = {
version: config.buildInfo.version,
commit: config.buildInfo.commit,
buildstamp: new Date(config.buildInfo.buildstamp * 1000)
buildstamp: new Date(config.buildInfo.buildstamp * 1000),
latestVersion: config.buildInfo.latestVersion,
hasUpdate: config.buildInfo.hasUpdate,
};
$scope.submit = function() {
@@ -169,7 +169,7 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $
return System.import(model.module).then(function(appModule) {
return {
baseUrl: model.baseUrl,
name: 'app-config-' + model.pluginId,
name: 'app-config-' + model.id,
bindings: {appModel: "=", appEditCtrl: "="},
attrs: {"app-model": "ctrl.model", "app-edit-ctrl": "ctrl"},
Component: appModule.ConfigCtrl,
+4 -1
View File
@@ -46,9 +46,12 @@ function popoverSrv($compile, $rootScope) {
drop.on('close', () => {
popoverScope.dismiss({fromDropClose: true});
destroyDrop();
if (options.onClose) {
options.onClose();
}
});
drop.open();
setTimeout(() => { drop.open(); }, 10);
};
}
+1 -1
View File
@@ -170,7 +170,7 @@ export default class TimeSeries {
}
isMsResolutionNeeded() {
for (var i = 0; i<this.datapoints.length; i++) {
for (var i = 0; i < this.datapoints.length; i++) {
if (this.datapoints[i][0] !== null) {
var timestamp = this.datapoints[i][0].toString();
if (timestamp.length === 13 && (timestamp % 1000) !== 0) {
+8 -29
View File
@@ -1,6 +1,6 @@
///<reference path="../../headers/common.d.ts" />
import {Subject} from 'vendor/npm/rxjs/Subject';
import EventEmitter from 'eventemitter3';
var hasOwnProp = {}.hasOwnProperty;
@@ -9,48 +9,27 @@ function createName(name) {
}
export class Emitter {
subjects: any;
emitter: any;
constructor() {
this.subjects = {};
this.emitter = new EventEmitter();
}
emit(name, data?) {
var fnName = createName(name);
this.subjects[fnName] || (this.subjects[fnName] = new Subject());
this.subjects[fnName].next(data);
this.emitter.emit(name, data);
}
on(name, handler, scope?) {
var fnName = createName(name);
this.subjects[fnName] || (this.subjects[fnName] = new Subject());
var subscription = this.subjects[fnName].subscribe(handler);
this.emitter.on(name, handler);
if (scope) {
scope.$on('$destroy', function() {
subscription.unsubscribe();
this.emitter.off(name, handler);
});
}
return subscription;
};
}
off(name, handler) {
var fnName = createName(name);
if (this.subjects[fnName]) {
this.subjects[fnName].dispose();
delete this.subjects[fnName];
}
}
dispose() {
var subjects = this.subjects;
for (var prop in subjects) {
if (hasOwnProp.call(subjects, prop)) {
subjects[prop].dispose();
}
}
this.subjects = {};
this.emitter.off(name, handler);
}
}
@@ -22,10 +22,6 @@
<td>Total users</td>
<td>{{ctrl.stats.user_count}}</td>
</tr>
<tr>
<td>Total grafana admins</td>
<td>{{ctrl.stats.grafana_admin_count}}</td>
</tr>
<tr>
<td>Total organizations</td>
<td>{{ctrl.stats.org_count}}</td>
@@ -428,6 +428,8 @@ function (angular, $, _, moment) {
// update graph yaxes changes
panelUpgrades.push(function(panel) {
if (panel.type !== 'graph') { return; }
if (!panel.grid) { return; }
if (!panel.yaxes) {
panel.yaxes = [
{
@@ -48,7 +48,6 @@
<li ng-if="dashboardMeta.canEdit"><a class="pointer" ng-click="editJson();">View JSON</a></li>
<li ng-if="contextSrv.isEditor && !dashboard.editable"><a class="pointer" ng-click="makeEditable();">Make Editable</a></li>
<li ng-if="contextSrv.isEditor"><a class="pointer" ng-click="saveDashboardAs();">Save As...</a></li>
<li ng-if="dashboard.id"><a class="pointer" ng-click="saveDashboardAsHome();">Set As Home</a></li>
<li ng-if="dashboardMeta.canSave"><a class="pointer" ng-click="deleteDashboard();">Delete dashboard</a></li>
</ul>
</li>
@@ -38,31 +38,26 @@
<div ng-include src="'shareLinkOptions.html'"></div>
<div class="gf-form-group position-center">
<div class="gf-form width-30" >
<div class="gf-form-group section">
<div class="gf-form width-30">
<textarea rows="5" data-share-panel-url class="gf-form-input width-30" ng-model='iframeHtml'></textarea>
</div>
</div>
<div class="gf-form-group">
<div class="gf-form position-center">
<button class="btn btn-inverse" data-clipboard-text="{{iframeHtml}}" clipboard-button><i class="fa fa-clipboard"></i> Copy</button>
</div>
</div>
</script>
<script type="text/ng-template" id="shareLinkOptions.html">
<div class="gf-form-group position-center">
<div class="gf-form-group section">
<gf-form-switch class="gf-form"
label="Current time range" label-class="width-12" switch-class="max-width-6"
checked="options.forCurrent" on-change="buildUrl()">
</gf-form-switch>
<gf-form-switch class="gf-form"
label="Template variables" label-class="width-12" switch-class="max-width-6"
checked="options.includeTemplateVars" on-change="buildUrl()">
</gf-form-switch>
<div class="gf-form">
<span class="gf-form-label width-5">Include</span>
<editor-checkbox text="Current time range" model="options.forCurrent" change="buildUrl()"></editor-checkbox>
</div>
<div class="gf-form">
<span class="gf-form-label width-5">Include</span>
<editor-checkbox text="Template variables" model="options.includeTemplateVars" change="buildUrl()"></editor-checkbox>
</div>
<div class="gf-form">
<span class="gf-form-label width-5">Theme</span>
<div class="gf-form-select-wrapper max-width-10">
<span class="gf-form-label width-12">Theme</span>
<div class="gf-form-select-wrapper width-6">
<select class="gf-form-input" ng-model="options.theme" ng-options="f as f for f in ['current', 'dark', 'light']" ng-change="buildUrl()"></select>
</div>
</div>
@@ -75,18 +70,19 @@
</div>
<div ng-include src="'shareLinkOptions.html'"></div>
<div class="gf-form-group position-center">
<div class="gf-form-inline">
<div class="gf-form width-30">
<input type="text" data-share-panel-url class="gf-form-input" ng-model="shareUrl"></input>
</div>
<div class="gf-form pull-right">
<button class="btn btn-inverse pull-right" data-clipboard-text="{{shareUrl}}" clipboard-button><i class="fa fa-clipboard"></i> Copy</button>
<div>
<div class="gf-form-group section">
<div class="gf-form-inline">
<div class="gf-form width-30">
<input type="text" data-share-panel-url class="gf-form-input" ng-model="shareUrl"></input>
</div>
<div class="gf-form pull-right">
<button class="btn btn-inverse pull-right" data-clipboard-text="{{shareUrl}}" clipboard-button><i class="fa fa-clipboard"></i> Copy</button>
</div>
</div>
</div>
</div>
<div class="gf-form position-center" ng-show="modeSharePanel">
<div class="gf-form section" ng-show="modeSharePanel">
<a href="{{imageUrl}}" target="_blank"><i class="fa fa-camera"></i> Direct link rendered image</a>
</div>
</script>
@@ -117,7 +113,7 @@
</p>
</div>
<div class="gf-form-group share-modal-options position-center">
<div class="gf-form-group share-modal-options">
<div class="gf-form" ng-if="step === 1">
<span class="gf-form-label width-12">Snapshot name</span>
<input type="text" ng-model="snapshot.name" class="gf-form-input max-width-15" >
@@ -82,6 +82,7 @@ class MetricsPanelCtrl extends PanelCtrl {
this.loading = true;
// load datasource service
this.setTimeQueryStart();
this.datasourceSrv.get(this.panel.datasource)
.then(this.issueQueries.bind(this))
.then(this.handleQueryResult.bind(this))
@@ -183,7 +184,6 @@ class MetricsPanelCtrl extends PanelCtrl {
cacheTimeout: this.panel.cacheTimeout
};
this.setTimeQueryStart();
return datasource.query(metricsQuery);
}
@@ -251,8 +251,12 @@ class MetricsPanelCtrl extends PanelCtrl {
}
addDataQuery(datasource) {
var target = {
};
var target: any = {};
if (datasource) {
target.datasource = datasource.name;
}
this.panel.targets.push(target);
}
}
+1
View File
@@ -46,6 +46,7 @@ export class PanelCtrl {
$scope.$on("refresh", () => this.refresh());
$scope.$on("render", () => this.render());
$scope.$on("$destroy", () => this.events.emit('panel-teardown'));
}
init() {
@@ -31,7 +31,7 @@
</div>
<gf-form-switch class="gf-form max-width-30"
label="Hide time override info" label-class="width-12"
checked="ctrl.panel.hideTimeOverride" switch-class="max-width-6" on-change="ctrl.render()">
checked="ctrl.panel.hideTimeOverride" switch-class="max-width-6" on-change="ctrl.refresh()">
</gf-form-switch>
</div>
</div>
@@ -5,16 +5,16 @@
<div class="gf-form">
<span class="gf-form-label width-7">Url</span>
<input class="gf-form-input max-width-21" type="text" ng-model='current.url' placeholder="http://my.server.com:8080" ng-pattern="/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/" required></input>
<input class="gf-form-input max-width-21" type="text" ng-model='current.url' placeholder="for example: http://localhost:8081" ng-pattern="/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/" required></input>
<info-popover>
<p>Specify a complete HTTP url (http://your_server:8080)</p>
<p>Specify a complete HTTP url (for example http://your_server:8080)</p>
<span ng-show="current.access === 'direct'">
Your access method is <code>Direct</code>, this means the url
Your access method is <em>Direct</em>, this means the url
needs to be accessable from the browser.
</span>
<span ng-show="current.access === 'proxy'">
Your access method is currently <code>Proxy</code>, this means the url
Your access method is currently <em>Proxy</em>, this means the url
needs to be accessable from the grafana backend.
</span>
</info-popover>
@@ -20,8 +20,9 @@
<li class="card-item-wrapper" ng-repeat="ds in ctrl.datasources">
<a class="card-item" href="datasources/edit/{{ds.id}}/">
<div class="card-item-header">
<i class="icon-gf icon-gf-{{ds.type}}"></i>
{{ds.type}}
<div class="card-item-type">
{{ds.type}}
</div>
</div>
<div class="card-item-body">
<figure class="card-item-figure">
@@ -4,8 +4,8 @@
<div class="page-container" ng-init="ctrl.init()">
<div class="page-header">
<div class="plugin-header">
<span ng-show="ctrl.model.info.logos.large" class="plugin-header-logo">
<img src="{{ctrl.model.info.logos.large}}">
<span class="plugin-header-logo">
<img ng-src="{{ctrl.model.info.logos.large}}">
</span>
<div class="plugin-header-info-block">
@@ -55,6 +55,9 @@
<section class="page-sidebar-section">
<h4>Version</h4>
<span>{{ctrl.model.info.version}}</span>
<div ng-show="ctrl.model.hasUpdate">
<a ng-click="ctrl.updateAvailable()" bs-tooltip="ctrl.model.latestVersion">Update Available!</a>
</div>
</section>
<section class="page-sidebar-section" ng-show="ctrl.model.type === 'app'">
<h5>Includes</h4>
@@ -33,8 +33,13 @@
<li class="card-item-wrapper" ng-repeat="plugin in ctrl.plugins">
<a class="card-item" href="plugins/{{plugin.id}}/edit">
<div class="card-item-header">
<i class="icon-gf icon-gf-{{plugin.type}}"></i>
{{plugin.type}}
<div class="card-item-type">
<i class="icon-gf icon-gf-{{plugin.type}}"></i>
{{plugin.type}}
</div>
<div class="card-item-notice" ng-show="plugin.hasUpdate">
<span bs-tooltip="plugin.latestVersion">Update available!</span>
</div>
</div>
<div class="card-item-body">
<figure class="card-item-figure">
@@ -0,0 +1,21 @@
<div class="modal-body">
<div class="modal-header">
<h2 class="modal-header-title">
<i class="fa fa-cloud-download"></i>
<span class="p-l-1">Update Plugin</span>
</h2>
<a class="modal-header-close" ng-click="dismiss();">
<i class="fa fa-remove"></i>
</a>
</div>
<div class="modal-content">
<div class="gf-form-group">
<p>Type the following on the command line to update {{plugin.name}}.</p>
<pre><code>grafana-cli plugins update {{plugin.id}}</code></pre>
<span class="small">Check out {{plugin.name}} on <a href="http://grafana/net/plugins/{{plugin.id}}">Grafana.net</a> for README and changelog. If you do not have access to the command line, ask your Grafana administator.</span>
</div>
<p class="pluginlist-none-installed code--line"><img class="pluginlist-inline-logo" src="public/img/grafana_icon.svg"><strong>Pro tip</strong>: To update all plugins at once, type <code class="code--small">grafana-cli plugins update-all</code> on the command line.</div>
</div>
</div>
@@ -19,6 +19,7 @@ export class PluginEditCtrl {
/** @ngInject */
constructor(private $scope,
private $rootScope,
private backendSrv,
private $routeParams,
private $sce,
@@ -47,6 +48,7 @@ export class PluginEditCtrl {
});
if (this.model.type === 'app') {
this.tabIndex = 1;
this.tabs.push('Config');
this.hasDashboards = _.findWhere(result.includes, {type: 'dashboard'});
@@ -73,7 +75,7 @@ export class PluginEditCtrl {
case 'datasource': return 'icon-gf icon-gf-datasources';
case 'panel': return 'icon-gf icon-gf-panel';
case 'app': return 'icon-gf icon-gf-apps';
case 'page': return 'icon-gf icon-gf-share';
case 'page': return 'icon-gf icon-gf-endpoint-tiny';
case 'dashboard': return 'icon-gf icon-gf-dashboard';
}
}
@@ -128,6 +130,16 @@ export class PluginEditCtrl {
this.postUpdateHook = callback;
}
updateAvailable() {
var modalScope = this.$scope.$new(true);
modalScope.plugin = this.model;
this.$rootScope.appEvent('show-modal', {
src: 'public/app/features/plugins/partials/update_instructions.html',
scope: modalScope
});
}
enable() {
this.model.enabled = true;
this.model.pinned = true;
@@ -142,4 +154,3 @@ export class PluginEditCtrl {
}
angular.module('grafana.controllers').controller('PluginEditCtrl', PluginEditCtrl);
@@ -272,7 +272,9 @@ function (angular, _, kbn) {
}
for (i = 0; i < metricNames.length; i++) {
var value = metricNames[i].text;
var item = metricNames[i];
var value = item.value || item.text;
var text = item.text || item.value;
if (regex) {
matches = regex.exec(value);
@@ -282,12 +284,11 @@ function (angular, _, kbn) {
}
}
options[value] = value;
options[value] = {text: text, value: value};
}
return _.map(_.keys(options).sort(), function(key) {
var option = { text: key, value: key };
return option;
return options[key];
});
};
+6 -2
View File
@@ -1,5 +1,4 @@
///<reference path="../../vendor/npm/angular2/typings/es6-promise/es6-promise.d.ts" />
///<reference path="../../vendor/npm/angular2/typings/es6-collections/es6-collections.d.ts" />
/// <reference path="./es6-shim/es6-shim.d.ts" />
declare var System: any;
@@ -48,3 +47,8 @@ declare module 'tether-drop' {
var config: any;
export default config;
}
declare module 'eventemitter3' {
var config: any;
export default config;
}
-73
View File
@@ -1,73 +0,0 @@
// Type definitions for es6-promise
// Project: https://github.com/jakearchibald/ES6-Promise
// Definitions by: François de Campredon <https://github.com/fdecampredon/>, vvakame <https://github.com/vvakame>
// Definitions: https://github.com/borisyankov/DefinitelyTyped
interface Thenable<R> {
then<U>(onFulfilled?: (value: R) => U | Thenable<U>, onRejected?: (error: any) => U | Thenable<U>): Thenable<U>;
then<U>(onFulfilled?: (value: R) => U | Thenable<U>, onRejected?: (error: any) => void): Thenable<U>;
}
declare class Promise<R> implements Thenable<R> {
/**
* If you call resolve in the body of the callback passed to the constructor,
* your promise is fulfilled with result object passed to resolve.
* If you call reject your promise is rejected with the object passed to resolve.
* For consistency and debugging (eg stack traces), obj should be an instanceof Error.
* Any errors thrown in the constructor callback will be implicitly passed to reject().
*/
constructor(callback: (resolve : (value?: R | Thenable<R>) => void, reject: (error?: any) => void) => void);
/**
* onFulfilled is called when/if "promise" resolves. onRejected is called when/if "promise" rejects.
* Both are optional, if either/both are omitted the next onFulfilled/onRejected in the chain is called.
* Both callbacks have a single parameter , the fulfillment value or rejection reason.
* "then" returns a new promise equivalent to the value you return from onFulfilled/onRejected after being passed through Promise.resolve.
* If an error is thrown in the callback, the returned promise rejects with that error.
*
* @param onFulfilled called when/if "promise" resolves
* @param onRejected called when/if "promise" rejects
*/
then<U>(onFulfilled?: (value: R) => U | Thenable<U>, onRejected?: (error: any) => U | Thenable<U>): Promise<U>;
then<U>(onFulfilled?: (value: R) => U | Thenable<U>, onRejected?: (error: any) => void): Promise<U>;
/**
* Sugar for promise.then(undefined, onRejected)
*
* @param onRejected called when/if "promise" rejects
*/
catch<U>(onRejected?: (error: any) => U | Thenable<U>): Promise<U>;
}
declare module Promise {
/**
* Make a new promise from the thenable.
* A thenable is promise-like in as far as it has a "then" method.
*/
function resolve<R>(value?: R | Thenable<R>): Promise<R>;
/**
* Make a promise that rejects to obj. For consistency and debugging (eg stack traces), obj should be an instanceof Error
*/
function reject(error: any): Promise<any>;
/**
* Make a promise that fulfills when every item in the array fulfills, and rejects if (and when) any item rejects.
* the array passed to all can be a mixture of promise-like objects and other objects.
* The fulfillment value is an array (in order) of fulfillment values. The rejection value is the first rejection value.
*/
function all<R>(promises: (R | Thenable<R>)[]): Promise<R[]>;
/**
* Make a Promise that fulfills when any item fulfills, and rejects if any item rejects.
*/
function race<R>(promises: (R | Thenable<R>)[]): Promise<R>;
}
declare module 'es6-promise' {
var foo: typeof Promise; // Temp variable to reference Promise in local context
module rsvp {
export var Promise: typeof foo;
}
export = rsvp;
}
+5 -3
View File
@@ -1,7 +1,9 @@
// Generated by typings
// Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/7de6c3dd94feaeb21f20054b9f30d5dabc5efabd/es6-shim/es6-shim.d.ts
// Type definitions for es6-shim v0.31.2
// Project: https://github.com/paulmillr/es6-shim
// Definitions by: Ron Buckton <http://github.com/rbuckton>
// Definitions: https://github.com/borisyankov/DefinitelyTyped
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
declare type PropertyKey = string | number | symbol;
@@ -621,7 +623,7 @@ interface WeakSetConstructor {
declare var WeakSet: WeakSetConstructor;
declare module Reflect {
declare namespace Reflect {
function apply(target: Function, thisArgument: any, argumentsList: ArrayLike<any>): any;
function construct(target: Function, argumentsList: ArrayLike<any>): any;
function defineProperty(target: any, propertyKey: PropertyKey, attributes: PropertyDescriptor): boolean;
@@ -649,7 +651,7 @@ declare module "es6-shim" {
var WeakMap: WeakMapConstructor;
var WeakSet: WeakSetConstructor;
var Promise: PromiseConstructor;
module Reflect {
namespace Reflect {
function apply(target: Function, thisArgument: any, argumentsList: ArrayLike<any>): any;
function construct(target: Function, argumentsList: ArrayLike<any>): any;
function defineProperty(target: any, propertyKey: PropertyKey, attributes: PropertyDescriptor): boolean;
+3 -3
View File
@@ -78,9 +78,9 @@
Grafana version: {{buildInfo.version}}, commit: {{buildInfo.commit}},
build date: {{buildInfo.buildstamp | date: 'yyyy-MM-dd HH:mm:ss' }}
</div>
<div class="version-footer text-center small" ng-show="buildInfo.hasUpdate">
<a class="external-link" target="_blank" href="http://grafana.org/download">New Grafana Version Available ({{buildInfo.latestVersion}})</a>
</div>
</div>
</div>
</div>
@@ -55,7 +55,7 @@ export default class InfluxDatasource {
query = query.replace(/\$interval/g, (target.interval || options.interval));
return query;
}).join("\n");
}).join(";");
// replace grafana variables
allQueries = allQueries.replace(/\$timeFilter/g, timeFilter);
@@ -107,7 +107,7 @@ export default class InfluxDatasource {
var timeFilter = this.getTimeFilter({rangeRaw: options.rangeRaw});
var query = options.annotation.query.replace('$timeFilter', timeFilter);
query = this.templateSrv.replace(query);
query = this.templateSrv.replace(query, null, 'regex');
return this._seriesQuery(query).then(data => {
if (!data || !data.results || !data.results[0]) {
@@ -133,6 +133,17 @@ export default class InfluxDatasource {
return this._influxRequest('GET', '/query', {q: query, epoch: 'ms'});
}
serializeParams(params) {
if (!params) { return '';}
return _.reduce(params, (memo, value, key) => {
if (value === null || value === undefined) { return memo; }
memo.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
return memo;
}, []).join("&");
}
testDatasource() {
return this.metricFindQuery('SHOW MEASUREMENTS LIMIT 1').then(() => {
return { status: "success", message: "Data source is working", title: "Success" };
@@ -166,6 +177,7 @@ export default class InfluxDatasource {
data: data,
precision: "ms",
inspect: { type: 'influxdb' },
paramSerializer: this.serializeParams,
};
options.headers = options.headers || {};
@@ -16,7 +16,7 @@ export class OpenTsConfigCtrl {
tsdbVersions = [
{name: '<=2.1', value: 1},
{name: '2.2', value: 2},
{name: '>=2.2', value: 2},
];
tsdbResolutions = [
+25 -33
View File
@@ -1,40 +1,32 @@
<div class="gf-form-group">
<div class="gf-form-inline">
<div class="gf-form">
<span class="gf-form-label width-10">Mode</span>
<div class="gf-form-select-wrapper max-width-10">
<select class="gf-form-input" ng-model="ctrl.panel.mode" ng-options="f for f in ctrl.modes" ng-change="ctrl.refresh()"></select>
</div>
</div>
<div class="gf-form" ng-show="ctrl.panel.mode === 'recently viewed'">
<span class="gf-form-label">
<i class="grafana-tip fa fa-question-circle ng-scope" bs-tooltip="'WARNING: This list will be cleared when clearing browser cache'" data-original-title="" title=""></i>
</span>
</div>
</div>
<div>
<div class="section gf-form-group">
<h5 class="section-heading">Options</h5>
<div class="gf-form-inline" ng-if="ctrl.panel.mode === 'search'">
<div class="gf-form">
<span class="gf-form-label width-10">Search options</span>
<span class="gf-form-label">Query</span>
<gf-form-switch class="gf-form" label="Starred" label-class="width-9" checked="ctrl.panel.starred" on-change="ctrl.refresh()"></gf-form-switch>
<gf-form-switch class="gf-form" label="Recently viewed" label-class="width-9" checked="ctrl.panel.recent" on-change="ctrl.refresh()"></gf-form-switch>
<gf-form-switch class="gf-form" label="Search" label-class="width-9" checked="ctrl.panel.search" on-change="ctrl.refresh()"></gf-form-switch>
<input type="text" class="gf-form-input" placeholder="title query"
ng-model="ctrl.panel.query" ng-change="ctrl.refresh()" ng-model-onblur>
<gf-form-switch class="gf-form" label="Show headings" label-class="width-9" checked="ctrl.panel.headings" on-change="ctrl.refresh()"></gf-form-switch>
</div>
<div class="gf-form">
<span class="gf-form-label width-9">Max items</span>
<input class="gf-form-input max-width-5" type="number" ng-model="ctrl.panel.limit" ng-model-onblur ng-change="ctrl.refresh()">
</div>
</div>
<div class="gf-form">
<span class="gf-form-label">Tags</span>
<div class="section gf-form-group">
<h5 class="section-heading">Search</h5>
<bootstrap-tagsinput ng-model="ctrl.panel.tags" tagclass="label label-tag" placeholder="add tags" on-tags-updated="ctrl.refresh()">
</bootstrap-tagsinput>
</div>
</div>
<div class="gf-form">
<span class="gf-form-label width-6">Query</span>
<input type="text" class="gf-form-input" placeholder="title query" ng-model="ctrl.panel.query" ng-change="ctrl.refresh()" ng-model-onblur>
</div>
<div class="gf-form">
<span class="gf-form-label width-6">Tags</span>
<bootstrap-tagsinput ng-model="ctrl.panel.tags" tagclass="label label-tag" placeholder="add tags" on-tags-updated="ctrl.refresh()">
</bootstrap-tagsinput>
</div>
</div>
<div class="gf-form-inline">
<div class="gf-form">
<span class="gf-form-label width-10">Limit number to</span>
<input class="gf-form-input" type="number" ng-model="ctrl.panel.limit" ng-model-onblur ng-change="ctrl.refresh()">
</div>
</div>
</div>
+16 -11
View File
@@ -1,12 +1,17 @@
<div class="dashlist">
<div class="dashlist-item" ng-repeat="dash in ctrl.dashList">
<a class="dashlist-link dashlist-link-{{dash.type}}" href="dashboard/{{dash.uri}}">
<span class="dashlist-title">
{{dash.title}}
</span>
<span class="dashlist-star">
<i class="fa" ng-class="{'fa-star': dash.isStarred, 'fa-star-o': dash.isStarred === false}"></i>
</span>
</a>
</div>
<div class="dashlist" ng-repeat="group in ctrl.groups">
<div class="dashlist-section" ng-if="group.show">
<h6 class="dashlist-section-header" ng-show="ctrl.panel.headings">
{{group.header}}
</h6>
<div class="dashlist-item" ng-repeat="dash in group.list">
<a class="dashlist-link dashlist-link-{{dash.type}}" href="dashboard/{{dash.uri}}">
<span class="dashlist-title">
{{dash.title}}
</span>
<span class="dashlist-star">
<i class="fa" ng-class="{'fa-star': dash.isStarred, 'fa-star-o': dash.isStarred === false}"></i>
</span>
</a>
</div>
</div>
</div>
+78 -24
View File
@@ -7,16 +7,19 @@ import {impressions} from 'app/features/dashboard/impression_store';
// Set and populate defaults
var panelDefaults = {
mode: 'starred',
query: '',
limit: 10,
tags: []
tags: [],
recent: false,
search: false,
starred: true,
headings: true,
};
class DashListCtrl extends PanelCtrl {
static templateUrl = 'module.html';
dashList: any[];
groups: any[];
modes: any[];
/** @ngInject */
@@ -31,6 +34,31 @@ class DashListCtrl extends PanelCtrl {
this.events.on('refresh', this.onRefresh.bind(this));
this.events.on('init-edit-mode', this.onInitEditMode.bind(this));
this.groups = [
{list: [], show: false, header: "Starred dashboards",},
{list: [], show: false, header: "Recently viewed dashboards"},
{list: [], show: false, header: "Search"},
];
// update capability
if (this.panel.mode) {
if (this.panel.mode === 'starred') {
this.panel.starred = true;
this.panel.headings = false;
}
if (this.panel.mode === 'recently viewed') {
this.panel.recent = true;
this.panel.starred = false;
this.panel.headings = false;
}
if (this.panel.mode === 'search') {
this.panel.search = true;
this.panel.starred = false;
this.panel.headings = false;
}
delete this.panel.mode;
}
}
onInitEditMode() {
@@ -40,34 +68,60 @@ class DashListCtrl extends PanelCtrl {
}
onRefresh() {
var params: any = {limit: this.panel.limit};
var promises = [];
if (this.panel.mode === 'recently viewed') {
var dashIds = _.first(impressions.getDashboardOpened(), this.panel.limit);
promises.push(this.getRecentDashboards());
promises.push(this.getStarred());
promises.push(this.getSearch());
return this.backendSrv.search({dashboardIds: dashIds, limit: this.panel.limit}).then(result => {
this.dashList = dashIds.map(orderId => {
return _.find(result, dashboard => {
return dashboard.id === orderId;
});
}).filter(el => {
return el !== undefined;
});
return Promise.all(promises)
.then(this.renderingCompleted.bind(this));
}
this.renderingCompleted();
});
getSearch() {
this.groups[2].show = this.panel.search;
if (!this.panel.search) {
return Promise.resolve();
}
if (this.panel.mode === 'starred') {
params.starred = "true";
} else {
params.query = this.panel.query;
params.tag = this.panel.tags;
}
var params = {
limit: this.panel.limit,
query: this.panel.query,
tag: this.panel.tags,
};
return this.backendSrv.search(params).then(result => {
this.dashList = result;
this.renderingCompleted();
this.groups[2].list = result;
});
}
getStarred() {
this.groups[0].show = this.panel.starred;
if (!this.panel.starred) {
return Promise.resolve();
}
var params = {limit: this.panel.limit, starred: "true"};
return this.backendSrv.search(params).then(result => {
this.groups[0].list = result;
});
}
getRecentDashboards() {
this.groups[1].show = this.panel.recent;
if (!this.panel.recent) {
return Promise.resolve();
}
var dashIds = _.first(impressions.getDashboardOpened(), this.panel.limit);
return this.backendSrv.search({dashboardIds: dashIds, limit: this.panel.limit}).then(result => {
this.groups[1].list = dashIds.map(orderId => {
return _.find(result, dashboard => {
return dashboard.id === orderId;
});
}).filter(el => {
return el !== undefined;
});
});
}
}
+4 -2
View File
@@ -151,8 +151,10 @@ function (angular, $, moment, _, kbn, GraphTooltip) {
}
function processOffsetHook(plot, gridMargin) {
if (panel.yaxis) { gridMargin.left = 20; }
if (panel.rightYAxisLabel) { gridMargin.right = 20; }
var left = panel.yaxes[0];
var right = panel.yaxes[1];
if (left.show && left.label) { gridMargin.left = 20; }
if (right.show && right.label) { gridMargin.right = 20; }
}
// Function for rendering panel
+12 -10
View File
@@ -9,7 +9,7 @@ function ($) {
var ctrl = scope.ctrl;
var panel = ctrl.panel;
var $tooltip = $('<div id="tooltip">');
var $tooltip = $('<div id="tooltip" class="graph-tooltip">');
this.findHoverIndexFromDataPoints = function(posX, series, last) {
var ps = series.datapoints.pointsize;
@@ -33,9 +33,8 @@ function ($) {
return j - 1;
};
this.showTooltip = function(absoluteTime, relativeTime, innerHtml, pos) {
var body = '<div class="graph-tooltip small"><div class="graph-tooltip-time">'+ absoluteTime +
' <span class="tone-down">(' + relativeTime + ')</span></div> ';
this.showTooltip = function(absoluteTime, innerHtml, pos) {
var body = '<div class="graph-tooltip-time">'+ absoluteTime + '</div>';
body += innerHtml + '</div>';
$tooltip.html(body).place_tt(pos.pageX + 20, pos.pageY);
};
@@ -109,7 +108,7 @@ function ($) {
var plot = elem.data().plot;
var plotData = plot.getData();
var seriesList = getSeriesFn();
var group, value, absoluteTime, relativeTime, hoverInfo, i, series, seriesHtml, tooltipFormat;
var group, value, absoluteTime, hoverInfo, i, series, seriesHtml, tooltipFormat;
if (panel.tooltip.msResolution) {
tooltipFormat = 'YYYY-MM-DD HH:mm:ss.SSS';
@@ -132,7 +131,6 @@ function ($) {
seriesHtml = '';
relativeTime = dashboard.getRelativeTime(seriesHoverInfo.time);
absoluteTime = dashboard.formatDate(seriesHoverInfo.time, tooltipFormat);
for (i = 0; i < seriesHoverInfo.length; i++) {
@@ -142,17 +140,22 @@ function ($) {
continue;
}
var highlightClass = '';
if (item && i === item.seriesIndex) {
highlightClass = 'graph-tooltip-list-item--highlight';
}
series = seriesList[i];
value = series.formatValue(hoverInfo.value);
seriesHtml += '<div class="graph-tooltip-list-item"><div class="graph-tooltip-series-name">';
seriesHtml += '<div class="graph-tooltip-list-item ' + highlightClass + '"><div class="graph-tooltip-series-name">';
seriesHtml += '<i class="fa fa-minus" style="color:' + series.color +';"></i> ' + series.label + ':</div>';
seriesHtml += '<div class="graph-tooltip-value">' + value + '</div></div>';
plot.highlight(i, hoverInfo.hoverIndex);
}
self.showTooltip(absoluteTime, relativeTime, seriesHtml, pos);
self.showTooltip(absoluteTime, seriesHtml, pos);
}
// single series tooltip
else if (item) {
@@ -169,12 +172,11 @@ function ($) {
value = series.formatValue(value);
relativeTime = dashboard.getRelativeTime(item.datapoint[0]);
absoluteTime = dashboard.formatDate(item.datapoint[0], tooltipFormat);
group += '<div class="graph-tooltip-value">' + value + '</div>';
self.showTooltip(absoluteTime, relativeTime, group, pos);
self.showTooltip(absoluteTime, group, pos);
}
// no hit
else {
+2 -2
View File
@@ -194,9 +194,9 @@ function (angular, _, $) {
}
var topPadding = 6;
$container.css("height", maxHeight - topPadding);
$container.css("max-height", maxHeight - topPadding);
} else {
$container.css("height", "");
$container.css("max-height", "");
}
}
}
+15 -3
View File
@@ -5,6 +5,7 @@ import './legend';
import './series_overrides_ctrl';
import template from './template';
import angular from 'angular';
import moment from 'moment';
import kbn from 'app/core/utils/kbn';
import _ from 'lodash';
@@ -108,13 +109,14 @@ class GraphCtrl extends MetricsPanelCtrl {
constructor($scope, $injector, private annotationsSrv) {
super($scope, $injector);
_.defaults(this.panel, panelDefaults);
_.defaults(this.panel, angular.copy(panelDefaults));
_.defaults(this.panel.tooltip, panelDefaults.tooltip);
_.defaults(this.panel.grid, panelDefaults.grid);
_.defaults(this.panel.legend, panelDefaults.legend);
this.colors = $scope.$root.colors;
this.events.on('render', this.onRender.bind(this));
this.events.on('data-received', this.onDataReceived.bind(this));
this.events.on('data-error', this.onDataError.bind(this));
this.events.on('data-snapshot-load', this.onDataSnapshotLoad.bind(this));
@@ -214,10 +216,21 @@ class GraphCtrl extends MetricsPanelCtrl {
this.panel.tooltip.msResolution = this.panel.tooltip.msResolution || series.isMsResolutionNeeded();
}
series.applySeriesOverrides(this.panel.seriesOverrides);
if (seriesData.unit) {
this.panel.yaxes[series.yaxis-1].format = seriesData.unit;
}
return series;
}
onRender() {
if (!this.seriesList) { return; }
for (let series of this.seriesList) {
series.applySeriesOverrides(this.panel.seriesOverrides);
}
}
changeSeriesColor(series, color) {
series.color = color;
this.panel.aliasColors[series.alias] = series.color;
@@ -234,7 +247,6 @@ class GraphCtrl extends MetricsPanelCtrl {
} else {
this.toggleSeriesExclusiveMode(serie);
}
this.render();
}
@@ -60,6 +60,9 @@ define([
template: '<gf-color-picker></gf-color-picker>',
model: {
colorSelected: $scope.colorSelected,
},
onClose: function() {
$scope.ctrl.render();
}
});
};
@@ -69,7 +69,7 @@
</gf-form-switch>
<gf-form-switch class="gf-form" ng-show="ctrl.panel.stack"
label="Percent" label-class="width-7"
checked="ctrl.panel.percent" on-change="ctrl.render()">
checked="ctrl.panel.percentage" on-change="ctrl.render()">
</gf-form-switch>
<div class="gf-form" ng-show="ctrl.panel.stack">
<label class="gf-form-label width-7">Tooltip value</label>
@@ -13,7 +13,6 @@
label="To the right" label-class="width-7"
checked="ctrl.panel.legend.rightSide" on-change="ctrl.render()">
</gf-form-switch>
<div ng-if="ctrl.panel.legend.rightSide" class="gf-form">
<label class="gf-form-label width-7">Width</label>
<input type="number" class="gf-form-input max-width-5" placeholder="250" bs-tooltip="'Set a min-width for the legend side table/block'" data-placement="right" ng-model="ctrl.panel.legend.sideWidth" ng-change="ctrl.render()" ng-model-onblur>
@@ -31,7 +30,7 @@
<gf-form-switch class="gf-form max-width-12"
label="Max" label-class="width-6" switch-class="max-width-5"
checked="ctrl.panel.legend.max" on-change="ctrl.legendCaluesOptionChanged()">
checked="ctrl.panel.legend.max" on-change="ctrl.legendValuesOptionChanged()">
</gf-form-switch>
</div>
@@ -0,0 +1,2 @@
# Plugin List Panel - Native Plugin
@@ -0,0 +1,40 @@
<div class="gf-form-group">
<div class="gf-form-inline">
<div class="gf-form">
<span class="gf-form-label width-10">Mode</span>
<div class="gf-form-select-wrapper max-width-10">
<select class="gf-form-input" ng-model="ctrl.panel.mode" ng-options="f for f in ctrl.modes" ng-change="ctrl.refresh()"></select>
</div>
</div>
<div class="gf-form" ng-show="ctrl.panel.mode === 'recently viewed'">
<span class="gf-form-label">
<i class="grafana-tip fa fa-question-circle ng-scope" bs-tooltip="'WARNING: This list will be cleared when clearing browser cache'" data-original-title="" title=""></i>
</span>
</div>
</div>
<div class="gf-form-inline" ng-if="ctrl.panel.mode === 'search'">
<div class="gf-form">
<span class="gf-form-label width-10">Search options</span>
<span class="gf-form-label">Query</span>
<input type="text" class="gf-form-input" placeholder="title query"
ng-model="ctrl.panel.query" ng-change="ctrl.refresh()" ng-model-onblur>
</div>
<div class="gf-form">
<span class="gf-form-label">Tags</span>
<bootstrap-tagsinput ng-model="ctrl.panel.tags" tagclass="label label-tag" placeholder="add tags" on-tags-updated="ctrl.refresh()">
</bootstrap-tagsinput>
</div>
</div>
<div class="gf-form-inline">
<div class="gf-form">
<span class="gf-form-label width-10">Limit number to</span>
<input class="gf-form-input" type="number" ng-model="ctrl.panel.limit" ng-model-onblur ng-change="ctrl.refresh()">
</div>
</div>
</div>
@@ -0,0 +1,119 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="100px" height="100px" viewBox="0 0 100 100" style="enable-background:new 0 0 100 100;" xml:space="preserve">
<g>
<g>
<path style="fill:#666666;" d="M8.842,11.219h0.1c1.228,0,2.227-0.999,2.227-2.227v-0.1L8.842,11.219z"/>
<path style="fill:#666666;" d="M0.008,2.113l2.054-2.054C0.966,0.139,0.089,1.016,0.008,2.113z"/>
<polygon style="fill:#666666;" points="0,2.998 0,5.533 5.484,0.05 2.948,0.05 "/>
<polygon style="fill:#666666;" points="6.361,0.05 0,6.411 0,8.946 8.896,0.05 "/>
<path style="fill:#666666;" d="M11.169,2.277c0-0.068-0.004-0.134-0.01-0.2l-9.132,9.132c0.066,0.006,0.133,0.01,0.2,0.01h2.325
l6.617-6.617V2.277z"/>
<path style="fill:#666666;" d="M9.654,0.169L0.119,9.704c0.201,0.592,0.643,1.073,1.211,1.324l9.649-9.649
C10.728,0.812,10.247,0.37,9.654,0.169z"/>
<polygon style="fill:#666666;" points="11.169,5.479 5.429,11.219 7.964,11.219 11.169,8.014 "/>
</g>
<path style="fill:#898989;" d="M88.146,11.031H14.866c-1.011,0-1.83-0.82-1.83-1.83v-7.37c0-1.011,0.82-1.831,1.83-1.831h73.281
c1.011,0,1.83,0.82,1.83,1.831v7.37C89.977,10.212,89.157,11.031,88.146,11.031z"/>
<g>
<path style="fill:#666666;" d="M8.842,23.902h0.1c1.228,0,2.227-0.999,2.227-2.227v-0.1L8.842,23.902z"/>
<path style="fill:#666666;" d="M0.008,14.796l2.054-2.054C0.966,12.822,0.089,13.699,0.008,14.796z"/>
<polygon style="fill:#666666;" points="0,15.681 0,18.216 5.484,12.733 2.948,12.733 "/>
<polygon style="fill:#666666;" points="6.361,12.733 0,19.094 0,21.629 8.896,12.733 "/>
<path style="fill:#666666;" d="M11.169,14.96c0-0.068-0.004-0.134-0.01-0.2l-9.132,9.132c0.066,0.006,0.133,0.01,0.2,0.01h2.325
l6.617-6.617V14.96z"/>
<path style="fill:#666666;" d="M9.654,12.852l-9.536,9.536c0.201,0.592,0.643,1.073,1.211,1.324l9.649-9.649
C10.728,13.495,10.247,13.053,9.654,12.852z"/>
<polygon style="fill:#666666;" points="11.169,18.162 5.429,23.902 7.964,23.902 11.169,20.697 "/>
</g>
<path style="fill:#898989;" d="M88.146,23.714H14.866c-1.011,0-1.83-0.82-1.83-1.83v-7.37c0-1.011,0.82-1.83,1.83-1.83h73.281
c1.011,0,1.83,0.82,1.83,1.83v7.37C89.977,22.895,89.157,23.714,88.146,23.714z"/>
<g>
<path style="fill:#666666;" d="M8.842,36.585h0.1c1.228,0,2.227-0.999,2.227-2.227v-0.1L8.842,36.585z"/>
<path style="fill:#666666;" d="M0.008,27.479l2.054-2.054C0.966,25.505,0.089,26.382,0.008,27.479z"/>
<polygon style="fill:#666666;" points="0,28.364 0,30.899 5.484,25.416 2.948,25.416 "/>
<polygon style="fill:#666666;" points="6.361,25.416 0,31.777 0,34.312 8.896,25.416 "/>
<path style="fill:#666666;" d="M11.169,27.643c0-0.068-0.004-0.134-0.01-0.2l-9.132,9.132c0.066,0.006,0.133,0.01,0.2,0.01h2.325
l6.617-6.617V27.643z"/>
<path style="fill:#666666;" d="M9.654,25.535L0.119,35.07c0.201,0.592,0.643,1.073,1.211,1.324l9.649-9.649
C10.728,26.178,10.247,25.736,9.654,25.535z"/>
<polygon style="fill:#666666;" points="11.169,30.845 5.429,36.585 7.964,36.585 11.169,33.38 "/>
</g>
<path style="fill:#898989;" d="M88.146,36.397H14.866c-1.011,0-1.83-0.82-1.83-1.831v-7.37c0-1.011,0.82-1.83,1.83-1.83h73.281
c1.011,0,1.83,0.82,1.83,1.83v7.37C89.977,35.578,89.157,36.397,88.146,36.397z"/>
<g>
<path style="fill:#666666;" d="M8.842,49.268h0.1c1.228,0,2.227-0.999,2.227-2.227v-0.1L8.842,49.268z"/>
<path style="fill:#666666;" d="M0.008,40.162l2.054-2.054C0.966,38.188,0.089,39.065,0.008,40.162z"/>
<polygon style="fill:#666666;" points="0,41.047 0,43.582 5.484,38.099 2.948,38.099 "/>
<polygon style="fill:#666666;" points="6.361,38.099 0,44.46 0,46.995 8.896,38.099 "/>
<path style="fill:#666666;" d="M11.169,40.326c0-0.068-0.004-0.134-0.01-0.2l-9.132,9.132c0.066,0.006,0.133,0.01,0.2,0.01h2.325
l6.617-6.617V40.326z"/>
<path style="fill:#666666;" d="M9.654,38.218l-9.536,9.536c0.201,0.592,0.643,1.073,1.211,1.324l9.649-9.649
C10.728,38.861,10.247,38.419,9.654,38.218z"/>
<polygon style="fill:#666666;" points="11.169,43.528 5.429,49.268 7.964,49.268 11.169,46.063 "/>
</g>
<path style="fill:#898989;" d="M88.146,49.08H14.866c-1.011,0-1.83-0.82-1.83-1.831v-7.37c0-1.011,0.82-1.831,1.83-1.831h73.281
c1.011,0,1.83,0.82,1.83,1.831v7.37C89.977,48.261,89.157,49.08,88.146,49.08z"/>
<g>
<path style="fill:#666666;" d="M8.842,61.951h0.1c1.228,0,2.227-0.999,2.227-2.227v-0.1L8.842,61.951z"/>
<path style="fill:#666666;" d="M0.008,52.845l2.054-2.054C0.966,50.871,0.089,51.748,0.008,52.845z"/>
<polygon style="fill:#666666;" points="0,53.73 0,56.265 5.484,50.782 2.948,50.782 "/>
<polygon style="fill:#666666;" points="6.361,50.782 0,57.143 0,59.678 8.896,50.782 "/>
<path style="fill:#666666;" d="M11.169,53.009c0-0.068-0.004-0.134-0.01-0.2l-9.132,9.132c0.066,0.006,0.133,0.01,0.2,0.01h2.325
l6.617-6.617V53.009z"/>
<path style="fill:#666666;" d="M9.654,50.901l-9.536,9.536c0.201,0.592,0.643,1.073,1.211,1.324l9.649-9.649
C10.728,51.544,10.247,51.102,9.654,50.901z"/>
<polygon style="fill:#666666;" points="11.169,56.211 5.429,61.951 7.964,61.951 11.169,58.746 "/>
</g>
<path style="fill:#898989;" d="M88.146,61.763H14.866c-1.011,0-1.83-0.82-1.83-1.83v-7.37c0-1.011,0.82-1.831,1.83-1.831h73.281
c1.011,0,1.83,0.82,1.83,1.831v7.37C89.977,60.944,89.157,61.763,88.146,61.763z"/>
<g>
<path style="fill:#666666;" d="M8.842,74.634h0.1c1.228,0,2.227-0.999,2.227-2.227v-0.1L8.842,74.634z"/>
<path style="fill:#666666;" d="M0.008,65.528l2.054-2.054C0.966,63.554,0.089,64.431,0.008,65.528z"/>
<polygon style="fill:#666666;" points="0,66.413 0,68.948 5.484,63.465 2.948,63.465 "/>
<polygon style="fill:#666666;" points="6.361,63.465 0,69.826 0,72.361 8.896,63.465 "/>
<path style="fill:#666666;" d="M11.169,65.692c0-0.068-0.004-0.134-0.01-0.2l-9.132,9.132c0.066,0.006,0.133,0.01,0.2,0.01h2.325
l6.617-6.617V65.692z"/>
<path style="fill:#666666;" d="M9.654,63.584l-9.536,9.536c0.201,0.592,0.643,1.073,1.211,1.324l9.649-9.649
C10.728,64.227,10.247,63.785,9.654,63.584z"/>
<polygon style="fill:#666666;" points="11.169,68.894 5.429,74.634 7.964,74.634 11.169,71.429 "/>
</g>
<path style="fill:#898989;" d="M88.146,74.446H14.866c-1.011,0-1.83-0.82-1.83-1.83v-7.37c0-1.011,0.82-1.831,1.83-1.831h73.281
c1.011,0,1.83,0.82,1.83,1.831v7.37C89.977,73.627,89.157,74.446,88.146,74.446z"/>
<g>
<path style="fill:#666666;" d="M8.842,87.317h0.1c1.228,0,2.227-0.999,2.227-2.227v-0.1L8.842,87.317z"/>
<path style="fill:#666666;" d="M0.008,78.211l2.054-2.054C0.966,76.237,0.089,77.114,0.008,78.211z"/>
<polygon style="fill:#666666;" points="0,79.096 0,81.631 5.484,76.148 2.948,76.148 "/>
<polygon style="fill:#666666;" points="6.361,76.148 0,82.509 0,85.044 8.896,76.148 "/>
<path style="fill:#666666;" d="M11.169,78.375c0-0.068-0.004-0.134-0.01-0.2l-9.132,9.132c0.066,0.006,0.133,0.01,0.2,0.01h2.325
l6.617-6.617V78.375z"/>
<path style="fill:#666666;" d="M9.654,76.267l-9.536,9.536c0.201,0.592,0.643,1.073,1.211,1.324l9.649-9.649
C10.728,76.91,10.247,76.468,9.654,76.267z"/>
<polygon style="fill:#666666;" points="11.169,81.577 5.429,87.317 7.964,87.317 11.169,84.112 "/>
</g>
<path style="fill:#898989;" d="M88.146,87.129H14.866c-1.011,0-1.83-0.82-1.83-1.83v-7.37c0-1.011,0.82-1.831,1.83-1.831h73.281
c1.011,0,1.83,0.82,1.83,1.831v7.37C89.977,86.31,89.157,87.129,88.146,87.129z"/>
<g>
<path style="fill:#666666;" d="M8.842,100h0.1c1.228,0,2.227-0.999,2.227-2.227v-0.1L8.842,100z"/>
<path style="fill:#666666;" d="M0.008,90.894l2.054-2.054C0.966,88.92,0.089,89.797,0.008,90.894z"/>
<polygon style="fill:#666666;" points="0,91.779 0,94.314 5.484,88.831 2.948,88.831 "/>
<polygon style="fill:#666666;" points="6.361,88.831 0,95.192 0,97.727 8.896,88.831 "/>
<path style="fill:#666666;" d="M11.169,91.058c0-0.068-0.004-0.134-0.01-0.2L2.027,99.99c0.066,0.006,0.133,0.01,0.2,0.01h2.325
l6.617-6.617V91.058z"/>
<path style="fill:#666666;" d="M9.654,88.95l-9.536,9.536c0.201,0.592,0.643,1.073,1.211,1.324l9.649-9.649
C10.728,89.593,10.247,89.151,9.654,88.95z"/>
<polygon style="fill:#666666;" points="11.169,94.26 5.429,100 7.964,100 11.169,96.795 "/>
</g>
<path style="fill:#898989;" d="M88.146,99.812H14.866c-1.011,0-1.83-0.82-1.83-1.83v-7.37c0-1.011,0.82-1.83,1.83-1.83h73.281
c1.011,0,1.83,0.82,1.83,1.83v7.37C89.977,98.993,89.157,99.812,88.146,99.812z"/>
<circle style="fill:#F7941E;" cx="96.125" cy="5.637" r="3.875"/>
<circle style="fill:#898989;" cx="96.125" cy="18.37" r="3.875"/>
<circle style="fill:#898989;" cx="96.125" cy="31.104" r="3.875"/>
<circle style="fill:#F7941E;" cx="96.125" cy="43.837" r="3.875"/>
<circle style="fill:#F7941E;" cx="96.125" cy="56.57" r="3.875"/>
<circle style="fill:#898989;" cx="96.125" cy="69.304" r="3.875"/>
<circle style="fill:#F7941E;" cx="96.125" cy="82.037" r="3.875"/>
<circle style="fill:#898989;" cx="96.125" cy="94.77" r="3.875"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.8 KiB

@@ -0,0 +1,30 @@
<div class="pluginlist">
<div class="pluginlist-section" ng-repeat="category in ctrl.viewModel">
<h6 class="pluginlist-section-header">
{{category.header}}
</h6>
<div class="pluginlist-item" ng-repeat="plugin in category.list">
<div class="pluginlist-link pluginlist-link-{{plugin.state}} pointer" ng-click="ctrl.gotoPlugin(plugin)">
<a href="plugins/{{plugin.id}}/edit">
<img ng-src="{{plugin.info.logos.small}}" class="pluginlist-image">
<span class="pluginlist-title">{{plugin.name}}</span>
<span class="pluginlist-version">v{{plugin.info.version}}</span>
</a>
<a class="pluginlist-message pluginlist-message--update" ng-show="plugin.hasUpdate" ng-click="ctrl.updateAvailable(plugin, $event)" bs-tooltip="plugin.latestVersion">
Update available!
</a>
<span class="pluginlist-message pluginlist-message--enable" ng-show="!plugin.enabled && !plugin.hasUpdate">
Enable now
</span>
<span class="pluginlist-message pluginlist-message--no-update" ng-show="plugin.enabled && !plugin.hasUpdate">
Up to date
</span>
</div>
</div>
<div class="pluginlist-item" ng-show="category.list.length === 0">
<a class="pluginlist-link pluginlist-link-{{plugin.state}}" href="http://grafana/net/plugins/">
<span class="pluginlist-none-installed">No additional panels installed. <span class="pluginlist-emphasis">Browse Grafana.net</span></span>
</a>
</div>
</div>
</div>
@@ -0,0 +1,72 @@
///<reference path="../../../headers/common.d.ts" />
import _ from 'lodash';
import config from 'app/core/config';
import {PanelCtrl} from '../../../features/panel/panel_ctrl';
// Set and populate defaults
var panelDefaults = {
};
class PluginListCtrl extends PanelCtrl {
static templateUrl = 'module.html';
pluginList: any[];
viewModel: any;
/** @ngInject */
constructor($scope, $injector, private backendSrv, private $location) {
super($scope, $injector);
_.defaults(this.panel, panelDefaults);
this.events.on('init-edit-mode', this.onInitEditMode.bind(this));
this.pluginList = [];
this.viewModel = [
{header: "Installed Apps", list: [], type: 'app'},
{header: "Installed Panels", list: [], type: 'panel'},
{header: "Installed Datasources", list: [], type: 'datasource'},
];
this.update();
}
onInitEditMode() {
this.editorTabIndex = 1;
this.addEditorTab('Options', 'public/app/plugins/panel/pluginlist/editor.html');
}
gotoPlugin(plugin) {
this.$location.path(`plugins/${plugin.id}/edit`);
}
updateAvailable(plugin, $event) {
$event.stopPropagation();
var modalScope = this.$scope.$new(true);
modalScope.plugin = plugin;
this.publishAppEvent('show-modal', {
src: 'public/app/features/plugins/partials/update_instructions.html',
scope: modalScope
});
}
update() {
this.backendSrv.get('api/plugins', {embedded: 0, core: 0}).then(plugins => {
this.pluginList = plugins;
this.viewModel[0].list = _.filter(plugins, {type: 'app'});
this.viewModel[1].list = _.filter(plugins, {type: 'panel'});
this.viewModel[2].list = _.filter(plugins, {type: 'datasource'});
for (let plugin of this.pluginList) {
if (plugin.hasUpdate) {
plugin.state = 'has-update';
} else if (!plugin.enabled) {
plugin.state = 'not-enabled';
}
}
});
}
}
export {PluginListCtrl, PluginListCtrl as PanelCtrl}
@@ -0,0 +1,16 @@
{
"type": "panel",
"name": "Plugin list",
"id": "pluginlist",
"info": {
"author": {
"name": "Grafana Project",
"url": "http://grafana.org"
},
"logos": {
"small": "img/icn-dashlist-panel.svg",
"large": "img/icn-dashlist-panel.svg"
}
}
}
+3 -2
View File
@@ -77,9 +77,9 @@ class TablePanelCtrl extends MetricsPanelCtrl {
this.pageIndex = 0;
if (this.panel.transform === 'annotations') {
this.setTimeQueryStart();
return this.annotationsSrv.getAnnotations(this.dashboard).then(annotations => {
this.dataRaw = annotations;
this.render();
return {data: annotations};
});
}
@@ -218,6 +218,7 @@ class TablePanelCtrl extends MetricsPanelCtrl {
if (data) {
renderPanel();
}
ctrl.renderingCompleted();
});
}
}
+5
View File
@@ -4,6 +4,7 @@ System.config({
paths: {
'remarkable': 'vendor/npm/remarkable/dist/remarkable.js',
'tether': 'vendor/npm/tether/dist/js/tether.js',
'eventemitter3': 'vendor/npm/eventemitter3/index.js',
'tether-drop': 'vendor/npm/tether-drop/dist/js/drop.js',
'moment': 'vendor/moment.js',
"jquery": "vendor/jquery/dist/jquery.js",
@@ -55,5 +56,9 @@ System.config({
deps: ['jquery'],
exports: 'angular',
},
'vendor/npm/eventemitter3/index.js': {
format: 'cjs',
exports: 'EventEmitter'
},
}
});
+30 -24
View File
@@ -9,55 +9,61 @@
"hideControls": true,
"sharedCrosshair": false,
"rows": [
{
{
"collapse": false,
"editable": true,
"height": "90px",
"height": "25px",
"panels": [
{
"content": "<div class=\"text-center dashboard-header\">\n <span>Home Dashboard</span>\n</div>",
"editable": true,
"id": 1,
"links": [],
"mode": "html",
"span": 12,
"style": {},
"title": "",
"transparent": true,
"type": "text",
"links": []
"type": "text"
}
],
"title": "New row"
},
{
},
{
"collapse": false,
"editable": true,
"height": "510px",
"panels": [
{
"id": 2,
"limit": 10,
"mode": "starred",
"id": 3,
"limit": 4,
"links": [],
"query": "",
"span": 6,
"span": 7,
"tags": [],
"title": "Starred dashboards",
"type": "dashlist"
"title": "",
"transparent": false,
"type": "dashlist",
"recent": true,
"search": false,
"starred": true,
"headings": true
},
{
"id": 3,
"limit": 10,
"mode": "recently viewed",
"query": "",
"span": 6,
"tags": [],
"title": "Recently viewed dashboards",
"type": "dashlist"
"editable": true,
"error": false,
"id": 4,
"isNew": true,
"links": [],
"span": 5,
"title": "",
"transparent": false,
"type": "pluginlist"
}
],
"title": "Row"
}
],
}
],
"time": {
"from": "now-6h",
"to": "now"
@@ -95,7 +101,7 @@
"annotations": {
"list": []
},
"schemaVersion": 9,
"version": 5,
"schemaVersion": 12,
"version": 2,
"links": []
}
Regular → Executable
BIN
View File
Binary file not shown.
Regular → Executable
+2 -2
View File
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 114 KiB

Regular → Executable
BIN
View File
Binary file not shown.
Regular → Executable
BIN
View File
Binary file not shown.
+1
View File
@@ -42,6 +42,7 @@
@import "components/panel_graph";
@import "components/submenu";
@import "components/panel_dashlist";
@import "components/panel_pluginlist";
@import "components/panel_singlestat";
@import "components/panel_table";
@import "components/panel_text";
+12 -10
View File
@@ -39,6 +39,7 @@ $brand-primary: $orange;
$brand-success: $green;
$brand-warning: $brand-primary;
$brand-danger: $red;
$brand-text-highlight: #f7941d;
// Status colors
// -------------------------
@@ -69,6 +70,7 @@ $page-gradient: linear-gradient(60deg, transparent 70%, darken($page-bg, 4%) 98%
$link-color: darken($white,11%);
$link-color-disabled: darken($link-color,30%);
$link-hover-color: $white;
$external-link-color: $blue;
// Typography
// -------------------------
@@ -240,14 +242,6 @@ $successBackground: $btn-success-bg;
$infoText: $blue-dark;
$infoBackground: $blue-dark;
// Tooltips and popovers
// -------------------------
$tooltipColor: $white;
$tooltipBackground: rgb(58, 57, 57);
$tooltipArrowWidth: 5px;
$tooltipArrowColor: $tooltipBackground;
$tooltipLinkColor: $link-color;
// popover
$popover-bg: $dark-4;
$popover-color: $text-color;
@@ -255,6 +249,16 @@ $popover-color: $text-color;
$popover-help-bg: $btn-secondary-bg;
$popover-help-color: $text-color;
// Tooltips and popovers
// -------------------------
$tooltipColor: $text-color;
$tooltipBackground: $dark-5;
$tooltipArrowWidth: 5px;
$tooltipArrowColor: $tooltipBackground;
$tooltipLinkColor: $link-color;
$graph-tooltip-bg: $dark-5;
// images
$checkboxImageUrl: '../img/checkbox.png';
@@ -262,5 +266,3 @@ $checkboxImageUrl: '../img/checkbox.png';
$card-background: linear-gradient(135deg, #2f2f2f, #262626);
$card-background-hover: linear-gradient(135deg, #343434, #262626);
$card-shadow: -1px -1px 0 0 hsla(0, 0%, 100%, .1), 1px 1px 0 0 rgba(0, 0, 0, .3);
+10 -9
View File
@@ -44,6 +44,7 @@ $brand-primary: $orange;
$brand-success: $green;
$brand-warning: $orange;
$brand-danger: $red;
$brand-text-highlight: #f7941d;
// Status colors
// -------------------------
@@ -75,6 +76,7 @@ $page-gradient: linear-gradient(60deg, transparent 70%, darken($page-bg, 4%) 98%
$link-color: $gray-1;
$link-color-disabled: lighten($link-color, 30%);
$link-hover-color: darken($link-color, 20%);
$external-link-color: $blue;
// Typography
// -------------------------
@@ -266,6 +268,12 @@ $infoText: $blue;
$infoBackground: $blue-dark;
$infoBorder: transparent;
// popover
$popover-bg: $gray-5;
$popover-color: $text-color;
$popover-help-bg: $blue-dark;
$popover-help-color: $gray-6;
// Tooltips and popovers
// -------------------------
@@ -273,14 +281,8 @@ $tooltipColor: $text-color;
$tooltipBackground: $gray-5;
$tooltipArrowWidth: 5px;
$tooltipArrowColor: $tooltipBackground;
$tooltipLinkColor: $text-color;
// popover
$popover-bg: $gray-5;
$popover-color: $text-color;
$popover-help-bg: $blue-dark;
$popover-help-color: $gray-6;
$tooltipLinkColor: $link-color;
$graph-tooltip-bg: $gray-5;
// images
$checkboxImageUrl: '../img/checkbox_white.png';
@@ -289,4 +291,3 @@ $checkboxImageUrl: '../img/checkbox_white.png';
$card-background: linear-gradient(135deg, $gray-5, $gray-6);
$card-background-hover: linear-gradient(135deg, $gray-6, $gray-7);
$card-shadow: -1px -1px 0 0 hsla(0, 0%, 100%, .1), 1px 1px 0 0 rgba(0, 0, 0, .1);
+10 -1
View File
@@ -23,6 +23,16 @@ code {
white-space: nowrap;
}
code.code--small {
font-size: $font-size-xs;
padding: 5px;
margin: 0 2px;
}
p.code--line {
line-height: 1.8;
}
// Blocks of code
pre {
display: block;
@@ -49,4 +59,3 @@ pre {
border: 0;
}
}
+1 -1
View File
@@ -169,7 +169,7 @@
.icon-gf-scale:before {
content: "\e906";
}
.icon-gf-litmus:before {
.icon-gf-worldping:before {
content: "\e627";
}
.icon-gf-grafana_wordmark:before {
+1 -1
View File
@@ -114,7 +114,7 @@ hr {
small,
.small {
font-size: 85%;
font-size: $font-size-sm;
font-weight: normal;
}
+36 -3
View File
@@ -76,13 +76,20 @@
}
.card-item-header {
margin-bottom: $spacer;
}
.card-item-type {
color: $text-color-weak;
text-transform: uppercase;
margin-bottom: $spacer;
font-size: $font-size-sm;
font-weight: bold;
}
.card-item-notice {
font-size: $font-size-sm;
}
.card-item-name {
color: $headings-color;
overflow: hidden;
@@ -97,8 +104,26 @@
width: 100%;
}
.card-item-sub-name--header {
color: $text-color-weak;
text-transform: uppercase;
margin-bottom: $spacer;
font-size: $font-size-sm;
font-weight: bold;
}
.card-list-layout-grid {
.card-item-type {
display: inline-block;
}
.card-item-notice {
font-size: $font-size-sm;
display: inline-block;
margin-left: $spacer;
}
.card-item-header-action {
float: right;
}
@@ -108,6 +133,10 @@
padding: 0 1.5rem 1.5rem 0rem;
}
.card-item-wrapper--clickable {
cursor: pointer;
}
.card-item-figure {
margin: 0 $spacer $spacer 0;
height: 6rem;
@@ -138,7 +167,7 @@
.card-item-wrapper {
width: 50%;
}
}
}
}
}
@@ -149,6 +178,10 @@
width: 100%;
}
.card-item-wrapper--clickable {
cursor: pointer;
}
.card-item {
border-bottom: .2rem solid $page-bg;
border-radius: 0;
@@ -157,6 +190,7 @@
.card-item-header {
float: right;
text-align: right;
}
.card-item-figure {
@@ -178,4 +212,3 @@
margin-right: 0;
}
}
+1 -5
View File
@@ -127,6 +127,7 @@
.share-modal-options {
margin: 11px 20px 33px 20px;
display: inline-block;
}
.share-modal-big-icon {
@@ -162,8 +163,3 @@
}
}
.modal-body {
.position-center {
display: inline-block;
}
}
+6 -1
View File
@@ -1,5 +1,10 @@
.dashlist-item {
.dashlist-section-header {
margin-bottom: $spacer;
color: $text-color-weak;
}
.dashlist-section {
margin-bottom: $spacer;
}
.dashlist-link {
+10 -6
View File
@@ -234,30 +234,34 @@
.graph-tooltip {
white-space: nowrap;
font-size: $font-size-sm;
background-color: $graph-tooltip-bg;
.graph-tooltip-time {
text-align: center;
font-weight: $font-weight-semi-bold;
position: relative;
top: -3px;
}
.tone-down {
opacity: 0.7;
padding: 0.2rem;
}
.graph-tooltip-list-item {
display: table-row;
&--highlight {
color: $link-color;
font-weight: bold;
}
}
.graph-tooltip-series-name {
display: table-cell;
padding: 0.15rem;
}
.graph-tooltip-value {
display: table-cell;
font-weight: bold;
padding-left: 10px;
padding-left: 15px;
text-align: right;
}
}
@@ -0,0 +1,75 @@
.pluginlist-section-header {
margin-bottom: $spacer;
color: $text-color-weak;
}
.pluginlist-section {
margin-bottom: $spacer;
}
.pluginlist-link {
display: block;
margin: 5px;
padding: 7px;
background-color: $tight-form-bg;
&:hover {
background-color: $tight-form-func-bg;
}
}
.pluginlist-icon {
vertical-align: sub;
font-size: $font-size-h1;
margin-right: $spacer / 2;
}
.pluginlist-image {
width: 20px;
}
.pluginlist-title {
margin-right: $spacer / 3;
}
.pluginlist-version {
font-size: $font-size-sm;
color: $text-color-weak;
}
.pluginlist-message {
float: right;
font-size: $font-size-sm;
}
.pluginlist-message--update {
&:hover {
border-bottom: 1px solid $text-color;
}
}
.pluginlist-message--enable{
color: $external-link-color;
&:hover {
border-bottom: 1px solid $external-link-color;
}
}
.pluginlist-message--no-update {
color: $text-color-weak;
}
.pluginlist-emphasis {
font-weight: 600;
}
.pluginlist-none-installed {
color: $text-color-weak;
font-size: $font-size-sm;
}
.pluginlist-inline-logo {
vertical-align: sub;
margin-right: $spacer / 3;
width: 16px;
}
+1 -8
View File
@@ -2,7 +2,6 @@
// Tooltips
// --------------------------------------------------
// Base class
.tooltip {
position: absolute;
@@ -37,6 +36,7 @@
border-color: transparent;
border-style: solid;
}
.tooltip {
&.top .tooltip-arrow {
bottom: 0;
@@ -88,13 +88,6 @@
}
}
.grafana-tooltip hr {
padding: 2px;
color: #c8c8c8;
margin: 0px;
border-bottom: 0px solid #c8c8c8;
}
.grafana-tip {
padding-left: 5px;
}
+2 -2
View File
@@ -278,11 +278,11 @@ div.flot-text {
.dashboard-header {
font-family: $headings-font-family;
font-size: $font-size-h2;
font-size: $font-size-h3;
text-align: center;
span {
display: inline-block;
@include brand-bottom-border();
padding: 1.2rem .5rem .4rem .5rem;
padding: 0.5rem .5rem .2rem .5rem;
}
}
+1
View File
@@ -52,6 +52,7 @@ button.close {
.pull-right {
float: right !important;
}
.pull-left {
float: left !important;
}

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