Compare commits

..

194 Commits

Author SHA1 Message Date
Torkel Ödegaard 4832ecf3e7 docs(): updated download links 2016-04-15 09:25:09 -04:00
Torkel Ödegaard 2e8ed97355 changelog(grafana-cli): fixed issue with grafana-cli and plugins dir, fixes #4723 2016-04-15 09:13:06 -04:00
Torkel Ödegaard d98839fc19 Revert "feat(cli): detects plugin folder for dev env"
This reverts commit a5eda6a87b.
2016-04-15 09:01:27 -04:00
Torkel Ödegaard 75555c470b Update latest.json 2016-04-14 18:09:04 -04:00
Torkel Ödegaard b3f3b70b90 Merge branch 'master' of github.com:grafana/grafana 2016-04-14 14:15:59 -04:00
Torkel Ödegaard 924800b7df bumped version 2016-04-14 14:15:47 -04:00
Matt Toback d27da57b02 Fixed the fix. 2016-04-14 12:39:37 -04:00
Torkel Ödegaard f1a01aed7e Merge pull request #4711 from grafana/issue-4704
Updated colors to fix issue
2016-04-14 12:30:47 -04:00
Matt Toback c1a5771fcd Updated colors to fix issue 2016-04-14 11:38:07 -04:00
Matt Toback 8ac5c36735 Still not fixed, not sure why.
despite having pushed that code with @torkelo.
2016-04-14 11:12:00 -04:00
Torkel Ödegaard 6355896584 fix(snapshots): Fixed issue with empty snapshots, fixes #4706 2016-04-14 11:06:48 -04:00
Torkel Ödegaard 6216ce455f fix(pluginlist): fixed issue with pluginlist link, fixes #4705 2016-04-14 10:55:24 -04:00
Torkel Ödegaard c0832995ae Merge branch 'master' of github.com:grafana/grafana 2016-04-14 08:47:38 -04:00
Torkel Ödegaard 9469b20cbd fix(graph): Fixed issue with light theme text color issue in tooltip, fixes #4702 2016-04-14 08:41:54 -04:00
Torkel Ödegaard 573457c03f fix(build): fixed build script issue 2016-04-13 17:48:20 -04:00
Torkel Ödegaard 5c395424c6 updated changelog 2016-04-13 17:42:33 -04:00
Torkel Ödegaard f6d53df282 Merge branch 'master' of github.com:grafana/grafana 2016-04-13 17:33:17 -04:00
Torkel Ödegaard e4374b1d56 docs(): updated download links 2016-04-13 17:33:03 -04:00
Torkel Ödegaard 7302ddaba5 docs(): fixed docs fixes #46688 2016-04-13 16:43:00 -04:00
Torkel Ödegaard 7456514816 fix(templating): fixed issue with template variables that use regex extraction, fixes #4672 2016-04-13 16:41:19 -04:00
Torkel Ödegaard d9ad4cf2fc fix(): made plugin settings api call accessable for viewer roles 2016-04-13 13:03:41 -04:00
Torkel Ödegaard 8b4efbed06 Merge branch 'master' of github.com:grafana/grafana 2016-04-13 12:23:45 -04:00
Torkel Ödegaard 10131aa8a0 fix(pluginlist): fixed issue with home dashboard and new pluginlist panel that casued permission denied error for non admin users, fixes #4686 2016-04-13 12:23:29 -04:00
Torkel Ödegaard 23dc85c1de Merge pull request #4628 from timstanley1985/master
Fix build on RPi.  Building from source timesout on karma tests so increase timeouts
2016-04-13 10:32:46 -04:00
Torkel Ödegaard 338c6b5a5c Merge pull request #4671 from grafana/login-page-style
Updated background color on div
2016-04-13 10:28:40 -04:00
Matt Toback c13ebce2f6 Update module.html 2016-04-12 17:01:30 -04:00
Matt Toback b6ccd7ffbe Updated background color on div 2016-04-12 15:18:47 -04:00
Torkel Ödegaard ad0794cc59 ux(): updated light theme 2016-04-12 14:26:55 -04:00
Torkel Ödegaard 6961cb7440 ux(): reduced tooltip border radius 2016-04-12 13:29:31 -04:00
Torkel Ödegaard 9868edb138 ux(): changed tooltip style 2016-04-12 13:00:33 -04:00
Torkel Ödegaard e822fad505 fix(timepicker): fixed issues and added some polish to timepicker shift back/forward buttons 2016-04-12 12:41:17 -04:00
Torkel Ödegaard 3ab5427019 fix(timepicker): fixed issues and added some polish to timepicker shift back/forward buttons 2016-04-12 12:39:59 -04:00
Torkel Ödegaard 7afb6fa3e3 ux(ds edit): combine save and test connection buttons, closes #4658 2016-04-12 11:20:03 -04:00
Torkel Ödegaard 29dff7ba1a fix(graph): fixed color picker appearingunder pinned side nav, fixes #4556 2016-04-12 11:07:34 -04:00
Torkel Ödegaard 329ee8d9f1 fix(prometheus): added template variable escape format to prometheus annotations queries, fixes #4656 2016-04-12 10:50:58 -04:00
Torkel Ödegaard f70e0003f8 Merge branch 'master' of github.com:grafana/grafana 2016-04-12 10:29:07 -04:00
Torkel Ödegaard bb0eb29c82 Update latest.json 2016-04-12 10:28:56 -04:00
Torkel Ödegaard 47bba71d7f fixed update check 2016-04-12 10:28:32 -04:00
bergquist 6b8921c8c5 Merge branch 'utkarshcmu-move' 2016-04-12 16:22:27 +02:00
bergquist 7d09579e3f feat(timepicker): adds arrows to move back and forth in current dashboard
closes #119
2016-04-12 16:21:29 +02:00
bergquist e5c9a24c33 Merge branch 'master' into utkarshcmu-move 2016-04-12 16:21:20 +02:00
Torkel Ödegaard e7626befc5 updated version 2016-04-12 10:17:39 -04:00
Torkel Ödegaard 905d31442c fix(annotations): make sure a data source that supports annotations is used in annotation editor when creating new 2016-04-12 10:16:40 -04:00
Torkel Ödegaard 917fabbc9b docs(): updated links again 2016-04-12 09:52:12 -04:00
bergquist 963001ba1a Merge branch 'move' of https://github.com/utkarshcmu/grafana into utkarshcmu-move 2016-04-12 15:22:38 +02: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
timstanley1985 92f5f8dced Fix build on RPi. Building from source timesout on karma tests so increase timeouts 2016-04-09 17:09:55 +01: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
utkarshcmu 6a55ec6955 Changed time shift icons 2016-04-07 02:21:54 -07:00
utkarshcmu e470786fb9 Backward and forward arrows for time shift 2016-04-07 01:30:49 -07: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
Torkel Ödegaard 43ebc172cb fix(profile): fixed change password link and page, fixes #4542 2016-04-04 13:52:41 -04:00
Torkel Ödegaard 892e79a982 fix(table): another fix for table panel sorting, fixes #4544 2016-04-04 13:43:13 -04:00
Torkel Ödegaard 542e080f0a fix(graph): fixed issue with graph legend when in table mode and series on righ-y axis, also fixed so that you can see what series are on right-y axis when legend is in table mode, fixes #4551, fixes #1145 2016-04-04 13:13:25 -04:00
Torkel Ödegaard f35eb0f148 fix(metrics tab): Fix for missing datasource name in datasource selector, fixes #4540 2016-04-03 16:19:14 -04:00
Torkel Ödegaard c764e7fd97 fix(elasticsearch): add a better default terms size limit of 10, fixes #4013 2016-04-03 16:11:42 -04:00
Torkel Ödegaard 1c97f10d8a fix(table panel): fixed column sort issue with table panel, #4532 2016-04-03 16:01:48 -04:00
Torkel Ödegaard d8499e6941 fix(influxdb): added regex start string char to templating suggestion, fixes #4405 2016-04-03 15:51:54 -04:00
Torkel Ödegaard 105a678d64 fix(): another test fix 2016-04-03 10:28:31 -04:00
Torkel Ödegaard a12de09533 changelog(): updated with preferences features 2016-04-03 10:21:11 -04:00
Torkel Ödegaard 431a610f00 fix(): fixed failing unit test 2016-04-03 10:12:43 -04:00
Torkel Ödegaard 6e6ebc5947 trying to fix karma issue 2016-04-03 09:43:33 -04:00
Torkel Ödegaard b30b78e442 feat(preferences): got timezone option to work on org and profile level, as well as dashboard 2016-04-03 06:05:43 -07:00
Torkel Ödegaard cab859a0e4 Merge branch 'master' into preferences 2016-04-03 05:33:11 -07:00
Torkel Ödegaard a2c6469d41 fix(): A Datasource with null jsonData would make Grafana fail to load page, fixes #4536 2016-04-03 05:32:15 -07:00
Torkel Ödegaard 55d95f9009 feat(prefs): moved context srv to typescript 2016-04-03 05:27:35 -07:00
Torkel Ödegaard ab1048b7ee feat(preferences): theme and home dashbord settings now work work on profile and org settings page 2016-04-02 13:54:06 -07:00
Torkel Ödegaard 38a10f8be4 progress 2016-04-01 17:34:30 -07:00
Torkel Ödegaard f165ba6480 Merge branch 'master' into preferences 2016-04-01 16:02:02 -07:00
Torkel Ödegaard bcc875a39c Merge branch 'master' of github.com:grafana/grafana 2016-04-01 10:57:17 -07:00
Torkel Ödegaard 2f2029a5c1 fix(dashboard): fixed dashboard layout for mobile devices, fixes #4529 2016-04-01 10:56:56 -07:00
Torkel Ödegaard 8dd509c31b Merge pull request #4491 from grafana/code-snippets
Code snippets
2016-04-01 10:45:10 -07:00
Torkel Ödegaard 171f6422b1 docs(): corrected version 2016-03-31 07:05:44 -07:00
Torkel Ödegaard 5e6c3f314c build(): trying to fix windows build on appveyor 2016-03-31 06:47:50 -07:00
Torkel Ödegaard efc97d177a Merge branch 'master' of github.com:grafana/grafana 2016-03-31 06:24:04 -07:00
Torkel Ödegaard 601e90f5fe docs(): updated install instructions 2016-03-31 06:23:52 -07:00
Torkel Ödegaard decede394a Merge pull request #4517 from pborreli/patch-1
doc(plugins): typo
2016-03-31 05:54:34 -07:00
Pascal Borreli eb850ddd36 doc(plugins): typo 2016-03-31 11:43:04 +01:00
Torkel Ödegaard 4a807b6373 feat(preferences): still a lot more work needed in prefernces branch 2016-03-30 14:10:32 -07:00
Torkel Ödegaard 6ed9164c9d Merge branch 'master' of github.com:grafana/grafana into preferences 2016-03-30 13:35:17 -07:00
Mitsuhiro Tanda 0a13b7c559 highlight series name in tooltip when shared tooltip active 2016-03-30 22:46:46 +09:00
Matt Toback ef89dabb61 Go away extra line 2016-03-28 18:37:52 -04:00
Matt Toback 9be1ffd633 Fixed code blocks in Grafana 2016-03-28 18:37:02 -04:00
Torkel Ödegaard 5e431149cd feat(preferences): removed unused api prefernce methods, these need to be abstracted more from an http api perpsective, they are too low level for the http api, #4399 2016-03-20 11:54:24 +01:00
Torkel Ödegaard 7023dedc05 feat(preferences): refactoring PR #4399 2016-03-20 11:52:19 +01:00
Torkel Ödegaard 9e98c86ae1 Merge branch 'master' into preferences 2016-03-20 11:27:49 +01:00
Torkel Ödegaard 071237d322 Merge pull request #4399 from utkarshcmu/preferences
Set home dashboard per user per org #3214
2016-03-20 11:25:03 +01:00
utkarshcmu cb42cfc6af Removed unwanted api, moved logic into backend 2016-03-17 02:29:34 -07:00
utkarshcmu 4fbe954a79 Home dashboard per user per org works 2016-03-17 01:38:18 -07:00
utkarshcmu 66621d762e Added getDashboardSlugById api 2016-03-17 01:01:58 -07:00
utkarshcmu 9dd6aefcec Minor corrections 2016-03-16 23:35:06 -07:00
utkarshcmu 749fd618a9 Saving home dashboard id in table 2016-03-16 23:22:27 -07:00
Torkel Ödegaard a88176e060 feat(preferences): lots of refactoring and changes to #3214 2016-03-15 22:49:52 +01:00
Torkel Ödegaard e06756bcab Merge branch 'preferences' of github.com:grafana/grafana into preferences 2016-03-15 22:15:27 +01:00
Torkel Ödegaard 9c222a445f Merge branch 'master' into preferences 2016-03-15 22:14:35 +01:00
Torkel Ödegaard 51bde36dee Merge pull request #4354 from utkarshcmu/preferences
Ability to save Preferences
2016-03-15 22:04:10 +01:00
utkarshcmu e371e03696 Able to save preference version, created, updated fields 2016-03-14 03:12:52 -07:00
utkarshcmu 0bf721a74c Able to save home dashboard 2016-03-13 23:57:38 -07:00
utkarshcmu 43b474143c Preferences model updated 2016-03-11 06:30:05 -08:00
Torkel Ödegaard 02221c993e Merge pull request #4268 from utkarshcmu/save-pref
Added APIs for Get and Save User Preferences
2016-03-07 08:06:32 +01:00
utkarshcmu 9c8d508247 Made API handling better, removed unused components 2016-03-06 12:32:22 -08:00
utkarshcmu 8f42bec270 Implemented GetUserPreferences API 2016-03-06 11:42:15 -08:00
utkarshcmu 660d3fa1e9 Implemented savePreferences API 2016-03-06 03:47:39 -08:00
utkarshcmu 1ef332e82c preferences UI poc 2016-03-05 23:44:56 -08:00
utkarshcmu cf926134ef Added savePreferencesAPI 2016-03-05 13:15:49 -08:00
162 changed files with 2126 additions and 789 deletions
+45 -1
View File
@@ -1,4 +1,48 @@
# 3.0.0 (unrelased master branch)
# 3.0.0-beta5 (2016-04-15
### Bug fixes
* **Grafna-cli**: Fixed issue grafana-cli tool, did not detect the right plugin dir, fixes [#4723](https://github.com/grafana/grafana/issues/4723)
* **Graph**: Fixed issue with light theme text color issue in tooltip, fixes [#4702](https://github.com/grafana/grafana/issues/4702)
* **Snapshot**: Fixed issue with empty snapshots, fixes [#4706](https://github.com/grafana/grafana/issues/4706)
# 3.0.0-beta4 (2016-04-13)
### Bug fixes
* **Home dashboard**: Fixed issue with permission denied error on home dashboard, fixes [#4686](https://github.com/grafana/grafana/issues/4686)
* **Templating**: Fixed issue templating variables that use regex extraction, fixes [#4672](https://github.com/grafana/grafana/issues/4672)
# 3.0.0-beta3 (2016-04-12)
### Enhancements
* **InfluxDB**: Changed multi query encoding to work with InfluxDB 0.11 & 0.12, closes [#4533](https://github.com/grafana/grafana/issues/4533)
* **Timepicker**: Add arrows and shortcuts for moving back and forth in current dashboard, closes [#119](https://github.com/grafana/grafana/issues/119)
### 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)
* **Preferences**: Set timezone on user and org level, closes [#3214](https://github.com/grafana/grafana/issues/3214), [#1200](https://github.com/grafana/grafana/issues/1200)
* **Preferences**: Set theme on user and org level, closes [#3214](https://github.com/grafana/grafana/issues/3214), [#1917](https://github.com/grafana/grafana/issues/1917)
### Bug fixes
* **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 [#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)
# 3.0.0-beta1 (2016-03-31)
### New Features
* **Playlists**: Playlists can now be persisted and started from urls, closes [#3655](https://github.com/grafana/grafana/issues/3655)
+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
+1 -1
View File
@@ -5,7 +5,7 @@ os: Windows Server 2012 R2
clone_folder: c:\gopath\src\github.com\grafana\grafana
environment:
nodejs_version: "4"
nodejs_version: "5"
GOPATH: c:\gopath
install:
+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 =
+1 -1
View File
@@ -1 +1 @@
4.0.0
3.0.0
-29
View File
@@ -207,35 +207,6 @@ page_keywords: grafana, admin, http, api, documentation, datasource
{"message":"Data source deleted"}
## Available data source types
`GET /api/datasources/plugins`
**Example Request**:
GET /api/datasources/plugins HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
**Example Response**:
HTTP/1.1 200
Content-Type: application/json
{
"grafana":{
"metrics":true,"module":"plugins/datasource/grafana/datasource",
"name":"Grafana (for testing)",
"partials":{
"query":"app/plugins/datasource/grafana/partials/query.editor.html"
},
"pluginType":"datasource",
"serviceName":"GrafanaDatasource",
"type":"grafana"
}
}
## Data source proxy calls
`GET /api/datasources/proxy/:datasourceId/*`
+9 -2
View File
@@ -10,14 +10,21 @@ page_keywords: grafana, installation, debian, ubuntu, guide
Description | Download
------------ | -------------
.deb for Debian-based Linux | [grafana_2.6.0_amd64.deb](https://grafanarel.s3.amazonaws.com/builds/grafana_2.6.0_amd64.deb)
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-beta51460725904_amd64.deb](https://grafanarel.s3.amazonaws.com/builds/grafana_3.0.0-beta51460725904_amd64.deb)
## Install
## Install Stable
$ wget https://grafanarel.s3.amazonaws.com/builds/grafana_2.6.0_amd64.deb
$ sudo apt-get install -y adduser libfontconfig
$ sudo dpkg -i grafana_2.6.0_amd64.deb
## Install 3.0 Beta
$ wget https://grafanarel.s3.amazonaws.com/builds/grafana_3.0.0-beta51460725904_amd64.deb
$ sudo apt-get install -y adduser libfontconfig
$ sudo dpkg -i grafana_3.0.0-beta51460725904_amd64.deb
## APT Repository
Add the following line to your `/etc/apt/sources.list` file.
+21 -2
View File
@@ -10,9 +10,10 @@ page_keywords: grafana, installation, centos, fedora, opensuse, redhat, guide
Description | Download
------------ | -------------
.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)
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-beta51460725904.x86_64.rpm](https://grafanarel.s3.amazonaws.com/builds/grafana-3.0.0-beta51460725904§.x86_64.rpm)
## Install from package file
## Install Stable Release from package file
You can install Grafana using Yum directly.
@@ -29,6 +30,24 @@ Or install manually using `rpm`.
$ sudo rpm -i --nodeps grafana-2.6.0-1.x86_64.rpm
## Install Beta Release from package file
You can install Grafana using Yum directly.
$ sudo yum install https://grafanarel.s3.amazonaws.com/builds/grafana-3.0.0-beta51460725904.x86_64.rpm
Or install manually using `rpm`.
#### On CentOS / Fedora / Redhat:
$ sudo yum install initscripts fontconfig
$ sudo rpm -Uvh grafana-3.0.0-beta51460725904.x86_64.rpm
#### On OpenSuse:
$ sudo rpm -i --nodeps grafana-3.0.0-beta51460725904.x86_64.rpm
## Install via YUM Repository
Add the following to a new file at `/etc/yum.repos.d/grafana.repo`
+1 -1
View File
@@ -10,7 +10,7 @@ page_keywords: grafana, installation, windows guide
Description | Download
------------ | -------------
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
+3 -3
View File
@@ -9,7 +9,6 @@ module.exports = function(config) {
// list of files / patterns to load in the browser
files: [
'vendor/npm/es6-shim/es6-shim.js',
'vendor/npm/es6-promise/dist/es6-promise.js',
'vendor/npm/systemjs/dist/system.src.js',
'test/test-main.js',
@@ -25,9 +24,10 @@ module.exports = function(config) {
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['PhantomJS'],
captureTimeout: 2000,
captureTimeout: 20000,
singleRun: true,
autoWatchBatchDelay: 1000,
autoWatchBatchDelay: 10000,
browserNoActivityTimeout: 60000,
});
+2 -1
View File
@@ -1,3 +1,4 @@
{
"version": "2.1.1"
"stable": "2.6.0",
"testing": "3.0.0-beta4"
}
+5 -5
View File
@@ -4,13 +4,12 @@
"company": "Coding Instinct AB"
},
"name": "grafana",
"version": "3.0.0-beta1",
"version": "3.0.0-beta5",
"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",
@@ -32,7 +31,7 @@
"grunt-contrib-watch": "^0.6.1",
"grunt-filerev": "^0.2.1",
"grunt-git-describe": "~2.3.2",
"grunt-karma": "~0.12.1",
"grunt-karma": "~0.12.2",
"grunt-ng-annotate": "^1.0.1",
"grunt-notify": "^0.4.3",
"grunt-postcss": "^0.8.0",
@@ -43,7 +42,7 @@
"grunt-typescript": "^0.8.0",
"grunt-usemin": "3.0.0",
"jshint-stylish": "~2.1.0",
"karma": "~0.13.15",
"karma": "0.13.22",
"karma-chrome-launcher": "~0.2.2",
"karma-coverage": "0.5.3",
"karma-coveralls": "1.1.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",
+6 -6
View File
@@ -1,14 +1,14 @@
#! /usr/bin/env bash
deb_ver=3.0.0-pre1459399258
rpm_ver=3.0.0-pre1459399258
deb_ver=3.0.0-beta41460581169
rpm_ver=3.0.0-beta41460581169
#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
@@ -16,7 +16,7 @@ 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
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
ackage_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
+21 -3
View File
@@ -29,6 +29,8 @@ 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)
@@ -96,10 +98,15 @@ func Register(r *macaron.Macaron) {
r.Put("/", bind(m.UpdateUserCommand{}), wrap(UpdateSignedInUser))
r.Post("/using/:id", wrap(UserSetUsingOrg))
r.Get("/orgs", wrap(GetSignedInUserOrgList))
r.Post("/stars/dashboard/:id", wrap(StarDashboard))
r.Delete("/stars/dashboard/:id", wrap(UnstarDashboard))
r.Put("/password", bind(m.ChangeUserPasswordCommand{}), wrap(ChangeUserPassword))
r.Get("/quotas", wrap(GetUserQuotas))
r.Get("/preferences", wrap(GetUserPreferences))
r.Put("/preferences", bind(dtos.UpdatePrefsCmd{}), wrap(UpdateUserPreferences))
})
// users (admin permission required)
@@ -130,6 +137,9 @@ func Register(r *macaron.Macaron) {
r.Post("/invites", quota("user"), bind(dtos.AddInviteForm{}), wrap(AddOrgInvite))
r.Patch("/invites/:code/revoke", wrap(RevokeInvite))
// prefs
r.Get("/preferences", wrap(GetOrgPreferences))
r.Put("/preferences", bind(dtos.UpdatePrefsCmd{}), wrap(UpdateOrgPreferences))
}, reqOrgAdmin)
// create new org
@@ -164,6 +174,11 @@ func Register(r *macaron.Macaron) {
r.Delete("/:id", wrap(DeleteApiKey))
}, reqOrgAdmin)
// Preferences
r.Group("/preferences", func() {
r.Post("/set-home-dash", bind(m.SavePreferencesCommand{}), wrap(SetHomeDashboard))
})
// Data sources
r.Group("/datasources", func() {
r.Get("/", GetDataSources)
@@ -176,12 +191,12 @@ func Register(r *macaron.Macaron) {
r.Get("/datasources/id/:name", wrap(GetDataSourceIdByName), reqSignedIn)
r.Group("/plugins", func() {
r.Get("/", wrap(GetPluginList))
r.Get("/plugins", wrap(GetPluginList))
r.Get("/plugins/:pluginId/settings", wrap(GetPluginSettingById))
r.Group("/plugins", func() {
r.Get("/:pluginId/readme", wrap(GetPluginReadme))
r.Get("/:pluginId/dashboards/", wrap(GetPluginDashboards))
r.Get("/:pluginId/settings", wrap(GetPluginSettingById))
r.Post("/:pluginId/settings", bind(m.UpdatePluginSettingCmd{}), wrap(UpdatePluginSetting))
}, reqOrgAdmin)
@@ -238,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
}
}
+18
View File
@@ -159,6 +159,24 @@ func canEditDashboard(role m.RoleType) bool {
}
func GetHomeDashboard(c *middleware.Context) {
prefsQuery := m.GetPreferencesWithDefaultsQuery{OrgId: c.OrgId, UserId: c.UserId}
if err := bus.Dispatch(&prefsQuery); err != nil {
c.JsonApiErr(500, "Failed to get preferences", err)
}
if prefsQuery.Result.HomeDashboardId != 0 {
slugQuery := m.GetDashboardSlugByIdQuery{Id: prefsQuery.Result.HomeDashboardId}
err := bus.Dispatch(&slugQuery)
if err != nil {
c.JsonApiErr(500, "Failed to get slug from database", err)
return
}
dashRedirect := dtos.DashboardRedirect{RedirectUri: "db/" + slugQuery.Result}
c.JSON(200, &dashRedirect)
return
}
filePath := path.Join(setting.StaticRootPath, "dashboards/home.json")
file, err := os.Open(filePath)
if err != nil {
+5
View File
@@ -33,6 +33,7 @@ type CurrentUser struct {
OrgRole m.RoleType `json:"orgRole"`
IsGrafanaAdmin bool `json:"isGrafanaAdmin"`
GravatarUrl string `json:"gravatarUrl"`
Timezone string `json:"timezone"`
}
type DashboardMeta struct {
@@ -57,6 +58,10 @@ type DashboardFullWithMeta struct {
Dashboard *simplejson.Json `json:"dashboard"`
}
type DashboardRedirect struct {
RedirectUri string `json:"redirectUri"`
}
type DataSource struct {
Id int64 `json:"id"`
OrgId int64 `json:"orgId"`
+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
+13
View File
@@ -0,0 +1,13 @@
package dtos
type Prefs struct {
Theme string `json:"theme"`
HomeDashboardId int64 `json:"homeDashboardId"`
Timezone string `json:"timezone"`
}
type UpdatePrefsCmd struct {
Theme string `json:"theme"`
HomeDashboardId int64 `json:"homeDashboardId"`
Timezone string `json:"timezone"`
}
+6 -4
View File
@@ -59,7 +59,7 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro
defaultDatasource = ds.Name
}
if len(ds.JsonData.MustMap()) > 0 {
if ds.JsonData != nil {
dsMap["jsonData"] = ds.JsonData
}
@@ -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")
}
+9 -1
View File
@@ -2,6 +2,7 @@ package api
import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
@@ -14,6 +15,12 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
return nil, err
}
prefsQuery := m.GetPreferencesWithDefaultsQuery{OrgId: c.OrgId, UserId: c.UserId}
if err := bus.Dispatch(&prefsQuery); err != nil {
return nil, err
}
prefs := prefsQuery.Result
var data = dtos.IndexViewData{
User: &dtos.CurrentUser{
Id: c.UserId,
@@ -21,12 +28,13 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
Login: c.Login,
Email: c.Email,
Name: c.Name,
LightTheme: c.Theme == "light",
OrgId: c.OrgId,
OrgName: c.OrgName,
OrgRole: c.OrgRole,
GravatarUrl: dtos.GetGravatarUrl(c.Email),
IsGrafanaAdmin: c.IsGrafanaAdmin,
LightTheme: prefs.Theme == "light",
Timezone: prefs.Timezone,
},
Settings: settings,
AppUrl: setting.AppUrl,
+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}
+73
View File
@@ -0,0 +1,73 @@
package api
import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
)
// POST /api/preferences/set-home-dash
func SetHomeDashboard(c *middleware.Context, cmd m.SavePreferencesCommand) Response {
cmd.UserId = c.UserId
cmd.OrgId = c.OrgId
if err := bus.Dispatch(&cmd); err != nil {
return ApiError(500, "Failed to set home dashboard", err)
}
return ApiSuccess("Home dashboard set")
}
// GET /api/user/preferences
func GetUserPreferences(c *middleware.Context) Response {
return getPreferencesFor(c.OrgId, c.UserId)
}
func getPreferencesFor(orgId int64, userId int64) Response {
prefsQuery := m.GetPreferencesQuery{UserId: userId, OrgId: orgId}
if err := bus.Dispatch(&prefsQuery); err != nil {
return ApiError(500, "Failed to get preferences", err)
}
dto := dtos.Prefs{
Theme: prefsQuery.Result.Theme,
HomeDashboardId: prefsQuery.Result.HomeDashboardId,
Timezone: prefsQuery.Result.Timezone,
}
return Json(200, &dto)
}
// PUT /api/user/preferences
func UpdateUserPreferences(c *middleware.Context, dtoCmd dtos.UpdatePrefsCmd) Response {
return updatePreferencesFor(c.OrgId, c.UserId, &dtoCmd)
}
func updatePreferencesFor(orgId int64, userId int64, dtoCmd *dtos.UpdatePrefsCmd) Response {
saveCmd := m.SavePreferencesCommand{
UserId: userId,
OrgId: orgId,
Theme: dtoCmd.Theme,
Timezone: dtoCmd.Timezone,
HomeDashboardId: dtoCmd.HomeDashboardId,
}
if err := bus.Dispatch(&saveCmd); err != nil {
return ApiError(500, "Failed to save preferences", err)
}
return ApiSuccess("Preferences updated")
}
// GET /api/org/preferences
func GetOrgPreferences(c *middleware.Context) Response {
return getPreferencesFor(c.OrgId, 0)
}
// PUT /api/org/preferences
func UpdateOrgPreferences(c *middleware.Context, dtoCmd dtos.UpdatePrefsCmd) Response {
return updatePreferencesFor(c.OrgId, 0, &dtoCmd)
}
+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 -4
View File
@@ -8,17 +8,37 @@ import (
"github.com/codegangsta/cli"
"github.com/grafana/grafana/pkg/cmd/grafana-cli/commands"
"github.com/grafana/grafana/pkg/cmd/grafana-cli/log"
"strings"
)
var version = "master"
func getGrafanaPluginDir() string {
os := runtime.GOOS
if os == "windows" {
currentOS := runtime.GOOS
defaultNix := "/var/lib/grafana/plugins"
if currentOS == "windows" {
return "C:\\opt\\grafana\\plugins"
} else {
return "/var/lib/grafana/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 grafana-cli is executed from the cmd folder we can assume
// that its in development environment.
return strings.HasSuffix(pwd, "/pkg/cmd/grafana-cli")
}
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-beta4"
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 {
+5
View File
@@ -148,3 +148,8 @@ type GetDashboardsQuery struct {
DashboardIds []int64
Result *[]Dashboard
}
type GetDashboardSlugByIdQuery struct {
Id int64
Result string
}
+53
View File
@@ -0,0 +1,53 @@
package models
import (
"errors"
"time"
)
// Typed errors
var (
ErrPreferencesNotFound = errors.New("Preferences not found")
)
type Preferences struct {
Id int64
OrgId int64
UserId int64
Version int
HomeDashboardId int64
Timezone string
Theme string
Created time.Time
Updated time.Time
}
// ---------------------
// QUERIES
type GetPreferencesQuery struct {
Id int64
OrgId int64
UserId int64
Result *Preferences
}
type GetPreferencesWithDefaultsQuery struct {
Id int64
OrgId int64
UserId int64
Result *Preferences
}
// ---------------------
// COMMANDS
type SavePreferencesCommand struct {
UserId int64
OrgId int64
HomeDashboardId int64 `json:"homeDashboardId"`
Timezone string `json:"timezone"`
Theme string `json:"theme"`
}
+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 {
-1
View File
@@ -136,7 +136,6 @@ type SignedInUser struct {
Login string
Name string
Email string
Theme string
ApiKeyId int64
IsGrafanaAdmin bool
}
+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
}
}
+21
View File
@@ -18,6 +18,7 @@ func init() {
bus.AddHandler("sql", DeleteDashboard)
bus.AddHandler("sql", SearchDashboards)
bus.AddHandler("sql", GetDashboardTags)
bus.AddHandler("sql", GetDashboardSlugById)
}
func SaveDashboard(cmd *m.SaveDashboardCommand) error {
@@ -255,3 +256,23 @@ func GetDashboards(query *m.GetDashboardsQuery) error {
return nil
}
type DashboardSlugDTO struct {
Slug string
}
func GetDashboardSlugById(query *m.GetDashboardSlugByIdQuery) error {
var rawSql = `SELECT slug from dashboard WHERE Id=?`
var slug = DashboardSlugDTO{}
exists, err := x.Sql(rawSql, query.Id).Get(&slug)
if err != nil {
return err
} else if exists == false {
return m.ErrDashboardNotFound
}
query.Result = slug.Slug
return nil
}
@@ -4,17 +4,29 @@ import . "github.com/grafana/grafana/pkg/services/sqlstore/migrator"
func addPreferencesMigrations(mg *Migrator) {
preferencesV1 := Table{
mg.AddMigration("drop preferences table v2", NewDropTableMigration("preferences"))
preferencesV2 := Table{
Name: "preferences",
Columns: []*Column{
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
{Name: "pref_id", Type: DB_Int, Nullable: false},
{Name: "pref_type", Type: DB_NVarchar, Length: 255, Nullable: false},
{Name: "pref_data", Type: DB_Text, Nullable: false},
{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: 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},
},
Indices: []*Index{
{Cols: []string{"org_id"}},
{Cols: []string{"user_id"}},
},
}
// create table
mg.AddMigration("create preferences table v1", NewAddTableMigration(preferencesV1))
mg.AddMigration("drop preferences table v3", NewDropTableMigration("preferences"))
// create table
mg.AddMigration("create preferences table v3", NewAddTableMigration(preferencesV2))
}
+96
View File
@@ -0,0 +1,96 @@
package sqlstore
import (
"time"
"github.com/grafana/grafana/pkg/bus"
m "github.com/grafana/grafana/pkg/models"
)
func init() {
bus.AddHandler("sql", GetPreferences)
bus.AddHandler("sql", GetPreferencesWithDefaults)
bus.AddHandler("sql", SavePreferences)
}
func GetPreferencesWithDefaults(query *m.GetPreferencesWithDefaultsQuery) error {
prefs := make([]*m.Preferences, 0)
filter := "(org_id=? AND user_id=?) OR (org_id=? AND user_id=0)"
err := x.Where(filter, query.OrgId, query.UserId, query.OrgId).
OrderBy("user_id ASC").
Find(&prefs)
if err != nil {
return err
}
res := &m.Preferences{
Theme: "dark",
Timezone: "browser",
HomeDashboardId: 0,
}
for _, p := range prefs {
if p.Theme != "" {
res.Theme = p.Theme
}
if p.Timezone != "" {
res.Timezone = p.Timezone
}
if p.HomeDashboardId != 0 {
res.HomeDashboardId = p.HomeDashboardId
}
}
query.Result = res
return nil
}
func GetPreferences(query *m.GetPreferencesQuery) error {
var prefs m.Preferences
exists, err := x.Where("org_id=? AND user_id=?", query.OrgId, query.UserId).Get(&prefs)
if err != nil {
return err
}
if exists {
query.Result = &prefs
} else {
query.Result = new(m.Preferences)
}
return nil
}
func SavePreferences(cmd *m.SavePreferencesCommand) error {
return inTransaction2(func(sess *session) error {
var prefs m.Preferences
exists, err := sess.Where("org_id=? AND user_id=?", cmd.OrgId, cmd.UserId).Get(&prefs)
if !exists {
prefs = m.Preferences{
UserId: cmd.UserId,
OrgId: cmd.OrgId,
HomeDashboardId: cmd.HomeDashboardId,
Timezone: cmd.Timezone,
Theme: cmd.Theme,
Created: time.Now(),
Updated: time.Now(),
}
_, err = sess.Insert(&prefs)
return err
} else {
prefs.HomeDashboardId = cmd.HomeDashboardId
prefs.Timezone = cmd.Timezone
prefs.Theme = cmd.Theme
prefs.Updated = time.Now()
prefs.Version += 1
_, err := sess.Id(prefs.Id).AllCols().Update(&prefs)
return err
}
})
}
+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
-1
View File
@@ -273,7 +273,6 @@ func GetSignedInUser(query *m.GetSignedInUserQuery) error {
u.email as email,
u.login as login,
u.name as name,
u.theme as theme,
org.name as org_name,
org_user.role as org_role,
org.id as org_id
+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()
+2 -11
View File
@@ -7,10 +7,6 @@ import coreModule from 'app/core/core_module';
var template = `
<div class="graph-legend-popover">
<a class="drop-popopver-close" ng-click="ctrl.close();" href="" ng-hide="ctrl.autoClose">
<i class="fa fa-times-circle"></i>
</a>
<div ng-show="ctrl.series" class="p-b-1">
<label>Y Axis:</label>
<button ng-click="ctrl.toggleAxis(yaxis);" class="btn btn-small"
@@ -31,7 +27,6 @@ var template = `
ng-style="{color:color}"
ng-click="ctrl.colorSelected(color);">&nbsp;</i>
</p>
</div>
`;
@@ -51,21 +46,17 @@ export class ColorPickerCtrl {
toggleAxis(yaxis) {
this.$scope.toggleAxis();
if (!this.$scope.autoClose) {
if (this.$scope.autoClose) {
this.$scope.dismiss();
}
}
colorSelected(color) {
this.$scope.colorSelected(color);
if (!this.$scope.autoClose) {
if (this.$scope.autoClose) {
this.$scope.dismiss();
}
}
close() {
this.$scope.dismiss();
}
}
export function colorPicker() {
@@ -0,0 +1,44 @@
///<reference path="../../headers/common.d.ts" />
import config from 'app/core/config';
import _ from 'lodash';
import $ from 'jquery';
import coreModule from 'app/core/core_module';
var template = `
<select class="gf-form-input" ng-model="ctrl.model" ng-options="f.value as f.text for f in ctrl.options"></select>
`;
export class DashboardSelectorCtrl {
model: any;
options: any;
/** @ngInject */
constructor(private backendSrv) {
}
$onInit() {
this.options = [{value: 0, text: 'Default'}];
return this.backendSrv.search({starred: true}).then(res => {
res.forEach(dash => {
this.options.push({value: dash.id, text: dash.title});
});
});
}
}
export function dashboardSelector() {
return {
restrict: 'E',
controller: DashboardSelectorCtrl,
bindToController: true,
controllerAs: 'ctrl',
template: template,
scope: {
model: '='
}
};
}
coreModule.directive('dashboardSelector', dashboardSelector);
@@ -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() {
+2
View File
@@ -32,6 +32,7 @@ import {liveSrv} from './live/live_srv';
import {Emitter} from './utils/emitter';
import {layoutSelector} from './components/layout_selector/layout_selector';
import {switchDirective} from './components/switch';
import {dashboardSelector} from './components/dashboard_selector';
import 'app/core/controllers/all';
import 'app/core/services/all';
import 'app/core/routes/routes';
@@ -54,4 +55,5 @@ export {
infoPopover,
Emitter,
appEvents,
dashboardSelector,
};
@@ -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,
+9 -5
View File
@@ -4,13 +4,17 @@ define([
function (coreModule) {
"use strict";
coreModule.default.controller('LoadDashboardCtrl', function($scope, $routeParams, dashboardLoaderSrv, backendSrv) {
coreModule.default.controller('LoadDashboardCtrl', function($scope, $routeParams, dashboardLoaderSrv, backendSrv, $location) {
if (!$routeParams.slug) {
backendSrv.get('/api/dashboards/home').then(function(result) {
var meta = result.meta;
meta.canSave = meta.canShare = meta.canStar = false;
$scope.initDashboard(result, $scope);
backendSrv.get('/api/dashboards/home').then(function(homeDash) {
if (homeDash.redirectUri) {
$location.path('dashboard/' + homeDash.redirectUri);
} else {
var meta = homeDash.meta;
meta.canSave = meta.canShare = meta.canStar = false;
$scope.initDashboard(homeDash, $scope);
}
});
return;
}
+7 -3
View File
@@ -88,16 +88,20 @@ function setupAngularRoutes($routeProvider, $locationProvider) {
resolve: loadOrgBundle,
})
.when('/profile', {
templateUrl: 'public/app/features/profile/partials/profile.html',
templateUrl: 'public/app/features/org/partials/profile.html',
controller : 'ProfileCtrl',
controllerAs: 'ctrl',
resolve: loadOrgBundle,
})
.when('/profile/password', {
templateUrl: 'public/app/features/profile/partials/password.html',
templateUrl: 'public/app/features/org/partials/change_password.html',
controller : 'ChangePasswordCtrl',
resolve: loadOrgBundle,
})
.when('/profile/select-org', {
templateUrl: 'public/app/features/profile/partials/select_org.html',
templateUrl: 'public/app/features/org/partials/select_org.html',
controller : 'SelectOrgCtrl',
resolve: loadOrgBundle,
})
// ADMIN
.when('/admin', {
-47
View File
@@ -1,47 +0,0 @@
define([
'angular',
'lodash',
'../core_module',
'app/core/store',
'app/core/config',
],
function (angular, _, coreModule, store, config) {
'use strict';
coreModule.default.service('contextSrv', function() {
function User() {
if (config.bootData.user) {
_.extend(this, config.bootData.user);
}
}
this.hasRole = function(role) {
return this.user.orgRole === role;
};
this.setPinnedState = function(val) {
this.pinned = val;
store.set('grafana.sidemenu.pinned', val);
};
this.toggleSideMenu = function() {
this.sidemenu = !this.sidemenu;
if (!this.sidemenu) {
this.setPinnedState(false);
}
};
this.pinned = store.getBool('grafana.sidemenu.pinned', false);
if (this.pinned) {
this.sidemenu = true;
}
this.version = config.buildInfo.version;
this.lightTheme = false;
this.user = new User();
this.isSignedIn = this.user.isSignedIn;
this.isGrafanaAdmin = this.user.isGrafanaAdmin;
this.isEditor = this.hasRole('Editor') || this.hasRole('Admin');
});
});
+73
View File
@@ -0,0 +1,73 @@
///<reference path="../../headers/common.d.ts" />
import config from 'app/core/config';
import _ from 'lodash';
import coreModule from 'app/core/core_module';
import store from 'app/core/store';
export class User {
isGrafanaAdmin: any;
isSignedIn: any;
orgRole: any;
constructor() {
if (config.bootData.user) {
_.extend(this, config.bootData.user);
}
}
}
export class ContextSrv {
pinned: any;
version: any;
user: User;
isSignedIn: any;
isGrafanaAdmin: any;
isEditor: any;
sidemenu: any;
lightTheme: any;
constructor() {
this.pinned = store.getBool('grafana.sidemenu.pinned', false);
if (this.pinned) {
this.sidemenu = true;
}
if (!config.buildInfo) {
config.buildInfo = {};
}
if (!config.bootData) {
config.bootData = {user: {}, settings: {}};
}
this.version = config.buildInfo.version;
this.lightTheme = false;
this.user = new User();
this.isSignedIn = this.user.isSignedIn;
this.isGrafanaAdmin = this.user.isGrafanaAdmin;
this.isEditor = this.hasRole('Editor') || this.hasRole('Admin');
}
hasRole(role) {
return this.user.orgRole === role;
}
setPinnedState(val) {
this.pinned = val;
store.set('grafana.sidemenu.pinned', val);
}
toggleSideMenu() {
this.sidemenu = !this.sidemenu;
if (!this.sidemenu) {
this.setPinnedState(false);
}
}
}
var contextSrv = new ContextSrv();
export {contextSrv};
coreModule.factory('contextSrv', function() {
return contextSrv;
});
+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);
};
}
+3 -1
View File
@@ -42,6 +42,7 @@ export default class TimeSeries {
fillBelowTo: any;
transform: any;
flotpairs: any;
unit: any;
constructor(opts) {
this.datapoints = opts.datapoints;
@@ -52,6 +53,7 @@ export default class TimeSeries {
this.valueFormater = kbn.valueFormats.none;
this.stats = {};
this.legend = true;
this.unit = opts.unit;
}
applySeriesOverrides(overrides) {
@@ -170,7 +172,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>
-3
View File
@@ -7,8 +7,5 @@ define([
'./playlist/all',
'./snapshot/all',
'./panel/all',
'./profile/profileCtrl',
'./profile/changePasswordCtrl',
'./profile/selectOrgCtrl',
'./styleguide/styleguide',
], function () {});
@@ -45,6 +45,7 @@ function (angular, _, $) {
$scope.reset = function() {
$scope.currentAnnotation = angular.copy(annotationDefaults);
$scope.currentAnnotation.datasource = $scope.datasources[0].name;
$scope.currentIsNew = true;
$scope.datasourceChanged();
};
@@ -134,6 +134,10 @@ function (angular, $, config, moment) {
});
};
$scope.timezoneChanged = function() {
$rootScope.$broadcast("refresh");
};
$scope.formatDate = function(date) {
return moment(date).format('MMM Do YYYY, h:mm:ss a');
};
+12 -2
View File
@@ -9,7 +9,7 @@ function (angular, $, _, moment) {
var module = angular.module('grafana.services');
module.factory('dashboardSrv', function() {
module.factory('dashboardSrv', function(contextSrv) {
function DashboardModel (data, meta) {
if (!data) {
@@ -25,7 +25,7 @@ function (angular, $, _, moment) {
this.originalTitle = this.title;
this.tags = data.tags || [];
this.style = data.style || "dark";
this.timezone = data.timezone || 'browser';
this.timezone = data.timezone || '';
this.editable = data.editable !== false;
this.hideControls = data.hideControls || false;
this.sharedCrosshair = data.sharedCrosshair || false;
@@ -208,6 +208,14 @@ function (angular, $, _, moment) {
});
};
p.isTimezoneUtc = function() {
return this.getTimezone() === 'utc';
};
p.getTimezone = function() {
return this.timezone ? this.timezone : contextSrv.user.timezone;
};
p._updateSchema = function(old) {
var i, j, k;
var oldVersion = this.schemaVersion;
@@ -420,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 = [
{
@@ -60,6 +60,14 @@ function(angular, $) {
scope.appEvent('zoom-out', evt);
}, { inputDisabled: true });
keyboardManager.bind('left', function(evt) {
scope.appEvent('shift-time-backward', evt);
}, { inputDisabled: true });
keyboardManager.bind('right', function(evt) {
scope.appEvent('shift-time-forward', evt);
}, { inputDisabled: true });
keyboardManager.bind('ctrl+e', function(evt) {
scope.appEvent('export-dashboard', evt);
}, { inputDisabled: true });
@@ -34,7 +34,7 @@
<div class="gf-form">
<label class="gf-form-label width-7">Timezone</label>
<div class="gf-form-select-wrapper">
<select ng-model="dashboard.timezone" class='gf-form-input' ng-options="f for f in ['browser','utc']"></select>
<select ng-model="dashboard.timezone" class='gf-form-input' ng-options="f.value as f.text for f in [{value: '', text: 'Default'}, {value: 'browser', text: 'Local browser time'},{value: 'utc', text: 'UTC'}]" ng-change="timezoneChanged()"></select>
</div>
</div>
</div>
@@ -62,66 +62,67 @@
</gf-form-switch>
</div>
</div>
</div>
<div ng-if="editor.index == 1">
<h5 class="section-heading">Rows settings</h5>
</div>
<div class="gf-form-group">
<div class="gf-form-inline" ng-repeat="row in dashboard.rows">
<div class="gf-form">
<span class="gf-form-label">Title</span>
<input type="text" class="gf-form-input max-width-14" ng-model='row.title'></input>
</div>
<gf-form-switch class="gf-form" label="Show title" checked="row.showTitle" switch-class="max-width-6"></gf-form-switch>
<div ng-if="editor.index == 1">
<h5 class="section-heading">Rows settings</h5>
<div class="gf-form">
<button class="btn btn-inverse gf-form-btn" ng-click="_.move(dashboard.rows,$index,$index-1)">
<i ng-class="{'invisible': $first}" class="fa fa-arrow-up"></i>
</button>
<button class="btn btn-inverse gf-from-btn" ng-click="_.move(dashboard.rows,$index,$index+1)">
<i ng-class="{'invisible': $last}" class="fa fa-arrow-down"></i>
</button>
<button class="btn btn-inverse gf-form-btn" click="dashboard.rows = _.without(dashboard.rows,row)">
<i class="fa fa-trash"></i>
</button>
</div>
</div>
</div>
</div>
<div class="gf-form-group">
<div class="gf-form-inline" ng-repeat="row in dashboard.rows">
<div class="gf-form">
<span class="gf-form-label">Title</span>
<input type="text" class="gf-form-input max-width-14" ng-model='row.title'></input>
</div>
<gf-form-switch class="gf-form" label="Show title" checked="row.showTitle" switch-class="max-width-6"></gf-form-switch>
<div ng-if="editor.index == 2">
<dash-links-editor></dash-links-editor>
</div>
<div class="gf-form">
<button class="btn btn-inverse gf-form-btn" ng-click="_.move(dashboard.rows,$index,$index-1)">
<i ng-class="{'invisible': $first}" class="fa fa-arrow-up"></i>
</button>
<button class="btn btn-inverse gf-from-btn" ng-click="_.move(dashboard.rows,$index,$index+1)">
<i ng-class="{'invisible': $last}" class="fa fa-arrow-down"></i>
</button>
<button class="btn btn-inverse gf-form-btn" click="dashboard.rows = _.without(dashboard.rows,row)">
<i class="fa fa-trash"></i>
</button>
</div>
</div>
</div>
</div>
<div ng-if="editor.index == 3">
<gf-time-picker-settings dashboard="dashboard"></gf-time-picker-settings>
</div>
<div ng-if="editor.index == 2">
<dash-links-editor></dash-links-editor>
</div>
<div ng-if="editor.index == 4">
<h5 class="section-heading">Dashboard info</h5>
<div class="gf-form-group">
<div class="gf-form">
<span class="gf-form-label width-10">Last updated at:</span>
<span class="gf-form-label width-18">{{formatDate(dashboardMeta.updated)}}</span>
</div>
<div class="gf-form">
<span class="gf-form-label width-10">Last updated by:</span>
<span class="gf-form-label width-18">{{dashboardMeta.updatedBy}}&nbsp;</span>
</div>
<div class="gf-form">
<span class="gf-form-label width-10">Created at:</span>
<span class="gf-form-label width-18">{{formatDate(dashboardMeta.created)}}&nbsp;</span>
</div>
<div class="gf-form">
<span class="gf-form-label width-10">Created by:</span>
<span class="gf-form-label width-18">{{dashboardMeta.createdBy}}&nbsp;</span>
</div>
<div class="gf-form">
<span class="gf-form-label width-10">Current version:</span>
<span class="gf-form-label width-18">{{dashboardMeta.version}}&nbsp;</span>
</div>
</div>
</div>
<div ng-if="editor.index == 3">
<gf-time-picker-settings dashboard="dashboard"></gf-time-picker-settings>
</div>
<div ng-if="editor.index == 4">
<h5 class="section-heading">Dashboard info</h5>
<div class="gf-form-group">
<div class="gf-form">
<span class="gf-form-label width-10">Last updated at:</span>
<span class="gf-form-label width-18">{{formatDate(dashboardMeta.updated)}}</span>
</div>
<div class="gf-form">
<span class="gf-form-label width-10">Last updated by:</span>
<span class="gf-form-label width-18">{{dashboardMeta.updatedBy}}&nbsp;</span>
</div>
<div class="gf-form">
<span class="gf-form-label width-10">Created at:</span>
<span class="gf-form-label width-18">{{formatDate(dashboardMeta.created)}}&nbsp;</span>
</div>
<div class="gf-form">
<span class="gf-form-label width-10">Created by:</span>
<span class="gf-form-label width-18">{{dashboardMeta.createdBy}}&nbsp;</span>
</div>
<div class="gf-form">
<span class="gf-form-label width-10">Current version:</span>
<span class="gf-form-label width-18">{{dashboardMeta.version}}&nbsp;</span>
</div>
</div>
</div>
</div>
@@ -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" >
@@ -1,9 +1,13 @@
<ul class="nav gf-timepicker-nav">
<li class="dashnav-zoom-out" style="padding-top: 2px">
<a class='small' ng-click='ctrl.zoom(2)'>
Zoom Out
</a>
<li class="dashnav-move-timeframe gf-timepicker-time-control" bs-tooltip="'Shift time backward <br> (left arrow key)'" data-placement="bottom">
<a ng-click='ctrl.move(-1)'><i class="fa fa-chevron-left"></i></a>
</li>
<li class="dashnav-zoom-out gf-timepicker-time-control" bs-tooltip="'Time range zoom in <br> CTRL+Z'" data-placement="bottom">
<a ng-click='ctrl.zoom(2)'>Zoom Out</a></li>
</li>
<li class="dashnav-move-timeframe gf-timepicker-time-control" bs-tooltip="'Shift time forward <br> (right arrow key)'" data-placement="bottom">
<a ng-click='ctrl.move(1)'><i class="fa fa-chevron-right"></i></a>
</li>
<li>
@@ -30,6 +30,8 @@ export class TimePickerCtrl {
$scope.ctrl = this;
$rootScope.onAppEvent('zoom-out', () => this.zoom(2), $scope);
$rootScope.onAppEvent('shift-time-forward', () => this.move(1), $scope);
$rootScope.onAppEvent('shift-time-backward', () => this.move(-1), $scope);
$rootScope.onAppEvent('refresh', () => this.init(), $scope);
$rootScope.onAppEvent('dash-editor-hidden', () => this.isOpen = false, $scope);
@@ -44,7 +46,7 @@ export class TimePickerCtrl {
var time = angular.copy(this.timeSrv.timeRange());
var timeRaw = angular.copy(this.timeSrv.timeRange(false));
if (this.dashboard.timezone === 'browser') {
if (!this.dashboard.isTimezoneUtc()) {
time.from.local();
time.to.local();
if (moment.isMoment(timeRaw.from)) {
@@ -87,6 +89,30 @@ export class TimePickerCtrl {
this.timeSrv.setTime({from: moment.utc(from), to: moment.utc(to) });
}
move(direction) {
var range = this.timeSrv.timeRange();
var timespan = (range.to.valueOf() - range.from.valueOf());
var to, from;
if (direction === -1) {
to = range.to.valueOf() - timespan;
from = range.from.valueOf() - timespan;
} else if (direction === 1) {
to = range.to.valueOf() + timespan;
from = range.from.valueOf() + timespan;
if (to > Date.now() && range.to < Date.now()) {
to = Date.now();
from = range.from.valueOf();
}
} else {
to = range.to.valueOf();
from = range.from.valueOf();
}
this.timeSrv.setTime({from: moment.utc(from), to: moment.utc(to) });
}
openDropdown() {
this.init();
this.isOpen = true;
@@ -125,7 +151,7 @@ export class TimePickerCtrl {
}
getAbsoluteMomentForTimezone(jsDate) {
return this.dashboard.timezone === 'browser' ? moment(jsDate) : moment(jsDate).utc();
return this.dashboard.isTimezoneUtc() ? moment(jsDate).utc() : moment(jsDate);
}
setRelativeFilter(timespan) {
@@ -102,7 +102,6 @@ function(angular, _) {
value.current = null;
value.options = null;
});
};
p.hasChanges = function() {
+5
View File
@@ -1,7 +1,12 @@
define([
'./org_users_ctrl',
'./profile_ctrl',
'./org_users_ctrl',
'./select_org_ctrl',
'./change_password_ctrl',
'./newOrgCtrl',
'./userInviteCtrl',
'./orgApiKeysCtrl',
'./orgDetailsCtrl',
'./prefs_control',
], function () {});
@@ -19,6 +19,7 @@
</div>
</form>
<prefs-control mode="org"></prefs-control>
<h3 class="page-heading">Address</h3>
@@ -6,31 +6,28 @@
<h1>Profile</h1>
</div>
<form name="userForm" class="gf-form-group">
<h3 class="page-heading">Preferences</h3>
<form name="ctrl.userForm" class="gf-form-group">
<h3 class="page-heading">Information</h3>
<div class="gf-form">
<span class="gf-form-label width-7">Name</span>
<input class="gf-form-input max-width-21" type="text" required ng-model="user.name" >
<span class="gf-form-label width-9">Name</span>
<input class="gf-form-input max-width-21" type="text" required ng-model="ctrl.user.name" >
</div>
<div class="gf-form">
<span class="gf-form-label width-7">Email</span>
<input class="gf-form-input max-width-21" type="email" required ng-model="user.email">
<span class="gf-form-label width-9">Email</span>
<input class="gf-form-input max-width-21" type="email" required ng-model="ctrl.user.email">
</div>
<div class="gf-form">
<span class="gf-form-label width-7">Username</span>
<input class="gf-form-input max-width-21" type="text" required ng-model="user.login">
<span class="gf-form-label width-9">Username</span>
<input class="gf-form-input max-width-21" type="text" required ng-model="ctrl.user.login">
</div>
<div class="gf-form">
<span class="gf-form-label width-7">UI Theme</span>
<select class="gf-form-input gf-size-auto" ng-model="user.theme" ng-options="f for f in ['dark', 'light']"></select>
</div>
<div class="gf-form-button-row">
<button type="submit" class="btn btn-success" ng-click="update()">Update</button>
<button type="submit" class="btn btn-success" ng-click="ctrl.update()">Update</button>
</div>
</form>
<prefs-control mode="user"></prefs-control>
<h3 class="page-heading">Password</h3>
<div class="gf-form-group">
<a href="profile/password" class="btn btn-inverse">Change Password</a>
@@ -47,7 +44,7 @@
</tr>
</thead>
<tbody>
<tr ng-repeat="org in orgs">
<tr ng-repeat="org in ctrl.orgs">
<td>{{org.name}}</td>
<td>{{org.role}}</td>
<td class="text-right">
+98
View File
@@ -0,0 +1,98 @@
///<reference path="../../headers/common.d.ts" />
import config from 'app/core/config';
import _ from 'lodash';
import coreModule from 'app/core/core_module';
export class PrefsControlCtrl {
prefs: any;
oldTheme: any;
prefsForm: any;
mode: string;
timezones: any = [
{value: '', text: 'Default'},
{value: 'browser', text: 'Local browser time'},
{value: 'utc', text: 'UTC'},
];
themes: any = [
{value: '', text: 'Default'},
{value: 'dark', text: 'Dark'},
{value: 'light', text: 'Light'},
];
/** @ngInject **/
constructor(private backendSrv, private $location) {
}
$onInit() {
return this.backendSrv.get(`/api/${this.mode}/preferences`).then(prefs => {
this.prefs = prefs;
this.oldTheme = prefs.theme;
});
}
updatePrefs() {
if (!this.prefsForm.$valid) { return; }
var cmd = {
theme: this.prefs.theme,
timezone: this.prefs.timezone,
homeDashboardId: this.prefs.homeDashboardId
};
this.backendSrv.put(`/api/${this.mode}/preferences`, cmd).then(() => {
window.location.href = config.appSubUrl + this.$location.path();
});
}
}
var template = `
<form name="ctrl.prefsForm" class="gf-form-group">
<h3 class="page-heading">Preferences</h3>
<div class="gf-form">
<span class="gf-form-label width-9">UI Theme</span>
<div class="gf-form-select-wrapper max-width-20">
<select class="gf-form-input" ng-model="ctrl.prefs.theme" ng-options="f.value as f.text for f in ctrl.themes"></select>
</div>
</div>
<div class="gf-form">
<span class="gf-form-label width-9">Home Dashboard</span>
<dashboard-selector
class="gf-form-select-wrapper max-width-20"
model="ctrl.prefs.homeDashboardId">
</dashboard-selector>
</div>
<div class="gf-form">
<label class="gf-form-label width-9">Timezone</label>
<div class="gf-form-select-wrapper max-width-20">
<select class="gf-form-input" ng-model="ctrl.prefs.timezone" ng-options="f.value as f.text for f in ctrl.timezones"></select>
</div>
</div>
<div class="gf-form-button-row">
<button type="submit" class="btn btn-success" ng-click="ctrl.updatePrefs()">Update</button>
</div>
</form>
`;
export function prefsControlDirective() {
return {
restrict: 'E',
controller: PrefsControlCtrl,
bindToController: true,
controllerAs: 'ctrl',
template: template,
scope: {
mode: "@"
}
};
}
coreModule.directive('prefsControl', prefsControlDirective);
+51
View File
@@ -0,0 +1,51 @@
///<reference path="../../headers/common.d.ts" />
import config from 'app/core/config';
import {coreModule} from 'app/core/core';
import _ from 'lodash';
export class ProfileCtrl {
user: any;
old_theme: any;
orgs: any;
userForm: any;
/** @ngInject **/
constructor(private backendSrv, private contextSrv, private $location) {
this.getUser();
this.getUserOrgs();
}
getUser() {
this.backendSrv.get('/api/user').then(user => {
this.user = user;
this.user.theme = user.theme || 'dark';
});
}
getUserOrgs() {
this.backendSrv.get('/api/user/orgs').then(orgs => {
this.orgs = orgs;
});
}
setUsingOrg(org) {
this.backendSrv.post('/api/user/using/' + org.orgId).then(() => {
window.location.href = config.appSubUrl + '/profile';
});
}
update() {
if (!this.userForm.$valid) { return; }
this.backendSrv.put('/api/user/', this.user).then(() => {
this.contextSrv.user.name = this.user.name || this.user.login;
if (this.old_theme !== this.user.theme) {
window.location.href = config.appSubUrl + this.$location.path();
}
});
}
}
coreModule.controller('ProfileCtrl', ProfileCtrl);
@@ -65,7 +65,7 @@ class MetricsPanelCtrl extends PanelCtrl {
var data = this.panel.snapshotData;
// backward compatability
if (!_.isArray(data)) {
data = data;
data = data.data;
}
this.events.emit('data-snapshot-load', data);
@@ -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))
@@ -165,6 +166,7 @@ class MetricsPanelCtrl extends PanelCtrl {
issueQueries(datasource) {
this.updateTimeRange();
this.datasource = datasource;
if (!this.panel.targets || this.panel.targets.length === 0) {
return this.$q.when([]);
@@ -182,7 +184,6 @@ class MetricsPanelCtrl extends PanelCtrl {
cacheTimeout: this.panel.cacheTimeout
};
this.setTimeQueryStart();
return datasource.query(metricsQuery);
}
@@ -250,9 +251,12 @@ class MetricsPanelCtrl extends PanelCtrl {
}
addDataQuery(datasource) {
var target = {
datasource: datasource ? datasource.name : undefined
};
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>
+10 -3
View File
@@ -16,6 +16,8 @@ var defaults = {
jsonData: {}
};
var datasourceCreated = false;
export class DataSourceEditCtrl {
isNew: boolean;
datasources: any[];
@@ -66,6 +68,11 @@ export class DataSourceEditCtrl {
this.backendSrv.get('/api/datasources/' + id).then(ds => {
this.isNew = false;
this.current = ds;
if (datasourceCreated) {
datasourceCreated = false;
this.testDatasource();
}
return this.typeChanged();
});
}
@@ -123,14 +130,14 @@ export class DataSourceEditCtrl {
if (this.current.id) {
return this.backendSrv.put('/api/datasources/' + this.current.id, this.current).then(() => {
this.updateFrontendSettings().then(() => {
if (test) {
this.testDatasource();
}
this.testDatasource();
});
});
} else {
return this.backendSrv.post('/api/datasources', this.current).then(result => {
this.updateFrontendSettings();
datasourceCreated = true;
this.$location.path('datasources/edit/' + result.id);
});
}
@@ -25,7 +25,7 @@
<div ng-if="ctrl.tabIndex === 0" class="tab-content">
<form name="ctrl.editForm">
<form name="ctrl.editForm" ng-if="ctrl.current">
<div class="gf-form-group">
<div class="gf-form-inline">
<div class="gf-form">
@@ -34,7 +34,7 @@
</div>
<info-popover offset="0px -130px">
The name is used when you select the data source in panels.
The <code>Default</code> data source is preselected in new
The <em>Default</em> data source is preselected in new
panels.
</info-popover>
<gf-form-switch class="gf-form" label="Default" checked="ctrl.current.isDefault" switch-class="max-width-6"></gf-form-switch>
@@ -55,7 +55,6 @@
<div ng-if="ctrl.testing" style="margin-top: 25px">
<h5 ng-show="!ctrl.testing.done">Testing.... <i class="fa fa-spiner fa-spin"></i></h5>
<h5 ng-show="ctrl.testing.done">Test results</h5>
<div class="alert-{{ctrl.testing.status}} alert">
<div class="alert-title">{{ctrl.testing.title}}</div>
<div ng-bind='ctrl.testing.message'></div>
@@ -64,10 +63,7 @@
<div class="gf-form-button-row">
<button type="submit" class="btn btn-success" ng-show="ctrl.isNew" ng-click="ctrl.saveChanges()">Add</button>
<button type="submit" class="btn btn-success" ng-show="!ctrl.isNew" ng-click="ctrl.saveChanges()">Save</button>
<button type="submit" class="btn btn-secondary" ng-show="!ctrl.isNew" ng-click="ctrl.saveChanges(true)">
Test Connection
</button>
<button type="submit" class="btn btn-success" ng-show="!ctrl.isNew" ng-click="ctrl.saveChanges()">Save &amp; Test</button>
<button type="submit" class="btn btn-danger" ng-show="!ctrl.isNew" ng-click="ctrl.delete()">
Delete
</button>
@@ -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);
@@ -1,51 +0,0 @@
define([
'angular',
'app/core/config',
],
function (angular, config) {
'use strict';
var module = angular.module('grafana.controllers');
module.controller('ProfileCtrl', function($scope, backendSrv, contextSrv, $location) {
$scope.init = function() {
$scope.getUser();
$scope.getUserOrgs();
};
$scope.getUser = function() {
backendSrv.get('/api/user').then(function(user) {
$scope.user = user;
$scope.user.theme = user.theme || 'dark';
$scope.old_theme = $scope.user.theme;
});
};
$scope.getUserOrgs = function() {
backendSrv.get('/api/user/orgs').then(function(orgs) {
$scope.orgs = orgs;
});
};
$scope.setUsingOrg = function(org) {
backendSrv.post('/api/user/using/' + org.orgId).then(function() {
window.location.href = config.appSubUrl + '/profile';
});
};
$scope.update = function() {
if (!$scope.userForm.$valid) { return; }
backendSrv.put('/api/user/', $scope.user).then(function() {
contextSrv.user.name = $scope.user.name || $scope.user.login;
if ($scope.old_theme !== $scope.user.theme) {
window.location.href = config.appSubUrl + $location.path();
}
});
};
$scope.init();
});
});
@@ -272,22 +272,24 @@ 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);
if (!matches) { continue; }
if (matches.length > 1) {
value = matches[1];
text = value;
}
}
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;
}

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