From 18eb9d60768129a062b3e3840ccf08110f7b40f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 9 Feb 2016 11:17:49 +0100 Subject: [PATCH] feat(apps): began work on app pages --- examples/nginx-app/module.js | 28 +-- examples/nginx-app/partials/logs.html | 4 + examples/nginx-app/partials/stream.html | 12 +- examples/nginx-app/plugin.json | 4 +- pkg/api/dtos/apps.go | 18 +- pkg/api/index.go | 4 - pkg/plugins/app_plugin.go | 20 +- public/app/app.ts | 6 - public/app/core/core.ts | 2 +- .../app/core/directives/plugin_component.ts | 17 +- public/app/core/routes/all.js | 166 ----------------- public/app/core/routes/routes.ts | 171 ++++++++++++++++++ public/app/features/apps/all.ts | 1 + public/app/features/apps/page_ctrl.ts | 23 +++ public/app/features/apps/partials/list.html | 2 +- public/app/features/apps/partials/page.html | 14 ++ public/views/index.html | 1 - 17 files changed, 267 insertions(+), 226 deletions(-) create mode 100644 examples/nginx-app/partials/logs.html delete mode 100644 public/app/core/routes/all.js create mode 100644 public/app/core/routes/routes.ts create mode 100644 public/app/features/apps/page_ctrl.ts create mode 100644 public/app/features/apps/partials/page.html diff --git a/examples/nginx-app/module.js b/examples/nginx-app/module.js index 9e061df04cc..1805d1a0fad 100644 --- a/examples/nginx-app/module.js +++ b/examples/nginx-app/module.js @@ -1,28 +1,20 @@ define([ - 'angular', - 'app/app' -], function(angular, app) { +], function() { + 'use strict'; - var module = angular.module('nginx-app', []); - app.default.useModule(module); + function StreamPageCtrl() {} + StreamPageCtrl.templateUrl = 'public/plugins/nginx-app/partials/stream.html'; - module.config(function($routeProvider) { - $routeProvider - .when('/nginx/stream', { - templateUrl: 'public/plugins/nginx-app/partials/stream.html', - }); - }); + function LogsPageCtrl() {} + LogsPageCtrl.templateUrl = 'public/plugins/nginx-app/partials/logs.html'; - function NginxConfigCtrl() { - this.appEditCtrl.beforeUpdate = function() { - alert('before!'); - }; - } + function NginxConfigCtrl() {} NginxConfigCtrl.templateUrl = 'public/plugins/nginx-app/partials/config.html'; - return { - ConfigCtrl: NginxConfigCtrl + ConfigCtrl: NginxConfigCtrl, + StreamPageCtrl: StreamPageCtrl, + LogsPageCtrl: LogsPageCtrl, }; }); diff --git a/examples/nginx-app/partials/logs.html b/examples/nginx-app/partials/logs.html new file mode 100644 index 00000000000..38ad06f4384 --- /dev/null +++ b/examples/nginx-app/partials/logs.html @@ -0,0 +1,4 @@ + +

Nginx logs view

+ +Logs! diff --git a/examples/nginx-app/partials/stream.html b/examples/nginx-app/partials/stream.html index a89afaced57..fc0f367046f 100644 --- a/examples/nginx-app/partials/stream.html +++ b/examples/nginx-app/partials/stream.html @@ -1,12 +1,4 @@ - - - -
-
-

NGINX app

-
-
+

Nginx stream view

