diff --git a/pkg/api/api.go b/pkg/api/api.go
index 839978845ab..1e1348a51ab 100644
--- a/pkg/api/api.go
+++ b/pkg/api/api.go
@@ -47,6 +47,9 @@ func Register(r *macaron.Macaron) {
r.Get("/dashboard/*", reqSignedIn, Index)
r.Get("/dashboard-solo/*", reqSignedIn, Index)
+ r.Get("/playlists/", reqSignedIn, Index)
+ r.Get("/playlists/*", reqSignedIn, Index)
+
// sign up
r.Get("/signup", Index)
r.Get("/api/user/signup/options", wrap(GetSignUpOptions))
@@ -169,6 +172,16 @@ func Register(r *macaron.Macaron) {
r.Get("/tags", GetDashboardTags)
})
+ // Playlist
+ r.Group("/playlists", func() {
+ r.Get("/", SearchPlaylists)
+ r.Get("/:id", ValidateOrgPlaylist, GetPlaylist)
+ r.Get("/:id/dashboards", ValidateOrgPlaylist, GetPlaylistDashboards)
+ r.Delete("/:id", reqEditorRole, ValidateOrgPlaylist, DeletePlaylist)
+ r.Put("/:id", reqEditorRole, bind(m.UpdatePlaylistQuery{}), ValidateOrgPlaylist, UpdatePlaylist)
+ r.Post("/", reqEditorRole, bind(m.CreatePlaylistQuery{}), CreatePlaylist)
+ })
+
// Search
r.Get("/search/", Search)
diff --git a/pkg/api/index.go b/pkg/api/index.go
index 9a39837261f..ff5a923ebba 100644
--- a/pkg/api/index.go
+++ b/pkg/api/index.go
@@ -53,6 +53,12 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
Href: "/",
})
+ data.MainNavLinks = append(data.MainNavLinks, &dtos.NavLink{
+ Text: "Playlists",
+ Icon: "fa fa-fw fa-list",
+ Href: "/playlists",
+ })
+
if c.OrgRole == m.ROLE_ADMIN {
data.MainNavLinks = append(data.MainNavLinks, &dtos.NavLink{
Text: "Data Sources",
diff --git a/pkg/api/playlist.go b/pkg/api/playlist.go
new file mode 100644
index 00000000000..83355418b4b
--- /dev/null
+++ b/pkg/api/playlist.go
@@ -0,0 +1,103 @@
+package api
+
+import (
+ "github.com/grafana/grafana/pkg/bus"
+ "github.com/grafana/grafana/pkg/middleware"
+ m "github.com/grafana/grafana/pkg/models"
+)
+
+func ValidateOrgPlaylist(c *middleware.Context) {
+ id := c.ParamsInt64(":id")
+ query := m.GetPlaylistByIdQuery{Id: id}
+ err := bus.Dispatch(&query)
+
+ if err != nil {
+ c.JsonApiErr(404, "Playlist not found", err)
+ return
+ }
+
+ if query.Result.OrgId != c.OrgId {
+ c.JsonApiErr(403, "You are not allowed to edit/view playlist", nil)
+ return
+ }
+}
+
+func SearchPlaylists(c *middleware.Context) {
+ query := c.Query("query")
+ limit := c.QueryInt("limit")
+
+ if limit == 0 {
+ limit = 1000
+ }
+
+ searchQuery := m.PlaylistQuery{
+ Title: query,
+ Limit: limit,
+ OrgId: c.OrgId,
+ }
+
+ err := bus.Dispatch(&searchQuery)
+ if err != nil {
+ c.JsonApiErr(500, "Search failed", err)
+ return
+ }
+
+ c.JSON(200, searchQuery.Result)
+}
+
+func GetPlaylist(c *middleware.Context) {
+ id := c.ParamsInt64(":id")
+ cmd := m.GetPlaylistByIdQuery{Id: id}
+
+ if err := bus.Dispatch(&cmd); err != nil {
+ c.JsonApiErr(500, "Playlist not found", err)
+ return
+ }
+
+ c.JSON(200, cmd.Result)
+}
+
+func GetPlaylistDashboards(c *middleware.Context) {
+ id := c.ParamsInt64(":id")
+
+ query := m.GetPlaylistDashboardsQuery{Id: id}
+ if err := bus.Dispatch(&query); err != nil {
+ c.JsonApiErr(500, "Playlist not found", err)
+ return
+ }
+
+ c.JSON(200, query.Result)
+}
+
+func DeletePlaylist(c *middleware.Context) {
+ id := c.ParamsInt64(":id")
+
+ cmd := m.DeletePlaylistQuery{Id: id}
+ if err := bus.Dispatch(&cmd); err != nil {
+ c.JsonApiErr(500, "Failed to delete playlist", err)
+ return
+ }
+
+ c.JSON(200, "")
+}
+
+func CreatePlaylist(c *middleware.Context, query m.CreatePlaylistQuery) {
+ query.OrgId = c.OrgId
+ err := bus.Dispatch(&query)
+ if err != nil {
+ c.JsonApiErr(500, "Failed to create playlist", err)
+ return
+ }
+
+ c.JSON(200, query.Result)
+}
+
+func UpdatePlaylist(c *middleware.Context, query m.UpdatePlaylistQuery) {
+ err := bus.Dispatch(&query)
+ if err != nil {
+ c.JsonApiErr(500, "Failed to save playlist", err)
+ return
+ }
+
+ c.JSON(200, query.Result)
+}
diff --git a/pkg/models/playlist.go b/pkg/models/playlist.go
new file mode 100644
index 00000000000..1bfa5949fe2
--- /dev/null
+++ b/pkg/models/playlist.go
@@ -0,0 +1,79 @@
+package models
+
+import (
+ "errors"
+)
+
+// Typed errors
+var (
+ ErrPlaylistNotFound = errors.New("Playlist not found")
+ ErrPlaylistWithSameNameExists = errors.New("A playlist with the same name already exists")
+)
+
+// Playlist model
+type Playlist struct {
+ Id int64 `json:"id"`
+ Title string `json:"title"`
+ Type string `json:"type"`
+ Timespan string `json:"timespan"`
+ Data []int `json:"data"`
+ OrgId int64 `json:"-"`
+}
+
+type PlaylistDashboard struct {
+ Id int64 `json:"id"`
+ Slug string `json:"slug"`
+ Title string `json:"title"`
+}
+
+func (this PlaylistDashboard) TableName() string {
+ return "dashboard"
+}
+
+type Playlists []*Playlist
+type PlaylistDashboards []*PlaylistDashboard
+
+//
+// COMMANDS
+//
+type PlaylistQuery struct {
+ Title string
+ Limit int
+ OrgId int64
+
+ Result Playlists
+}
+
+type UpdatePlaylistQuery struct {
+ Id int64
+ Title string
+ Type string
+ Timespan string
+ Data []int
+
+ Result *Playlist
+}
+
+type CreatePlaylistQuery struct {
+ Title string
+ Type string
+ Timespan string
+ Data []int
+ OrgId int64
+
+ Result *Playlist
+}
+
+type GetPlaylistByIdQuery struct {
+ Id int64
+ Result *Playlist
+}
+
+type GetPlaylistDashboardsQuery struct {
+ Id int64
+ Result *PlaylistDashboards
+}
+
+type DeletePlaylistQuery struct {
+ Id int64
+}
diff --git a/pkg/services/sqlstore/migrations/migrations.go b/pkg/services/sqlstore/migrations/migrations.go
index 4c9283e36ac..c789db1966a 100644
--- a/pkg/services/sqlstore/migrations/migrations.go
+++ b/pkg/services/sqlstore/migrations/migrations.go
@@ -20,6 +20,7 @@ func AddMigrations(mg *Migrator) {
addQuotaMigration(mg)
addPluginBundleMigration(mg)
addSessionMigration(mg)
+ addPlaylistMigrations(mg)
}
func addMigrationLogMigrations(mg *Migrator) {
diff --git a/pkg/services/sqlstore/migrations/playlist_mig.go b/pkg/services/sqlstore/migrations/playlist_mig.go
new file mode 100644
index 00000000000..d8625f301ae
--- /dev/null
+++ b/pkg/services/sqlstore/migrations/playlist_mig.go
@@ -0,0 +1,20 @@
+package migrations
+
+import . "github.com/grafana/grafana/pkg/services/sqlstore/migrator"
+
+func addPlaylistMigrations(mg *Migrator) {
+ playlistV1 := Table{
+ Name: "playlist",
+ Columns: []*Column{
+ {Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
+ {Name: "title", Type: DB_NVarchar, Length: 255, Nullable: false},
+ {Name: "type", Type: DB_NVarchar, Length: 255, Nullable: false},
+ {Name: "data", Type: DB_Text, Nullable: false},
+ {Name: "timespan", Type: DB_NVarchar, Length: 255, Nullable: false},
+ {Name: "org_id", Type: DB_BigInt, Nullable: false},
+ },
+ }
+
+ // create table
+ mg.AddMigration("create playlist table v1", NewAddTableMigration(playlistV1))
+}
diff --git a/pkg/services/sqlstore/playlist.go b/pkg/services/sqlstore/playlist.go
new file mode 100644
index 00000000000..3e04f65f9d4
--- /dev/null
+++ b/pkg/services/sqlstore/playlist.go
@@ -0,0 +1,125 @@
+package sqlstore
+
+import (
+ "github.com/go-xorm/xorm"
+
+ "github.com/grafana/grafana/pkg/bus"
+ m "github.com/grafana/grafana/pkg/models"
+)
+
+func init() {
+ bus.AddHandler("sql", CreatePlaylist)
+ bus.AddHandler("sql", UpdatePlaylist)
+ bus.AddHandler("sql", DeletePlaylist)
+ bus.AddHandler("sql", SearchPlaylists)
+ bus.AddHandler("sql", GetPlaylist)
+ bus.AddHandler("sql", GetPlaylistDashboards)
+}
+
+func CreatePlaylist(query *m.CreatePlaylistQuery) error {
+ var err error
+
+ playlist := m.Playlist{
+ Title: query.Title,
+ Type: query.Type,
+ Data: query.Data,
+ Timespan: query.Timespan,
+ OrgId: query.OrgId,
+ }
+
+ _, err = x.Insert(&playlist)
+
+ query.Result = &playlist
+ return err
+}
+
+func UpdatePlaylist(query *m.UpdatePlaylistQuery) error {
+ var err error
+ x.Logger.SetLevel(5)
+ playlist := m.Playlist{
+ Id: query.Id,
+ Title: query.Title,
+ Type: query.Type,
+ Data: query.Data,
+ Timespan: query.Timespan,
+ }
+
+ existingPlaylist := x.Where("id = ?", query.Id).Find(m.Playlist{})
+
+ if existingPlaylist == nil {
+ return m.ErrPlaylistNotFound
+ }
+
+ _, err = x.Id(query.Id).Cols("id", "title", "data", "timespan").Update(&playlist)
+
+ query.Result = &playlist
+ return err
+}
+
+func GetPlaylist(query *m.GetPlaylistByIdQuery) error {
+ if query.Id == 0 {
+ return m.ErrCommandValidationFailed
+ }
+
+ playlist := m.Playlist{}
+ _, err := x.Id(query.Id).Get(&playlist)
+ query.Result = &playlist
+
+ return err
+}
+
+func DeletePlaylist(query *m.DeletePlaylistQuery) error {
+ if query.Id == 0 {
+ return m.ErrCommandValidationFailed
+ }
+
+ return inTransaction(func(sess *xorm.Session) error {
+ var rawSql = "DELETE FROM playlist WHERE id = ?"
+ _, err := sess.Exec(rawSql, query.Id)
+ return err
+ })
+}
+
+func SearchPlaylists(query *m.PlaylistQuery) error {
+ var playlists = make(m.Playlists, 0)
+
+ sess := x.Limit(query.Limit)
+
+ if query.Title != "" {
+ sess.Where("title LIKE ?", query.Title)
+ }
+
+ sess.Where("org_id = ?", query.OrgId)
+ err := sess.Find(&playlists)
+ query.Result = playlists
+
+ return err
+}
+
+func GetPlaylistDashboards(query *m.GetPlaylistDashboardsQuery) error {
+ if query.Id == 0 {
+ return m.ErrCommandValidationFailed
+ }
+
+ var dashboards = make(m.PlaylistDashboards, 0)
+ var playlist = m.Playlist{}
+
+ hasPlaylist, err := x.Id(query.Id).Get(&playlist)
+ query.Result = &dashboards
+
+ if err != nil {
+ return err
+ }
+
+ if !hasPlaylist || len(playlist.Data) == 0 {
+ return nil
+ }
+
+ err = x.In("id", playlist.Data).Find(&dashboards)
+
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/public/app/features/all.js b/public/app/features/all.js
index 6519d112b74..9019ec06172 100644
--- a/public/app/features/all.js
+++ b/public/app/features/all.js
@@ -4,6 +4,7 @@ define([
'./annotations/annotationsSrv',
'./templating/templateSrv',
'./dashboard/all',
+ './playlist/all',
'./panel/all',
'./profile/profileCtrl',
'./profile/changePasswordCtrl',
diff --git a/public/app/features/dashboard/all.js b/public/app/features/dashboard/all.js
index 0ee3a64a806..524441b7a99 100644
--- a/public/app/features/dashboard/all.js
+++ b/public/app/features/dashboard/all.js
@@ -4,7 +4,6 @@ define([
'./dashboardNavCtrl',
'./snapshotTopNavCtrl',
'./saveDashboardAsCtrl',
- './playlistCtrl',
'./rowCtrl',
'./shareModalCtrl',
'./shareSnapshotCtrl',
diff --git a/public/app/features/dashboard/playlistCtrl.js b/public/app/features/dashboard/playlistCtrl.js
deleted file mode 100644
index 9242905405a..00000000000
--- a/public/app/features/dashboard/playlistCtrl.js
+++ /dev/null
@@ -1,55 +0,0 @@
-define([
- 'angular',
- 'lodash',
- 'app/core/config'
-],
-function (angular, _, config) {
- 'use strict';
-
- var module = angular.module('grafana.controllers');
-
- module.controller('PlaylistCtrl', function($scope, playlistSrv, backendSrv) {
-
- $scope.init = function() {
- $scope.playlist = [];
- $scope.timespan = config.playlist_timespan;
- $scope.search();
- };
-
- $scope.search = function() {
- var query = {starred: true, limit: 10};
-
- if ($scope.searchQuery) {
- query.query = $scope.searchQuery;
- query.starred = false;
- }
-
- backendSrv.search(query).then(function(results) {
- $scope.searchHits = results;
- $scope.filterHits();
- });
- };
-
- $scope.filterHits = function() {
- $scope.filteredHits = _.reject($scope.searchHits, function(dash) {
- return _.findWhere($scope.playlist, {uri: dash.uri});
- });
- };
-
- $scope.addDashboard = function(dashboard) {
- $scope.playlist.push(dashboard);
- $scope.filterHits();
- };
-
- $scope.removeDashboard = function(dashboard) {
- $scope.playlist = _.without($scope.playlist, dashboard);
- $scope.filterHits();
- };
-
- $scope.start = function() {
- playlistSrv.start($scope.playlist, $scope.timespan);
- };
-
- });
-
-});
diff --git a/public/app/features/playlist/all.js b/public/app/features/playlist/all.js
new file mode 100644
index 00000000000..85831247eb7
--- /dev/null
+++ b/public/app/features/playlist/all.js
@@ -0,0 +1,5 @@
+define([
+ './playlistsCtrl',
+ './playlistEditCtrl',
+ './playlistRoutes'
+], function () {});
diff --git a/public/app/features/playlist/partials/playlist-remove.html b/public/app/features/playlist/partials/playlist-remove.html
new file mode 100644
index 00000000000..d40b8b9ca42
--- /dev/null
+++ b/public/app/features/playlist/partials/playlist-remove.html
@@ -0,0 +1,5 @@
+
Are you sure want to delete "{{ playlist.title }}" playlist?
+
+
+
+
diff --git a/public/app/features/playlist/partials/playlist.html b/public/app/features/playlist/partials/playlist.html
new file mode 100644
index 00000000000..46598d18c7f
--- /dev/null
+++ b/public/app/features/playlist/partials/playlist.html
@@ -0,0 +1,119 @@
+
+
+
+
+
+
+
Playlist dashboards
+
+
+
+
+
+
+
+
+ {{dashboard.title}}
+ |
+
+
+ |
+
+
+
+ No dashboards found
+ |
+
+
+
+ Playlist empty
+ |
+
+
+
+
+
+
+
+ {{dashboard.title}}
+ |
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/public/app/features/playlist/partials/playlists.html b/public/app/features/playlist/partials/playlists.html
new file mode 100644
index 00000000000..7ebfff12e95
--- /dev/null
+++ b/public/app/features/playlist/partials/playlists.html
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+ No saved playlists
+
+
+
+ Saved playlists
+
+
+
+
+
+
+
+
diff --git a/public/app/features/playlist/playlistEditCtrl.js b/public/app/features/playlist/playlistEditCtrl.js
new file mode 100644
index 00000000000..ca64ec04aaf
--- /dev/null
+++ b/public/app/features/playlist/playlistEditCtrl.js
@@ -0,0 +1,115 @@
+define([
+ 'angular',
+ 'app/core/config',
+ 'lodash'
+],
+function (angular, config, _) {
+ 'use strict';
+
+ var module = angular.module('grafana.controllers');
+
+ module.controller('PlaylistEditCtrl', function(
+ playlist,
+ dashboards,
+ $scope,
+ playlistSrv,
+ backendSrv,
+ $location
+ ) {
+ $scope.search = function() {
+ var query = {starred: true, limit: 10};
+
+ if ($scope.searchQuery) {
+ query.query = $scope.searchQuery;
+ query.starred = false;
+ }
+
+ $scope.loading = true;
+
+ backendSrv.search(query)
+ .then(function(results) {
+ $scope.foundDashboards = results;
+ $scope.filterFoundDashboards();
+ })
+ .finally(function() {
+ $scope.loading = false;
+ });
+ };
+
+ $scope.filterFoundDashboards = function() {
+ $scope.filteredDashboards = _.reject($scope.foundDashboards, function(dashboard) {
+ return _.findWhere(dashboards, function(listDashboard) {
+ return listDashboard.id === dashboard.id;
+ });
+ });
+ };
+
+ $scope.addDashboard = function(dashboard) {
+ dashboards.push(dashboard);
+ $scope.filterFoundDashboards();
+ };
+
+ $scope.removeDashboard = function(dashboard) {
+ _.remove(dashboards, function(listedDashboard) {
+ return dashboard === listedDashboard;
+ });
+ $scope.filterFoundDashboards();
+ };
+
+ $scope.savePlaylist = function(playlist, dashboards) {
+ var savePromise;
+
+ playlist.data = dashboards.map(function(dashboard) {
+ return dashboard.id;
+ });
+
+ // Hardcoding playlist type for this iteration
+ playlist.type = "dashboards";
+
+ savePromise = playlist.id
+ ? backendSrv.put('/api/playlists/' + playlist.id, playlist)
+ : backendSrv.post('/api/playlists', playlist);
+
+ savePromise
+ .then(function() {
+ $scope.appEvent('alert-success', ['Playlist saved', '']);
+ $location.path('/playlists');
+ }, function() {
+ $scope.appEvent('alert-success', ['Unable to save playlist', '']);
+ });
+ };
+
+ $scope.startPlaylist = function(playlist, dashboards) {
+ playlistSrv.start(dashboards, playlist.timespan);
+ };
+
+ $scope.isPlaylistEmpty = function() {
+ return !dashboards.length;
+ };
+
+ $scope.isSearchResultsEmpty = function() {
+ return !$scope.foundDashboards.length;
+ };
+
+ $scope.isSearchQueryEmpty = function() {
+ return $scope.searchQuery === '';
+ };
+
+ $scope.backToList = function() {
+ $location.path('/playlists');
+ };
+
+ $scope.isLoading = function() {
+ return $scope.loading;
+ };
+
+ $scope.playlist = playlist;
+ $scope.dashboards = dashboards;
+ $scope.timespan = config.playlist_timespan;
+ $scope.filteredDashboards = [];
+ $scope.foundDashboards = [];
+ $scope.searchQuery = '';
+ $scope.loading = false;
+ $scope.search();
+ });
+});
diff --git a/public/app/features/playlist/playlistRoutes.js b/public/app/features/playlist/playlistRoutes.js
new file mode 100644
index 00000000000..2c2515200ae
--- /dev/null
+++ b/public/app/features/playlist/playlistRoutes.js
@@ -0,0 +1,73 @@
+define([
+ 'angular',
+ 'app/core/config',
+ 'lodash'
+],
+function (angular, config, _) {
+ 'use strict';
+
+ var module = angular.module('grafana.routes');
+
+ module.config(function($routeProvider) {
+ $routeProvider
+ .when('/playlists', {
+ templateUrl: 'app/features/playlist/partials/playlists.html',
+ controller : 'PlaylistsCtrl',
+ resolve: {
+ playlists: function (backendSrv) {
+ return backendSrv.get('/api/playlists');
+ }
+ }
+ })
+ .when('/playlists/create', {
+ templateUrl: 'app/features/playlist/partials/playlist.html',
+ controller : 'PlaylistEditCtrl',
+ resolve: {
+ playlist: function() {
+ return {
+ timespan: '1m'
+ };
+ },
+ dashboards: function() {
+ return [];
+ }
+ }
+ })
+ .when('/playlists/edit/:id', {
+ templateUrl: 'app/features/playlist/partials/playlist.html',
+ controller : 'PlaylistEditCtrl',
+ resolve: {
+ playlist: function(backendSrv, $route) {
+ var playlistId = $route.current.params.id;
+
+ return backendSrv.get('/api/playlists/' + playlistId);
+ },
+ dashboards: function(backendSrv, $route) {
+ var playlistId = $route.current.params.id;
+
+ return backendSrv.get('/api/playlists/' + playlistId + '/dashboards');
+ }
+ }
+ })
+ .when('/playlists/play/:id', {
+ templateUrl: 'app/partials/dashboard.html',
+ controller : 'LoadDashboardCtrl',
+ resolve: {
+ init: function(backendSrv, playlistSrv, $route) {
+ var playlistId = $route.current.params.id;
+
+ return backendSrv.get('/api/playlists/' + playlistId)
+ .then(function(playlist) {
+ return backendSrv.get('/api/playlists/' + playlistId + '/dashboards')
+ .then(function(dashboards) {
+ _.each(dashboards, function(dashboard) {
+ dashboard.uri = 'db/' + dashboard.slug;
+ });
+ playlistSrv.start(dashboards, playlist.timespan);
+ });
+ });
+ }
+ }
+ });
+ });
+});
diff --git a/public/app/features/playlist/playlistsCtrl.js b/public/app/features/playlist/playlistsCtrl.js
new file mode 100644
index 00000000000..57e11bffe21
--- /dev/null
+++ b/public/app/features/playlist/playlistsCtrl.js
@@ -0,0 +1,49 @@
+define([
+ 'angular',
+ 'lodash'
+],
+function (angular, _) {
+ 'use strict';
+
+ var module = angular.module('grafana.controllers');
+
+ module.controller('PlaylistsCtrl', function(
+ playlists,
+ $scope,
+ $location,
+ backendSrv
+ ) {
+ $scope.playlists = playlists;
+
+ $scope.playlistUrl = function(playlist) {
+ return '/playlists/play/' + playlist.id;
+ };
+
+ $scope.removePlaylist = function(playlist) {
+ var modalScope = $scope.$new(true);
+
+ modalScope.playlist = playlist;
+ modalScope.removePlaylist = function() {
+ modalScope.dismiss();
+ _.remove(playlists, {id: playlist.id});
+
+ backendSrv.delete('/api/playlists/' + playlist.id)
+ .then(function() {
+ $scope.appEvent('alert-success', ['Playlist deleted', '']);
+ }, function() {
+ $scope.appEvent('alert-error', ['Unable to delete playlist', '']);
+ playlists.push(playlist);
+ });
+ };
+
+ $scope.appEvent('show-modal', {
+ src: './app/features/playlist/partials/playlist-remove.html',
+ scope: modalScope
+ });
+ };
+
+ $scope.createPlaylist = function() {
+ $location.path('/playlists/create');
+ };
+ });
+});
diff --git a/public/app/partials/playlist.html b/public/app/partials/playlist.html
deleted file mode 100644
index 3d12e5af2d6..00000000000
--- a/public/app/partials/playlist.html
+++ /dev/null
@@ -1,100 +0,0 @@
-
-
-
-
-
-
-
-
-
Search result
-
-
-
- {{dashboard.title}}
- |
-
-
- |
-
-
-
- No dashboards found
- |
-
-
-
-
-
Playlist dashboards
-
-
-
-
- {{dashboard.title}}
- |
-
-
- |
-
-
-
- Playlist empty
- |
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/public/app/partials/search.html b/public/app/partials/search.html
index 97bdbc496bf..fab8fd9291a 100644
--- a/public/app/partials/search.html
+++ b/public/app/partials/search.html
@@ -71,10 +71,6 @@
Import
-