diff --git a/pkg/api/api.go b/pkg/api/api.go
index dc26a6f4601..0c14f45778f 100644
--- a/pkg/api/api.go
+++ b/pkg/api/api.go
@@ -41,6 +41,9 @@ func Register(r *macaron.Macaron) {
r.Get("/admin/orgs", reqGrafanaAdmin, Index)
r.Get("/admin/orgs/edit/:id", reqGrafanaAdmin, Index)
+ r.Get("/plugins", reqSignedIn, Index)
+ r.Get("/plugins/edit/*", reqSignedIn, Index)
+
r.Get("/dashboard/*", reqSignedIn, Index)
r.Get("/dashboard-solo/*", reqSignedIn, Index)
diff --git a/pkg/api/index.go b/pkg/api/index.go
index 357aa7d335c..a4f5ac69928 100644
--- a/pkg/api/index.go
+++ b/pkg/api/index.go
@@ -60,6 +60,10 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
Text: "Data Sources",
Icon: "fa fa-fw fa-database",
Href: "/datasources",
+ }, &dtos.NavLink{
+ Text: "Plugins",
+ Icon: "fa fa-fw fa-cubes",
+ Href: "/plugins",
})
}
diff --git a/public/app/core/routes/all.js b/public/app/core/routes/all.js
index a7e36a0e228..fd029a27939 100644
--- a/public/app/core/routes/all.js
+++ b/public/app/core/routes/all.js
@@ -131,6 +131,16 @@ define([
templateUrl: 'app/partials/reset_password.html',
controller : 'ResetPasswordCtrl',
})
+ .when('/plugins', {
+ templateUrl: 'app/features/org/partials/plugins.html',
+ controller: 'PluginsCtrl',
+ resolve: loadOrgBundle,
+ })
+ .when('/plugins/edit/:type', {
+ templateUrl: 'app/features/org/partials/pluginEdit.html',
+ controller: 'PluginEditCtrl',
+ resolve: loadOrgBundle,
+ })
.otherwise({
templateUrl: 'app/partials/error.html',
controller: 'ErrorCtrl'
diff --git a/public/app/features/org/all.js b/public/app/features/org/all.js
index d03d270709d..be9668e33de 100644
--- a/public/app/features/org/all.js
+++ b/public/app/features/org/all.js
@@ -6,4 +6,8 @@ define([
'./userInviteCtrl',
'./orgApiKeysCtrl',
'./orgDetailsCtrl',
+ './pluginsCtrl',
+ './pluginEditCtrl',
+ './plugin_srv',
+ './plugin_directive',
], function () {});
diff --git a/public/app/features/org/partials/pluginConfigCore.html b/public/app/features/org/partials/pluginConfigCore.html
new file mode 100644
index 00000000000..1b13b46d0e5
--- /dev/null
+++ b/public/app/features/org/partials/pluginConfigCore.html
@@ -0,0 +1,3 @@
+
+{{current.type}} plugin does not have any additional config.
+
diff --git a/public/app/features/org/partials/pluginEdit.html b/public/app/features/org/partials/pluginEdit.html
new file mode 100644
index 00000000000..d77eb5ebd6c
--- /dev/null
+++ b/public/app/features/org/partials/pluginEdit.html
@@ -0,0 +1,42 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/public/app/features/org/partials/plugins.html b/public/app/features/org/partials/plugins.html
new file mode 100644
index 00000000000..97949649094
--- /dev/null
+++ b/public/app/features/org/partials/plugins.html
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
Plugins
+
+
+ No plugins defined
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public/app/features/org/pluginEditCtrl.js b/public/app/features/org/pluginEditCtrl.js
new file mode 100644
index 00000000000..b4ba2c05653
--- /dev/null
+++ b/public/app/features/org/pluginEditCtrl.js
@@ -0,0 +1,35 @@
+define([
+ 'angular',
+ 'lodash',
+ 'app/core/config',
+],
+function (angular, _, config) {
+ 'use strict';
+
+ var module = angular.module('grafana.controllers');
+
+ module.controller('PluginEditCtrl', function($scope, pluginSrv, $routeParams) {
+ $scope.init = function() {
+ $scope.current = {};
+ $scope.getPlugins();
+ };
+
+ $scope.getPlugins = function() {
+ pluginSrv.get($routeParams.type).then(function(result) {
+ $scope.current = _.clone(result);
+ });
+ };
+
+ $scope.update = function() {
+ $scope._update();
+ };
+
+ $scope._update = function() {
+ pluginSrv.update($scope.current).then(function() {
+ window.location.href = config.appSubUrl + "plugins";
+ });
+ };
+
+ $scope.init();
+ });
+});
\ No newline at end of file
diff --git a/public/app/features/org/plugin_directive.js b/public/app/features/org/plugin_directive.js
new file mode 100644
index 00000000000..8862f5580e7
--- /dev/null
+++ b/public/app/features/org/plugin_directive.js
@@ -0,0 +1,43 @@
+define([
+ 'angular',
+],
+function (angular) {
+ 'use strict';
+
+ var module = angular.module('grafana.directives');
+
+ module.directive('pluginConfigLoader', function($compile) {
+ return {
+ restrict: 'E',
+ link: function(scope, elem) {
+ var directive = 'grafana-plugin-core';
+ if (scope.current.module) {
+ directive = 'grafana-plugin-'+scope.current.type;
+ }
+ scope.require([scope.current.module], function () {
+ var panelEl = angular.element(document.createElement(directive));
+ elem.append(panelEl);
+ $compile(panelEl)(scope);
+ });
+ }
+ };
+ });
+
+ module.directive('grafanaPluginCore', function() {
+ return {
+ restrict: 'E',
+ templateUrl: 'app/features/org/partials/pluginConfigCore.html',
+ transclude: true,
+ link: function(scope, elem) {
+ console.log("grafana plugin core", scope, elem);
+ scope.update = function() {
+ //Perform custom save events to the plugins own backend if needed.
+
+ // call parent update to commit the change to the plugin object.
+ // this will cause the page to reload.
+ scope._update();
+ };
+ }
+ };
+ });
+});
\ No newline at end of file
diff --git a/public/app/features/org/plugin_srv.js b/public/app/features/org/plugin_srv.js
new file mode 100644
index 00000000000..863828c7659
--- /dev/null
+++ b/public/app/features/org/plugin_srv.js
@@ -0,0 +1,58 @@
+define([
+ 'angular',
+ 'lodash',
+],
+function (angular, _) {
+ 'use strict';
+
+ var module = angular.module('grafana.services');
+
+ module.service('pluginSrv', function($rootScope, $timeout, $q, backendSrv) {
+ var self = this;
+ this.init = function() {
+ console.log("pluginSrv init");
+ this.plugins = {};
+ };
+
+ this.get = function(type) {
+ return $q(function(resolve) {
+ if (type in self.plugins) {
+ return resolve(self.plugins[type]);
+ }
+ backendSrv.get('/api/plugins').then(function(results) {
+ _.forEach(results, function(p) {
+ self.plugins[p.type] = p;
+ });
+ return resolve(self.plugins[type]);
+ });
+ });
+ };
+
+ this.getAll = function() {
+ return $q(function(resolve) {
+ if (!_.isEmpty(self.plugins)) {
+ return resolve(self.plugins);
+ }
+ backendSrv.get('api/plugins').then(function(results) {
+ _.forEach(results, function(p) {
+ self.plugins[p.type] = p;
+ });
+ return resolve(self.plugins);
+ });
+ });
+ };
+
+ this.update = function(plugin) {
+ return $q(function(resolve, reject) {
+ backendSrv.post('/api/plugins', plugin).then(function(resp) {
+ self.plugins[plugin.type] = plugin;
+ resolve(resp);
+ }, function(resp) {
+ reject(resp);
+ });
+ });
+ };
+
+ this.init();
+ });
+});
diff --git a/public/app/features/org/pluginsCtrl.js b/public/app/features/org/pluginsCtrl.js
new file mode 100644
index 00000000000..49dc104a840
--- /dev/null
+++ b/public/app/features/org/pluginsCtrl.js
@@ -0,0 +1,33 @@
+define([
+ 'angular',
+ 'app/core/config',
+],
+function (angular, config) {
+ 'use strict';
+
+ var module = angular.module('grafana.controllers');
+
+ module.controller('PluginsCtrl', function($scope, $location, pluginSrv) {
+
+ $scope.init = function() {
+ $scope.plugins = {};
+ $scope.getPlugins();
+ };
+
+ $scope.getPlugins = function() {
+ pluginSrv.getAll().then(function(result) {
+ console.log(result);
+ $scope.plugins = result;
+ });
+ };
+
+ $scope.update = function(plugin) {
+ pluginSrv.update(plugin).then(function() {
+ window.location.href = config.appSubUrl + $location.path();
+ });
+ };
+
+ $scope.init();
+
+ });
+});
\ No newline at end of file