+testing! diff --git a/examples/nginx-app/plugin.json b/examples/nginx-app/plugin.json index 6941fe87f17..7e3cdd7ad08 100644 --- a/examples/nginx-app/plugin.json +++ b/examples/nginx-app/plugin.json @@ -6,8 +6,8 @@ "staticRoot": ".", "pages": [ - {"name": "Live stream", "url": "nginx/stream", "reqRole": "Editor"}, - {"name": "Log view", "url": "nginx/log", "reqRole": "Editor"} + { "name": "Live stream", "component": "StreamPageCtrl", "role": "Editor"}, + { "name": "Log view", "component": "LogsPageCtrl", "role": "Viewer"} ], "css": { diff --git a/pkg/api/dtos/apps.go b/pkg/api/dtos/apps.go index d10883a26d7..7a1e18148e2 100644 --- a/pkg/api/dtos/apps.go +++ b/pkg/api/dtos/apps.go @@ -6,15 +6,15 @@ import ( ) type AppSettings struct { - Name string `json:"name"` - AppId string `json:"appId"` - Enabled bool `json:"enabled"` - Pinned bool `json:"pinned"` - Module string `json:"module"` - Info *plugins.PluginInfo `json:"info"` - Pages []plugins.AppPluginPage `json:"pages"` - Includes []plugins.AppIncludeInfo `json:"includes"` - JsonData map[string]interface{} `json:"jsonData"` + Name string `json:"name"` + AppId string `json:"appId"` + Enabled bool `json:"enabled"` + Pinned bool `json:"pinned"` + Module string `json:"module"` + Info *plugins.PluginInfo `json:"info"` + Pages []*plugins.AppPluginPage `json:"pages"` + Includes []*plugins.AppIncludeInfo `json:"includes"` + JsonData map[string]interface{} `json:"jsonData"` } func NewAppSettingsDto(def *plugins.AppPlugin, data *models.AppSettings) *AppSettings { diff --git a/pkg/api/index.go b/pkg/api/index.go index 522b53b9026..9eea59fa4d2 100644 --- a/pkg/api/index.go +++ b/pkg/api/index.go @@ -86,10 +86,6 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) { } for _, plugin := range enabledPlugins.Apps { - if plugin.Module != "" { - data.PluginModules = append(data.PluginModules, plugin.Module) - } - if plugin.Css != nil { data.PluginCss = append(data.PluginCss, &dtos.PluginCss{Light: plugin.Css.Light, Dark: plugin.Css.Dark}) } diff --git a/pkg/plugins/app_plugin.go b/pkg/plugins/app_plugin.go index 547cd91283c..cb1349047da 100644 --- a/pkg/plugins/app_plugin.go +++ b/pkg/plugins/app_plugin.go @@ -4,13 +4,15 @@ import ( "encoding/json" "strings" + "github.com/gosimple/slug" "github.com/grafana/grafana/pkg/models" ) type AppPluginPage struct { - Name string `json:"name"` - Url string `json:"url"` - ReqRole models.RoleType `json:"reqRole"` + Name string `json:"name"` + Slug string `json:"slug"` + Component string `json:"component"` + Role models.RoleType `json:"role"` } type AppPluginCss struct { @@ -27,9 +29,9 @@ type AppIncludeInfo struct { type AppPlugin struct { FrontendPluginBase Css *AppPluginCss `json:"css"` - Pages []AppPluginPage `json:"pages"` + Pages []*AppPluginPage `json:"pages"` Routes []*AppPluginRoute `json:"routes"` - Includes []AppIncludeInfo `json:"-"` + Includes []*AppIncludeInfo `json:"-"` Pinned bool `json:"-"` Enabled bool `json:"-"` @@ -67,7 +69,7 @@ func (app *AppPlugin) Load(decoder *json.Decoder, pluginDir string) error { for _, panel := range Panels { if strings.HasPrefix(panel.PluginDir, app.PluginDir) { panel.IncludedInAppId = app.Id - app.Includes = append(app.Includes, AppIncludeInfo{ + app.Includes = append(app.Includes, &AppIncludeInfo{ Name: panel.Name, Id: panel.Id, Type: panel.Type, @@ -75,6 +77,12 @@ func (app *AppPlugin) Load(decoder *json.Decoder, pluginDir string) error { } } + for _, page := range app.Pages { + if page.Slug == "" { + page.Slug = slug.Make(page.Name) + } + } + Apps[app.Id] = app return nil } diff --git a/public/app/app.ts b/public/app/app.ts index d9ad3a449d3..a4e1bebca6e 100644 --- a/public/app/app.ts +++ b/public/app/app.ts @@ -72,12 +72,6 @@ export class GrafanaApp { this.useModule(coreModule); var preBootRequires = [System.import('app/features/all')]; - var pluginModules = config.bootData.pluginModules || []; - - // add plugin modules - for (var i = 0; i < pluginModules.length; i++) { - preBootRequires.push(System.import(pluginModules[i])); - } Promise.all(preBootRequires).then(() => { // disable tool tip animation diff --git a/public/app/core/core.ts b/public/app/core/core.ts index 55c0f4ec049..361dea334ef 100644 --- a/public/app/core/core.ts +++ b/public/app/core/core.ts @@ -28,7 +28,7 @@ import {navbarDirective} from './components/navbar/navbar'; import {arrayJoin} from './directives/array_join'; import 'app/core/controllers/all'; import 'app/core/services/all'; -import 'app/core/routes/all'; +import 'app/core/routes/routes'; import './filters/filters'; import coreModule from './core_module'; diff --git a/public/app/core/directives/plugin_component.ts b/public/app/core/directives/plugin_component.ts index 8dd05a83ab0..518d5a8af56 100644 --- a/public/app/core/directives/plugin_component.ts +++ b/public/app/core/directives/plugin_component.ts @@ -143,15 +143,28 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $ } // AppConfigCtrl case 'app-config-ctrl': { - return System.import(scope.ctrl.appModel.module).then(function(appModule) { + let appModel = scope.ctrl.appModel; + return System.import(appModel.module).then(function(appModule) { return { - name: 'app-config-' + scope.ctrl.appModel.appId, + name: 'app-config-' + appModel.appId, bindings: {appModel: "=", appEditCtrl: "="}, attrs: {"app-model": "ctrl.appModel", "app-edit-ctrl": "ctrl"}, Component: appModule.ConfigCtrl, }; }); } + // App Page + case 'app-page': { + let appModel = scope.ctrl.appModel; + return System.import(appModel.module).then(function(appModule) { + return { + name: 'app-page-' + appModel.appId + '-' + scope.ctrl.page.slug, + bindings: {appModel: "="}, + attrs: {"app-model": "ctrl.appModel"}, + Component: appModule[scope.ctrl.page.component], + }; + }); + } // Panel case 'panel': { return loadPanelComponentInfo(scope, attrs); diff --git a/public/app/core/routes/all.js b/public/app/core/routes/all.js deleted file mode 100644 index 2d313d7322f..00000000000 --- a/public/app/core/routes/all.js +++ /dev/null @@ -1,166 +0,0 @@ -define([ - 'angular', - '../core_module', - './bundle_loader', - './dashboard_loaders', -], function(angular, coreModule, BundleLoader) { - "use strict"; - - coreModule.default.config(function($routeProvider, $locationProvider) { - $locationProvider.html5Mode(true); - - var loadOrgBundle = new BundleLoader.BundleLoader('app/features/org/all'); - var loadAppsBundle = new BundleLoader.BundleLoader('app/features/apps/all'); - - $routeProvider - .when('/', { - templateUrl: 'public/app/partials/dashboard.html', - controller : 'LoadDashboardCtrl', - reloadOnSearch: false, - }) - .when('/dashboard/:type/:slug', { - templateUrl: 'public/app/partials/dashboard.html', - controller : 'LoadDashboardCtrl', - reloadOnSearch: false, - }) - .when('/dashboard-solo/:type/:slug', { - templateUrl: 'public/app/features/panel/partials/soloPanel.html', - controller : 'SoloPanelCtrl', - }) - .when('/dashboard-import/:file', { - templateUrl: 'public/app/partials/dashboard.html', - controller : 'DashFromImportCtrl', - reloadOnSearch: false, - }) - .when('/dashboard/new', { - templateUrl: 'public/app/partials/dashboard.html', - controller : 'NewDashboardCtrl', - reloadOnSearch: false, - }) - .when('/import/dashboard', { - templateUrl: 'public/app/features/dashboard/partials/import.html', - controller : 'DashboardImportCtrl', - }) - .when('/datasources', { - templateUrl: 'public/app/features/datasources/partials/list.html', - controller : 'DataSourcesCtrl', - resolve: loadOrgBundle, - }) - .when('/datasources/edit/:id', { - templateUrl: 'public/app/features/datasources/partials/edit.html', - controller : 'DataSourceEditCtrl', - resolve: loadOrgBundle, - }) - .when('/datasources/new', { - templateUrl: 'public/app/features/datasources/partials/edit.html', - controller : 'DataSourceEditCtrl', - resolve: loadOrgBundle, - }) - .when('/org', { - templateUrl: 'public/app/features/org/partials/orgDetails.html', - controller : 'OrgDetailsCtrl', - resolve: loadOrgBundle, - }) - .when('/org/new', { - templateUrl: 'public/app/features/org/partials/newOrg.html', - controller : 'NewOrgCtrl', - resolve: loadOrgBundle, - }) - .when('/org/users', { - templateUrl: 'public/app/features/org/partials/orgUsers.html', - controller : 'OrgUsersCtrl', - resolve: loadOrgBundle, - }) - .when('/org/apikeys', { - templateUrl: 'public/app/features/org/partials/orgApiKeys.html', - controller : 'OrgApiKeysCtrl', - resolve: loadOrgBundle, - }) - .when('/profile', { - templateUrl: 'public/app/features/profile/partials/profile.html', - controller : 'ProfileCtrl', - }) - .when('/profile/password', { - templateUrl: 'public/app/features/profile/partials/password.html', - controller : 'ChangePasswordCtrl', - }) - .when('/profile/select-org', { - templateUrl: 'public/app/features/profile/partials/select_org.html', - controller : 'SelectOrgCtrl', - }) - .when('/admin/settings', { - templateUrl: 'public/app/features/admin/partials/settings.html', - controller : 'AdminSettingsCtrl', - }) - .when('/admin/users', { - templateUrl: 'public/app/features/admin/partials/users.html', - controller : 'AdminListUsersCtrl', - }) - .when('/admin/users/create', { - templateUrl: 'public/app/features/admin/partials/new_user.html', - controller : 'AdminEditUserCtrl', - }) - .when('/admin/users/edit/:id', { - templateUrl: 'public/app/features/admin/partials/edit_user.html', - controller : 'AdminEditUserCtrl', - }) - .when('/admin/orgs', { - templateUrl: 'public/app/features/admin/partials/orgs.html', - controller : 'AdminListOrgsCtrl', - }) - .when('/admin/orgs/edit/:id', { - templateUrl: 'public/app/features/admin/partials/edit_org.html', - controller : 'AdminEditOrgCtrl', - }) - .when('/admin/stats', { - templateUrl: 'public/app/features/admin/partials/stats.html', - controller : 'AdminStatsCtrl', - controllerAs: 'ctrl', - }) - .when('/login', { - templateUrl: 'public/app/partials/login.html', - controller : 'LoginCtrl', - }) - .when('/invite/:code', { - templateUrl: 'public/app/partials/signup_invited.html', - controller : 'InvitedCtrl', - }) - .when('/signup', { - templateUrl: 'public/app/partials/signup_step2.html', - controller : 'SignUpCtrl', - }) - .when('/user/password/send-reset-email', { - templateUrl: 'public/app/partials/reset_password.html', - controller : 'ResetPasswordCtrl', - }) - .when('/user/password/reset', { - templateUrl: 'public/app/partials/reset_password.html', - controller : 'ResetPasswordCtrl', - }) - .when('/dashboard/snapshots', { - templateUrl: 'public/app/features/snapshot/partials/snapshots.html', - controller : 'SnapshotsCtrl', - controllerAs: 'ctrl', - }) - .when('/apps', { - templateUrl: 'public/app/features/apps/partials/list.html', - controller: 'AppListCtrl', - controllerAs: 'ctrl', - resolve: loadAppsBundle, - }) - .when('/apps/edit/:appId', { - templateUrl: 'public/app/features/apps/partials/edit.html', - controller: 'AppEditCtrl', - controllerAs: 'ctrl', - resolve: loadAppsBundle, - }) - .when('/global-alerts', { - templateUrl: 'public/app/features/dashboard/partials/globalAlerts.html', - }) - .otherwise({ - templateUrl: 'public/app/partials/error.html', - controller: 'ErrorCtrl' - }); - }); - -}); diff --git a/public/app/core/routes/routes.ts b/public/app/core/routes/routes.ts new file mode 100644 index 00000000000..95fecce664d --- /dev/null +++ b/public/app/core/routes/routes.ts @@ -0,0 +1,171 @@ +/// + +import angular from 'angular'; +import coreModule from 'app/core/core_module'; +import {BundleLoader} from './bundle_loader'; + +/** @ngInject **/ +function setupAngularRoutes($routeProvider, $locationProvider) { + $locationProvider.html5Mode(true); + + var loadOrgBundle = new BundleLoader('app/features/org/all'); + var loadAppsBundle = new BundleLoader('app/features/apps/all'); + + $routeProvider + .when('/', { + templateUrl: 'public/app/partials/dashboard.html', + controller : 'LoadDashboardCtrl', + reloadOnSearch: false, + }) + .when('/dashboard/:type/:slug', { + templateUrl: 'public/app/partials/dashboard.html', + controller : 'LoadDashboardCtrl', + reloadOnSearch: false, + }) + .when('/dashboard-solo/:type/:slug', { + templateUrl: 'public/app/features/panel/partials/soloPanel.html', + controller : 'SoloPanelCtrl', + }) + .when('/dashboard-import/:file', { + templateUrl: 'public/app/partials/dashboard.html', + controller : 'DashFromImportCtrl', + reloadOnSearch: false, + }) + .when('/dashboard/new', { + templateUrl: 'public/app/partials/dashboard.html', + controller : 'NewDashboardCtrl', + reloadOnSearch: false, + }) + .when('/import/dashboard', { + templateUrl: 'public/app/features/dashboard/partials/import.html', + controller : 'DashboardImportCtrl', + }) + .when('/datasources', { + templateUrl: 'public/app/features/datasources/partials/list.html', + controller : 'DataSourcesCtrl', + resolve: loadOrgBundle, + }) + .when('/datasources/edit/:id', { + templateUrl: 'public/app/features/datasources/partials/edit.html', + controller : 'DataSourceEditCtrl', + resolve: loadOrgBundle, + }) + .when('/datasources/new', { + templateUrl: 'public/app/features/datasources/partials/edit.html', + controller : 'DataSourceEditCtrl', + resolve: loadOrgBundle, + }) + .when('/org', { + templateUrl: 'public/app/features/org/partials/orgDetails.html', + controller : 'OrgDetailsCtrl', + resolve: loadOrgBundle, + }) + .when('/org/new', { + templateUrl: 'public/app/features/org/partials/newOrg.html', + controller : 'NewOrgCtrl', + resolve: loadOrgBundle, + }) + .when('/org/users', { + templateUrl: 'public/app/features/org/partials/orgUsers.html', + controller : 'OrgUsersCtrl', + resolve: loadOrgBundle, + }) + .when('/org/apikeys', { + templateUrl: 'public/app/features/org/partials/orgApiKeys.html', + controller : 'OrgApiKeysCtrl', + resolve: loadOrgBundle, + }) + .when('/profile', { + templateUrl: 'public/app/features/profile/partials/profile.html', + controller : 'ProfileCtrl', + }) + .when('/profile/password', { + templateUrl: 'public/app/features/profile/partials/password.html', + controller : 'ChangePasswordCtrl', + }) + .when('/profile/select-org', { + templateUrl: 'public/app/features/profile/partials/select_org.html', + controller : 'SelectOrgCtrl', + }) + .when('/admin/settings', { + templateUrl: 'public/app/features/admin/partials/settings.html', + controller : 'AdminSettingsCtrl', + }) + .when('/admin/users', { + templateUrl: 'public/app/features/admin/partials/users.html', + controller : 'AdminListUsersCtrl', + }) + .when('/admin/users/create', { + templateUrl: 'public/app/features/admin/partials/new_user.html', + controller : 'AdminEditUserCtrl', + }) + .when('/admin/users/edit/:id', { + templateUrl: 'public/app/features/admin/partials/edit_user.html', + controller : 'AdminEditUserCtrl', + }) + .when('/admin/orgs', { + templateUrl: 'public/app/features/admin/partials/orgs.html', + controller : 'AdminListOrgsCtrl', + }) + .when('/admin/orgs/edit/:id', { + templateUrl: 'public/app/features/admin/partials/edit_org.html', + controller : 'AdminEditOrgCtrl', + }) + .when('/admin/stats', { + templateUrl: 'public/app/features/admin/partials/stats.html', + controller : 'AdminStatsCtrl', + controllerAs: 'ctrl', + }) + .when('/login', { + templateUrl: 'public/app/partials/login.html', + controller : 'LoginCtrl', + }) + .when('/invite/:code', { + templateUrl: 'public/app/partials/signup_invited.html', + controller : 'InvitedCtrl', + }) + .when('/signup', { + templateUrl: 'public/app/partials/signup_step2.html', + controller : 'SignUpCtrl', + }) + .when('/user/password/send-reset-email', { + templateUrl: 'public/app/partials/reset_password.html', + controller : 'ResetPasswordCtrl', + }) + .when('/user/password/reset', { + templateUrl: 'public/app/partials/reset_password.html', + controller : 'ResetPasswordCtrl', + }) + .when('/dashboard/snapshots', { + templateUrl: 'public/app/features/snapshot/partials/snapshots.html', + controller : 'SnapshotsCtrl', + controllerAs: 'ctrl', + }) + .when('/apps', { + templateUrl: 'public/app/features/apps/partials/list.html', + controller: 'AppListCtrl', + controllerAs: 'ctrl', + resolve: loadAppsBundle, + }) + .when('/apps/:appId/edit', { + templateUrl: 'public/app/features/apps/partials/edit.html', + controller: 'AppEditCtrl', + controllerAs: 'ctrl', + resolve: loadAppsBundle, + }) + .when('/apps/:appId/page/:slug', { + templateUrl: 'public/app/features/apps/partials/page.html', + controller: 'AppPageCtrl', + controllerAs: 'ctrl', + resolve: loadAppsBundle, + }) + .when('/global-alerts', { + templateUrl: 'public/app/features/dashboard/partials/globalAlerts.html', + }) + .otherwise({ + templateUrl: 'public/app/partials/error.html', + controller: 'ErrorCtrl' + }); +} + +coreModule.config(setupAngularRoutes); diff --git a/public/app/features/apps/all.ts b/public/app/features/apps/all.ts index fcdd27dff4d..9d54165e56b 100644 --- a/public/app/features/apps/all.ts +++ b/public/app/features/apps/all.ts @@ -1,2 +1,3 @@ import './edit_ctrl'; +import './page_ctrl'; import './list_ctrl'; diff --git a/public/app/features/apps/page_ctrl.ts b/public/app/features/apps/page_ctrl.ts new file mode 100644 index 00000000000..ac04a59f43f --- /dev/null +++ b/public/app/features/apps/page_ctrl.ts @@ -0,0 +1,23 @@ +/// + +import angular from 'angular'; +import _ from 'lodash'; + +export class AppPageCtrl { + page: any; + appModel: any; + + /** @ngInject */ + constructor(private backendSrv, private $routeParams: any, private $rootScope) { + this.backendSrv.get(`/api/org/apps/${this.$routeParams.appId}/settings`).then(app => { + this.appModel = app; + this.page = _.findWhere(app.pages, {slug: this.$routeParams.slug}); + if (!this.page) { + $rootScope.appEvent('alert-error', ['App Page Not Found', '']); + } + }); + } +} + +angular.module('grafana.controllers').controller('AppPageCtrl', AppPageCtrl); + diff --git a/public/app/features/apps/partials/list.html b/public/app/features/apps/partials/list.html index 6d2d8cc24df..59c1054816f 100644 --- a/public/app/features/apps/partials/list.html +++ b/public/app/features/apps/partials/list.html @@ -24,7 +24,7 @@ - + {{app.name}}     diff --git a/public/app/features/apps/partials/page.html b/public/app/features/apps/partials/page.html new file mode 100644 index 00000000000..0f111ce9398 --- /dev/null +++ b/public/app/features/apps/partials/page.html @@ -0,0 +1,14 @@ + + + + +

+ App page +

+ +
+ + +
diff --git a/public/views/index.html b/public/views/index.html index 40be796b306..724afd33f33 100644 --- a/public/views/index.html +++ b/public/views/index.html @@ -50,7 +50,6 @@ window.grafanaBootData = { user:[[.User]], settings: [[.Settings]], - pluginModules: [[.PluginModules]], mainNavLinks: [[.MainNavLinks]] };