diff --git a/pkg/api/api.go b/pkg/api/api.go
index e9d85dc0c8d..0d783968c57 100644
--- a/pkg/api/api.go
+++ b/pkg/api/api.go
@@ -185,7 +185,7 @@ func Register(r *macaron.Macaron) {
// rendering
r.Get("/render/*", reqSignedIn, RenderToPng)
- InitThirdPartyRoutes(r)
+ InitExternalPluginRoutes(r)
r.NotFound(NotFoundHandler)
}
diff --git a/pkg/api/thirdparty.go b/pkg/api/externalplugin.go
similarity index 67%
rename from pkg/api/thirdparty.go
rename to pkg/api/externalplugin.go
index 588c3b40ce7..e1ee5806b9e 100644
--- a/pkg/api/thirdparty.go
+++ b/pkg/api/externalplugin.go
@@ -3,16 +3,16 @@ package api
import (
"encoding/json"
"github.com/Unknwon/macaron"
+ "github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/util"
- "log"
"net/http"
"net/http/httputil"
"net/url"
)
-func InitThirdPartyRoutes(r *macaron.Macaron) {
+func InitExternalPluginRoutes(r *macaron.Macaron) {
/*
// Handle Auth and role requirements
if route.ReqSignedIn {
@@ -30,34 +30,33 @@ func InitThirdPartyRoutes(r *macaron.Macaron) {
}
}
*/
- for _, integration := range plugins.Integrations {
- log.Printf("adding routes for integration")
- for _, route := range integration.Routes {
- log.Printf("adding route %s %s", route.Method, route.Path)
- r.Route(util.JoinUrlFragments("/thirdparty/", route.Path), route.Method, ThirdParty(route.Url))
+ for _, plugin := range plugins.ExternalPlugins {
+ log.Info("adding routes for external plugin")
+ for _, route := range plugin.Settings.Routes {
+ log.Info("adding route %s /plugins%s", route.Method, route.Path)
+ r.Route(util.JoinUrlFragments("/plugins/", route.Path), route.Method, ExternalPlugin(route.Url))
}
}
}
-func ThirdParty(routeUrl string) macaron.Handler {
+func ExternalPlugin(routeUrl string) macaron.Handler {
return func(c *middleware.Context) {
path := c.Params("*")
//Create a HTTP header with the context in it.
ctx, err := json.Marshal(c.SignedInUser)
if err != nil {
- c.JsonApiErr(500, "Not found", err)
+ c.JsonApiErr(500, "failed to marshal context to json.", err)
return
}
- log.Printf(string(ctx))
targetUrl, _ := url.Parse(routeUrl)
- proxy := NewThirdPartyProxy(string(ctx), path, targetUrl)
+ proxy := NewExternalPluginProxy(string(ctx), path, targetUrl)
proxy.Transport = dataProxyTransport
proxy.ServeHTTP(c.RW(), c.Req.Request)
}
}
-func NewThirdPartyProxy(ctx string, proxyPath string, targetUrl *url.URL) *httputil.ReverseProxy {
+func NewExternalPluginProxy(ctx string, proxyPath string, targetUrl *url.URL) *httputil.ReverseProxy {
director := func(req *http.Request) {
req.URL.Scheme = targetUrl.Scheme
req.URL.Host = targetUrl.Host
diff --git a/pkg/api/index.go b/pkg/api/index.go
index 072878a2cfd..e41db285efc 100644
--- a/pkg/api/index.go
+++ b/pkg/api/index.go
@@ -52,25 +52,25 @@ func setIndexViewData(c *middleware.Context) error {
if setting.GoogleTagManagerId != "" {
c.Data["GoogleTagManagerId"] = setting.GoogleTagManagerId
}
- // This can be loaded from the DB/file to allow 3rdParty integration
- thirdPartyJs := make([]string, 0)
- thirdPartyCss := make([]string, 0)
- thirdPartyMenu := make([]*plugins.ThirdPartyMenuItem, 0)
- for _, integration := range plugins.Integrations {
- for _, js := range integration.Js {
- thirdPartyJs = append(thirdPartyJs, js.Src)
+
+ externalPluginJs := make([]string, 0)
+ externalPluginCss := make([]string, 0)
+ externalPluginMenu := make([]*plugins.ExternalPluginMenuItem, 0)
+ for _, plugin := range plugins.ExternalPlugins {
+ for _, js := range plugin.Settings.Js {
+ externalPluginJs = append(externalPluginJs, js.Src)
}
- for _, css := range integration.Css {
- thirdPartyCss = append(thirdPartyCss, css.Href)
+ for _, css := range plugin.Settings.Css {
+ externalPluginCss = append(externalPluginCss, css.Href)
}
- for _, item := range integration.MenuItems {
- thirdPartyMenu = append(thirdPartyMenu, item)
+ for _, item := range plugin.Settings.MenuItems {
+ externalPluginMenu = append(externalPluginMenu, item)
}
}
- c.Data["ThirdPartyJs"] = thirdPartyJs
- c.Data["ThirdPartyCss"] = thirdPartyCss
- c.Data["ThirdPartyMenu"] = thirdPartyMenu
+ c.Data["ExternalPluginJs"] = externalPluginJs
+ c.Data["ExternalPluginCss"] = externalPluginCss
+ c.Data["ExternalPluginMenu"] = externalPluginMenu
return nil
}
diff --git a/pkg/models/third_party.go b/pkg/models/external_plugin.go
similarity index 52%
rename from pkg/models/third_party.go
rename to pkg/models/external_plugin.go
index 0bce0d86f8f..a5fab329fb1 100644
--- a/pkg/models/third_party.go
+++ b/pkg/models/external_plugin.go
@@ -1,6 +1,6 @@
package models
-type ThirdPartyRoute struct {
+type ExternalPluginRoute struct {
Path string `json:"path"`
Method string `json:"method"`
ReqSignedIn bool `json:"req_signed_in"`
@@ -9,23 +9,23 @@ type ThirdPartyRoute struct {
Url string `json:"url"`
}
-type ThirdPartyJs struct {
+type ExternalPluginJs struct {
src string `json:"src"`
}
-type ThirdPartyMenuItem struct {
+type ExternalPluginMenuItem struct {
Text string `json:"text"`
Icon string `json:"icon"`
Href string `json:"href"`
}
-type ThirdPartyCss struct {
+type ExternalPluginCss struct {
Href string `json:"href"`
}
-type ThirdPartyIntegration struct {
- Routes []*ThirdPartyRoute `json:"routes"`
- Js []*ThirdPartyJs `json:"js"`
- Css []*ThirdPartyCss `json:"css"`
- MenuItems []*ThirdPartyMenuItem `json:"menu_items"`
+type ExternalPluginIntegration struct {
+ Routes []*ExternalPluginRoute `json:"routes"`
+ Js []*ExternalPluginJs `json:"js"`
+ Css []*ExternalPluginCss `json:"css"`
+ MenuItems []*ExternalPluginMenuItem `json:"menu_items"`
}
diff --git a/pkg/plugins/plugins.go b/pkg/plugins/plugins.go
index c411a47a977..73c642176bb 100644
--- a/pkg/plugins/plugins.go
+++ b/pkg/plugins/plugins.go
@@ -16,7 +16,7 @@ type PluginMeta struct {
Name string `json:"name"`
}
-type ThirdPartyRoute struct {
+type ExternalPluginRoute struct {
Path string `json:"path"`
Method string `json:"method"`
ReqSignedIn bool `json:"req_signed_in"`
@@ -25,35 +25,35 @@ type ThirdPartyRoute struct {
Url string `json:"url"`
}
-type ThirdPartyJs struct {
+type ExternalPluginJs struct {
Src string `json:"src"`
}
-type ThirdPartyMenuItem struct {
+type ExternalPluginMenuItem struct {
Text string `json:"text"`
Icon string `json:"icon"`
Href string `json:"href"`
}
-type ThirdPartyCss struct {
+type ExternalPluginCss struct {
Href string `json:"href"`
}
-type ThirdPartyIntegration struct {
- Routes []*ThirdPartyRoute `json:"routes"`
- Js []*ThirdPartyJs `json:"js"`
- Css []*ThirdPartyCss `json:"css"`
- MenuItems []*ThirdPartyMenuItem `json:"menu_items"`
+type ExternalPluginSettings struct {
+ Routes []*ExternalPluginRoute `json:"routes"`
+ Js []*ExternalPluginJs `json:"js"`
+ Css []*ExternalPluginCss `json:"css"`
+ MenuItems []*ExternalPluginMenuItem `json:"menu_items"`
}
-type ThirdPartyPlugin struct {
- PluginType string `json:"pluginType"`
- Integration ThirdPartyIntegration `json:"integration"`
+type ExternalPlugin struct {
+ PluginType string `json:"pluginType"`
+ Settings ExternalPluginSettings `json:"settings"`
}
var (
- DataSources map[string]interface{}
- Integrations []ThirdPartyIntegration
+ DataSources map[string]interface{}
+ ExternalPlugins []ExternalPlugin
)
type PluginScanner struct {
@@ -67,7 +67,7 @@ func Init() {
func scan(pluginDir string) error {
DataSources = make(map[string]interface{})
- Integrations = make([]ThirdPartyIntegration, 0)
+ ExternalPlugins = make([]ExternalPlugin, 0)
scanner := &PluginScanner{
pluginPath: pluginDir,
@@ -131,13 +131,13 @@ func (scanner *PluginScanner) loadPluginJson(path string) error {
DataSources[datasourceType.(string)] = pluginJson
}
- if pluginType == "thirdPartyIntegration" {
- p := ThirdPartyPlugin{}
+ if pluginType == "externalPlugin" {
+ p := ExternalPlugin{}
reader.Seek(0, 0)
if err := jsonParser.Decode(&p); err != nil {
return err
}
- Integrations = append(Integrations, p.Integration)
+ ExternalPlugins = append(ExternalPlugins, p)
}
return nil
diff --git a/public/app/controllers/sidemenuCtrl.js b/public/app/controllers/sidemenuCtrl.js
index bd6538b15cb..64fe3763aeb 100644
--- a/public/app/controllers/sidemenuCtrl.js
+++ b/public/app/controllers/sidemenuCtrl.js
@@ -30,13 +30,15 @@ function (angular, _, $, config) {
});
}
- if (_.isArray(window.thirdParty.MainLinks)) {
- _.forEach(window.thirdParty.MainLinks, function(item) {
- $scope.mainLinks.push({
- text: item.text,
- icon: item.icon,
- href: $scope.getUrl(item.href)
- });
+ if (_.isArray(window.externalPlugins.MainLinks)) {
+ _.forEach(window.externalPlugins.MainLinks, function(item) {
+ if (!item.adminOnly || contextSrv.hasRole('Admin')) {
+ $scope.mainLinks.push({
+ text: item.text,
+ icon: item.icon,
+ href: $scope.getUrl(item.href)
+ });
+ }
});
}
};
diff --git a/public/app/plugins/externalPlugins/example/README.TXT b/public/app/plugins/externalPlugins/example/README.TXT
new file mode 100644
index 00000000000..0963375e9fe
--- /dev/null
+++ b/public/app/plugins/externalPlugins/example/README.TXT
@@ -0,0 +1,3 @@
+Example app is available at https://github.com/raintank/grafana-plugin-example
+
+To use, download the example app from github and run it (requires python Flask). Then rename the "_plugin.json" file in this director to "plugin.json" and restart Grafana.
diff --git a/public/app/plugins/externalPlugins/example/_plugin.json b/public/app/plugins/externalPlugins/example/_plugin.json
new file mode 100644
index 00000000000..a8469eadd62
--- /dev/null
+++ b/public/app/plugins/externalPlugins/example/_plugin.json
@@ -0,0 +1,41 @@
+{
+ "pluginType": "externalPlugin",
+ "settings": {
+ "routes": [
+ {
+ "path": "/example/static/*",
+ "method": "*",
+ "req_signed_in": false,
+ "req_grafana_admin": false,
+ "req_role": "Admin",
+ "url": "http://localhost:5000/static"
+ },
+ {
+ "path": "/example/api/*",
+ "method": "*",
+ "req_signed_in": true,
+ "req_grafana_admin": false,
+ "req_role": "Admin",
+ "url": "http://localhost:5000/api"
+ }
+ ],
+ "css": [
+ {
+ "href": "/example/static/css/example.css"
+ }
+ ],
+ "js": [
+ {
+ "src": "/example/static/js/app.js"
+ }
+ ],
+ "menu_items": [
+ {
+ "text": "Example Plugin",
+ "icon": "fa fa-fw fa-smile-o",
+ "href": "/example/servers",
+ "adminOnly": false,
+ }
+ ]
+ }
+}
diff --git a/public/app/plugins/thirdPartyIntegration/raintank/plugin.json b/public/app/plugins/thirdPartyIntegration/raintank/plugin.json
deleted file mode 100644
index 7c1dcb0dcee..00000000000
--- a/public/app/plugins/thirdPartyIntegration/raintank/plugin.json
+++ /dev/null
@@ -1,40 +0,0 @@
-{
- "pluginType": "thirdPartyIntegration",
- "integration": {
- "routes": [
- {
- "path": "/raintank/public/*",
- "method": "*",
- "req_signed_in": false,
- "req_grafana_admin": false,
- "req_role": "Admin",
- "url": "http://localhost:3001/public"
- },
- {
- "path": "/raintank/api/*",
- "method": "*",
- "req_signed_in": true,
- "req_grafana_admin": false,
- "req_role": "Admin",
- "url": "http://localhost:3001/api"
- }
- ],
- "css": [
- {
- "href": "/path/to/file.css"
- }
- ],
- "js": [
- {
- "src": "/raintank/public/app.js"
- }
- ],
- "menu_items": [
- {
- "text": "Menu Text",
- "icon": "fa fa-fw fa-database",
- "href": "/raintank/test"
- }
- ]
- }
-}
diff --git a/public/views/index.html b/public/views/index.html
index 9998b0d2b68..5dc46550fef 100644
--- a/public/views/index.html
+++ b/public/views/index.html
@@ -13,8 +13,8 @@
[[else]]
[[end]]
- [[ range $css := .ThirdPartyCss ]]
-
+ [[ range $css := .ExternalPluginCss ]]
+
[[ end ]]
@@ -56,16 +56,16 @@
settings: [[.Settings]],
};
- window.thirdParty = {
- MainLinks: [[.ThirdPartyMenu]]
+ window.externalPlugins = {
+ MainLinks: [[.ExternalPluginMenu]]
};
require(['app/app'], function (app) {
app.boot();
})
- [[ range $js := .ThirdPartyJs]]
-
+ [[ range $js := .ExternalPluginJs]]
+
[[ end ]]
[[if .GoogleAnalyticsId]]