diff --git a/pkg/api/api.go b/pkg/api/api.go index d4d23b17ef4..4024655775e 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -209,7 +209,7 @@ func (hs *HttpServer) registerRoutes() { r.Get("/plugins", wrap(GetPluginList)) r.Get("/plugins/:pluginId/settings", wrap(GetPluginSettingById)) - r.Get("/plugins/:pluginId/readme", wrap(GetPluginReadme)) + r.Get("/plugins/:pluginId/markdown/:name", wrap(GetPluginMarkdown)) r.Group("/plugins", func() { r.Get("/:pluginId/dashboards/", wrap(GetPluginDashboards)) diff --git a/pkg/api/plugins.go b/pkg/api/plugins.go index fe7122f8557..042c03f9832 100644 --- a/pkg/api/plugins.go +++ b/pkg/api/plugins.go @@ -147,15 +147,16 @@ func GetPluginDashboards(c *middleware.Context) Response { } } -func GetPluginReadme(c *middleware.Context) Response { +func GetPluginMarkdown(c *middleware.Context) Response { pluginId := c.Params(":pluginId") + name := c.Params(":name") - if content, err := plugins.GetPluginReadme(pluginId); err != nil { + if content, err := plugins.GetPluginMarkdown(pluginId, name); err != nil { if notfound, ok := err.(plugins.PluginNotFoundError); ok { return ApiError(404, notfound.Error(), nil) } - return ApiError(500, "Could not get readme", err) + return ApiError(500, "Could not get markdown file", err) } else { return Respond(200, content) } diff --git a/pkg/plugins/datasource_plugin.go b/pkg/plugins/datasource_plugin.go index 8cb0748936b..e62157a7a13 100644 --- a/pkg/plugins/datasource_plugin.go +++ b/pkg/plugins/datasource_plugin.go @@ -1,15 +1,22 @@ package plugins -import "encoding/json" +import ( + "encoding/json" + "os" + "path/filepath" +) type DataSourcePlugin struct { FrontendPluginBase - Annotations bool `json:"annotations"` - Metrics bool `json:"metrics"` - Alerting bool `json:"alerting"` - BuiltIn bool `json:"builtIn"` - Mixed bool `json:"mixed"` - Routes []*AppPluginRoute `json:"routes"` + Annotations bool `json:"annotations"` + Metrics bool `json:"metrics"` + Alerting bool `json:"alerting"` + QueryOptions map[string]bool `json:"queryOptions,omitempty"` + BuiltIn bool `json:"builtIn,omitempty"` + Mixed bool `json:"mixed,omitempty"` + HasQueryHelp bool `json:"hasQueryHelp,omitempty"` + + Routes []*AppPluginRoute `json:"-"` } func (p *DataSourcePlugin) Load(decoder *json.Decoder, pluginDir string) error { @@ -21,6 +28,15 @@ func (p *DataSourcePlugin) Load(decoder *json.Decoder, pluginDir string) error { return err } + // look for help markdown + helpPath := filepath.Join(p.PluginDir, "QUERY_HELP.md") + if _, err := os.Stat(helpPath); os.IsNotExist(err) { + helpPath = filepath.Join(p.PluginDir, "query_help.md") + } + if _, err := os.Stat(helpPath); err == nil { + p.HasQueryHelp = true + } + DataSources[p.Id] = p return nil } diff --git a/pkg/plugins/models.go b/pkg/plugins/models.go index fd04852f2f4..541b37c8a8a 100644 --- a/pkg/plugins/models.go +++ b/pkg/plugins/models.go @@ -38,8 +38,8 @@ type PluginBase struct { Includes []*PluginInclude `json:"includes"` Module string `json:"module"` BaseUrl string `json:"baseUrl"` - HideFromList bool `json:"hideFromList"` - State string `json:"state"` + HideFromList bool `json:"hideFromList,omitempty"` + State string `json:"state,omitempty"` IncludedInAppId string `json:"-"` PluginDir string `json:"-"` @@ -48,9 +48,6 @@ type PluginBase struct { GrafanaNetVersion string `json:"-"` GrafanaNetHasUpdate bool `json:"-"` - - // cache for readme file contents - Readme []byte `json:"-"` } func (pb *PluginBase) registerPlugin(pluginDir string) error { diff --git a/pkg/plugins/plugins.go b/pkg/plugins/plugins.go index b6c3639cbbf..885bd5c9e03 100644 --- a/pkg/plugins/plugins.go +++ b/pkg/plugins/plugins.go @@ -3,6 +3,7 @@ package plugins import ( "encoding/json" "errors" + "fmt" "io/ioutil" "os" "path" @@ -166,30 +167,24 @@ func (scanner *PluginScanner) loadPluginJson(pluginJsonFilePath string) error { return loader.Load(jsonParser, currentDir) } -func GetPluginReadme(pluginId string) ([]byte, error) { +func GetPluginMarkdown(pluginId string, name string) ([]byte, error) { plug, exists := Plugins[pluginId] if !exists { return nil, PluginNotFoundError{pluginId} } - if plug.Readme != nil { - return plug.Readme, nil + path := filepath.Join(plug.PluginDir, fmt.Sprintf("%s.md", strings.ToUpper(name))) + if _, err := os.Stat(path); os.IsNotExist(err) { + path = filepath.Join(plug.PluginDir, fmt.Sprintf("%s.md", strings.ToLower(name))) } - readmePath := filepath.Join(plug.PluginDir, "README.md") - if _, err := os.Stat(readmePath); os.IsNotExist(err) { - readmePath = filepath.Join(plug.PluginDir, "readme.md") + if _, err := os.Stat(path); os.IsNotExist(err) { + return make([]byte, 0), nil } - if _, err := os.Stat(readmePath); os.IsNotExist(err) { - plug.Readme = make([]byte, 0) - return plug.Readme, nil - } - - if readmeBytes, err := ioutil.ReadFile(readmePath); err != nil { + if data, err := ioutil.ReadFile(path); err != nil { return nil, err } else { - plug.Readme = readmeBytes - return plug.Readme, nil + return data, nil } } diff --git a/public/app/core/components/code_editor/code_editor.ts b/public/app/core/components/code_editor/code_editor.ts index d2d32d1f46e..151ebffab5d 100644 --- a/public/app/core/components/code_editor/code_editor.ts +++ b/public/app/core/components/code_editor/code_editor.ts @@ -159,7 +159,6 @@ function link(scope, elem, attrs) { enableSnippets: true }); - console.log('getting completer', lang); if (scope.getCompleter()) { // make copy of array as ace seems to share completers array between instances codeEditor.completers = codeEditor.completers.slice(); diff --git a/public/app/core/core.ts b/public/app/core/core.ts index f7ebb614d2a..6a5702d1f12 100644 --- a/public/app/core/core.ts +++ b/public/app/core/core.ts @@ -20,6 +20,7 @@ import './jquery_extended'; import './partials'; import './components/jsontree/jsontree'; import './components/code_editor/code_editor'; +import './utils/outline'; import {grafanaAppDirective} from './components/grafana_app'; import {sideMenuDirective} from './components/sidemenu/sidemenu'; diff --git a/public/app/core/utils/kbn.js b/public/app/core/utils/kbn.js index 6f8340d5940..37888fb10ac 100644 --- a/public/app/core/utils/kbn.js +++ b/public/app/core/utils/kbn.js @@ -163,21 +163,15 @@ function($, _) { ms: 0.001 }; - kbn.calculateInterval = function(range, resolution, userInterval) { + kbn.calculateInterval = function(range, resolution, lowLimitInterval) { var lowLimitMs = 1; // 1 millisecond default low limit - var intervalMs, lowLimitInterval; + var intervalMs; - if (userInterval) { - if (userInterval[0] === '>') { - lowLimitInterval = userInterval.slice(1); - lowLimitMs = kbn.interval_to_ms(lowLimitInterval); - } - else { - return { - intervalMs: kbn.interval_to_ms(userInterval), - interval: userInterval, - }; + if (lowLimitInterval) { + if (lowLimitInterval[0] === '>') { + lowLimitInterval = lowLimitInterval.slice(1); } + lowLimitMs = kbn.interval_to_ms(lowLimitInterval); } intervalMs = kbn.round_interval((range.to.valueOf() - range.from.valueOf()) / resolution); diff --git a/public/app/core/utils/outline.js b/public/app/core/utils/outline.js new file mode 100644 index 00000000000..f968b3e51f9 --- /dev/null +++ b/public/app/core/utils/outline.js @@ -0,0 +1,32 @@ +// outline.js +// based on http://www.paciellogroup.com/blog/2012/04/how-to-remove-css-outlines-in-an-accessible-manner/ +(function(d) { + "use strict"; + + var style_element = d.createElement('STYLE'), + dom_events = 'addEventListener' in d, + add_event_listener = function(type, callback) { + // Basic cross-browser event handling + if(dom_events){ + d.addEventListener(type, callback); + } else { + d.attachEvent('on' + type, callback); + } + }, + set_css = function(css_text) { + // Handle setting of