diff --git a/conf/defaults.ini b/conf/defaults.ini index 750cc23a329..74ae209ac9a 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -670,7 +670,7 @@ disable_total_stats = false basic_auth_username = basic_auth_password = -# Metrics environment info adds dimensions to the `grafana_environment_info` metric, which +# Metrics environment info adds dimensions to the `grafana_environment_info` metric, which # can expose more information about the Grafana instance. [metrics.environment_info] #exampleLabel1 = exampleValue1 @@ -768,6 +768,7 @@ enable_alpha = false app_tls_skip_verify_insecure = false # Enter a comma-separated list of plugin identifiers to identify plugins that are allowed to be loaded even if they lack a valid signature. allow_loading_unsigned_plugins = +marketplace_url = https://grafana.com/grafana/plugins/ #################################### Grafana Image Renderer Plugin ########################## [plugin.grafana-image-renderer] diff --git a/conf/sample.ini b/conf/sample.ini index 1ebd84a236f..23691037135 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -664,7 +664,7 @@ ; basic_auth_username = ; basic_auth_password = -# Metrics environment info adds dimensions to the `grafana_environment_info` metric, which +# Metrics environment info adds dimensions to the `grafana_environment_info` metric, which # can expose more information about the Grafana instance. [metrics.environment_info] #exampleLabel1 = exampleValue1 @@ -756,6 +756,7 @@ ;app_tls_skip_verify_insecure = false # Enter a comma-separated list of plugin identifiers to identify plugins that are allowed to be loaded even if they lack a valid signature. ;allow_loading_unsigned_plugins = +;marketplace_url = https://grafana.com/grafana/plugins/ #################################### Grafana Image Renderer Plugin ########################## [plugin.grafana-image-renderer] diff --git a/docs/sources/administration/configuration.md b/docs/sources/administration/configuration.md index e7bbdcb8827..f417b95e5f8 100644 --- a/docs/sources/administration/configuration.md +++ b/docs/sources/administration/configuration.md @@ -1336,6 +1336,10 @@ Set to `true` if you want to test alpha plugins that are not yet ready for gener Enter a comma-separated list of plugin identifiers to identify plugins that are allowed to be loaded even if they lack a valid signature. +### marketplace_url + +Custom install/learn more url for enterprise plugins. Defaults to https://grafana.com/grafana/plugins/. +
## [plugin.grafana-image-renderer] diff --git a/packages/grafana-data/src/types/config.ts b/packages/grafana-data/src/types/config.ts index c41a35ae48a..d2cf27cc653 100644 --- a/packages/grafana-data/src/types/config.ts +++ b/packages/grafana-data/src/types/config.ts @@ -55,6 +55,8 @@ export interface LicenseInfo { expiry: number; licenseUrl: string; stateInfo: string; + hasValidLicense: boolean; + edition: string; } /** diff --git a/packages/grafana-data/src/types/datasource.ts b/packages/grafana-data/src/types/datasource.ts index 2f6bbb9bda2..de7345ba574 100644 --- a/packages/grafana-data/src/types/datasource.ts +++ b/packages/grafana-data/src/types/datasource.ts @@ -114,6 +114,7 @@ export interface DataSourcePluginMeta extends PluginMet queryOptions?: PluginMetaQueryOptions; sort?: number; streaming?: boolean; + unlicensed?: boolean; } interface PluginMetaQueryOptions { diff --git a/packages/grafana-runtime/src/config.ts b/packages/grafana-runtime/src/config.ts index 8f1cff5abbd..fa598ec6cd6 100644 --- a/packages/grafana-runtime/src/config.ts +++ b/packages/grafana-runtime/src/config.ts @@ -62,6 +62,7 @@ export class GrafanaBootConfig implements GrafanaConfig { rendererAvailable = false; http2Enabled = false; dateFormats?: SystemDateFormatSettings; + marketplaceUrl?: string; constructor(options: GrafanaBootConfig) { this.theme = options.bootData.user.lightTheme ? getTheme(GrafanaThemeType.Light) : getTheme(GrafanaThemeType.Dark); diff --git a/pkg/api/frontendsettings.go b/pkg/api/frontendsettings.go index f7e3f674072..139fccdeca3 100644 --- a/pkg/api/frontendsettings.go +++ b/pkg/api/frontendsettings.go @@ -230,14 +230,17 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *models.ReqContext) (map[string]i "isEnterprise": hs.License.HasValidLicense(), }, "licenseInfo": map[string]interface{}{ - "hasLicense": hs.License.HasLicense(), - "expiry": hs.License.Expiry(), - "stateInfo": hs.License.StateInfo(), - "licenseUrl": hs.License.LicenseURL(c.SignedInUser), + "hasLicense": hs.License.HasLicense(), + "hasValidLicense": hs.License.HasValidLicense(), + "expiry": hs.License.Expiry(), + "stateInfo": hs.License.StateInfo(), + "licenseUrl": hs.License.LicenseURL(c.SignedInUser), + "edition": hs.License.Edition(), }, "featureToggles": hs.Cfg.FeatureToggles, "rendererAvailable": hs.RenderService.IsAvailable(), "http2Enabled": hs.Cfg.Protocol == setting.HTTP2Scheme, + "marketplaceUrl": hs.Cfg.MarketplaceURL, } return jsonObj, nil diff --git a/pkg/api/plugins.go b/pkg/api/plugins.go index 838243b099c..04e2d9a1af6 100644 --- a/pkg/api/plugins.go +++ b/pkg/api/plugins.go @@ -8,7 +8,6 @@ import ( "time" "github.com/grafana/grafana-plugin-sdk-go/backend" - "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/models" diff --git a/pkg/services/licensing/oss.go b/pkg/services/licensing/oss.go index 0e967840036..423903d59d2 100644 --- a/pkg/services/licensing/oss.go +++ b/pkg/services/licensing/oss.go @@ -7,6 +7,10 @@ import ( "github.com/grafana/grafana/pkg/setting" ) +const ( + openSource = "Open Source" +) + type OSSLicensingService struct { Cfg *setting.Cfg `inject:""` HooksService *hooks.HooksService `inject:""` @@ -21,7 +25,7 @@ func (*OSSLicensingService) Expiry() int64 { } func (*OSSLicensingService) Edition() string { - return "Open Source" + return openSource } func (*OSSLicensingService) StateInfo() string { diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index 99864a7bb47..bf00f0689a8 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -273,6 +273,7 @@ type Cfg struct { PluginsAppsSkipVerifyTLS bool PluginSettings PluginSettings PluginsAllowUnsigned []string + MarketplaceURL string DisableSanitizeHtml bool EnterpriseLicensePath string @@ -796,6 +797,7 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error { plug = strings.TrimSpace(plug) cfg.PluginsAllowUnsigned = append(cfg.PluginsAllowUnsigned, plug) } + cfg.MarketplaceURL = pluginsSection.Key("marketplace_url").MustString("https://grafana.com/grafana/plugins/") cfg.Protocol = Protocol // Read and populate feature toggles list diff --git a/public/app/features/datasources/NewDataSourcePage.tsx b/public/app/features/datasources/NewDataSourcePage.tsx index c809291b005..0f52e71370d 100644 --- a/public/app/features/datasources/NewDataSourcePage.tsx +++ b/public/app/features/datasources/NewDataSourcePage.tsx @@ -129,10 +129,9 @@ interface DataSourceTypeCardProps { const DataSourceTypeCard: FC = props => { const { plugin, onLearnMoreClick } = props; const isPhantom = plugin.module === 'phantom'; - const onClick = !isPhantom ? props.onClick : () => {}; - + const onClick = !isPhantom && !plugin.unlicensed ? props.onClick : () => {}; // find first plugin info link - const learnMoreLink = plugin.info.links && plugin.info.links.length > 0 ? plugin.info.links[0] : null; + const learnMoreLink = plugin.info?.links?.length > 0 ? plugin.info.links[0] : null; return ( = props => { {learnMoreLink.name} )} - {!isPhantom && } + {!isPhantom && } } labels={ diff --git a/public/app/features/datasources/state/buildCategories.ts b/public/app/features/datasources/state/buildCategories.ts index f2235d94fdf..a701b5f02f8 100644 --- a/public/app/features/datasources/state/buildCategories.ts +++ b/public/app/features/datasources/state/buildCategories.ts @@ -1,5 +1,6 @@ import { DataSourcePluginMeta, PluginType } from '@grafana/data'; import { DataSourcePluginCategory } from 'app/types'; +import { config } from '../../../core/config'; export function buildCategories(plugins: DataSourcePluginMeta[]): DataSourcePluginCategory[] { const categories: DataSourcePluginCategory[] = [ @@ -22,10 +23,15 @@ export function buildCategories(plugins: DataSourcePluginMeta[]): DataSourcePlug categoryIndex[category.id] = category; } + const { edition, hasValidLicense } = config.licenseInfo; + for (const plugin of plugins) { + const enterprisePlugin = enterprisePlugins.find(item => item.id === plugin.id); // Force category for enterprise plugins - if (plugin.enterprise || enterprisePlugins.find(item => item.id === plugin.id)) { + if (plugin.enterprise || enterprisePlugin) { plugin.category = 'enterprise'; + plugin.unlicensed = edition !== 'Open Source' && !hasValidLicense; + plugin.info.links = enterprisePlugin?.info?.links || plugin.info.links; } // Fix link name @@ -197,7 +203,7 @@ function getPhantomPlugin(options: GetPhantomPluginOptions): DataSourcePluginMet author: { name: 'Grafana Labs' }, links: [ { - url: 'https://grafana.com/grafana/plugins/' + options.id, + url: config.marketplaceUrl + options.id, name: 'Install now', }, ],