mirror of https://github.com/grafana/grafana
parent
3dc3d363fd
commit
bcaaedf2ff
@ -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) |
||||
} |
||||
@ -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 |
||||
} |
||||
@ -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)) |
||||
} |
||||
@ -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 |
||||
} |
||||
@ -0,0 +1,5 @@ |
||||
define([ |
||||
'./playlistsCtrl', |
||||
'./playlistEditCtrl', |
||||
'./playlistRoutes' |
||||
], function () {}); |
||||
@ -0,0 +1,5 @@ |
||||
<p class="text-center">Are you sure want to delete "{{ playlist.title }}" playlist?</p> |
||||
<p class="text-center"> |
||||
<button type="button" class="btn btn-danger" ng-click="removePlaylist()">Yes</button> |
||||
<button type="button" class="btn btn-default" ng-click="dismiss()">No</button> |
||||
</p> |
||||
@ -0,0 +1,115 @@ |
||||
<topnav icon="fa fa-fw fa-list" title="Playlists"></topnav> |
||||
|
||||
<div class="page-container" ng-form="playlistEditForm"> |
||||
<div class="page"> |
||||
<div class="row" style="margin-bottom: 10px;"> |
||||
<div> |
||||
<div class="tight-form"> |
||||
<ul class="tight-form-list"> |
||||
<li class="tight-form-item" style="width: 100px"> |
||||
<strong>Title</strong> |
||||
</li> |
||||
<li> |
||||
<input type="text" required ng-model="playlist.title" class="input-xxlarge tight-form-input last"> |
||||
</li> |
||||
</ul> |
||||
<div class="clearfix"></div> |
||||
</div> |
||||
<div class="tight-form"> |
||||
<ul class="tight-form-list"> |
||||
<li class="tight-form-item" style="width: 100px"> |
||||
<strong>Timespan</strong> |
||||
</li> |
||||
<li> |
||||
<input type="text" required ng-model="playlist.timespan" class="input-xxlarge tight-form-input last"> |
||||
</li> |
||||
</ul> |
||||
<div class="clearfix"></div> |
||||
</div> |
||||
</div> |
||||
|
||||
<br> |
||||
|
||||
<div style="display: inline-block"> |
||||
<div class="tight-form"> |
||||
<ul class="tight-form-list"> |
||||
<li class="tight-form-item"> |
||||
Search |
||||
</li> |
||||
<li> |
||||
<input type="text" |
||||
class="tight-form-input input-xlarge last" |
||||
ng-model="searchQuery" |
||||
placeholder="dashboard title" |
||||
ng-trim="true" |
||||
ng-change="search()"> |
||||
</li> |
||||
</ul> |
||||
<div class="clearfix"></div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<div class="span6"> |
||||
<h5>Playlist dashboards</h5> |
||||
</div> |
||||
</div> |
||||
|
||||
<div class="row"> |
||||
<div class="span6"> |
||||
<table class="grafana-options-table"> |
||||
<tr ng-repeat="dashboard in filteredDashboards"> |
||||
<td style="white-space: nowrap;"> |
||||
{{dashboard.title}} |
||||
</td> |
||||
<td style="text-align: center"> |
||||
<button class="btn btn-inverse btn-mini pull-right" ng-click="addDashboard(dashboard)"> |
||||
<i class="fa fa-plus"></i> |
||||
Add to playlist |
||||
</button> |
||||
</td> |
||||
</tr> |
||||
<tr ng-if="isSearchResultsEmpty() && !isSearchQueryEmpty()"> |
||||
<td colspan="2"> |
||||
<i class="fa fa-warning"></i> No dashboards found |
||||
</td> |
||||
</tr> |
||||
<tr ng-if="isSearchQueryEmpty() && isPlaylistEmpty()"> |
||||
<td colspan="2"> |
||||
<i class="fa fa-warning"></i> Playlist empty |
||||
</td> |
||||
</tr> |
||||
</table> |
||||
</div> |
||||
<div class="span6"> |
||||
<table class="grafana-options-table"> |
||||
<tr ng-repeat="dashboard in dashboards"> |
||||
<td style="white-space: nowrap;"> |
||||
{{dashboard.title}} |
||||
</td> |
||||
<td style="text-align: center"> |
||||
<button class="btn btn-inverse btn-mini pull-right" ng-click="removeDashboard(dashboard)"> |
||||
<i class="fa fa-remove"></i> |
||||
</button> |
||||
</td> |
||||
</tr> |
||||
</table> |
||||
</div> |
||||
</div> |
||||
|
||||
<br> |
||||
<br> |
||||
|
||||
<div class="pull-left"> |
||||
<div class="tight-form"> |
||||
<button type="button" |
||||
class="btn btn-success" |
||||
ng-disabled="playlistEditForm.$invalid || isPlaylistEmpty()" |
||||
ng-click="savePlaylist(playlist, dashboards)">Save</button> |
||||
<button type="button" |
||||
class="btn btn-default" |
||||
ng-click="backToList()">Cancel</button> |
||||
</div> |
||||
</div> |
||||
|
||||
<div class="clearfix"></div> |
||||
</div> |
||||
@ -0,0 +1,43 @@ |
||||
<topnav icon="fa fa-fw fa-list" title="Playlists"></topnav> |
||||
|
||||
<div class="page-container"> |
||||
<div class="page"> |
||||
<button type="submit" class="btn btn-success" ng-click="createPlaylist()">Create playlist</button> |
||||
|
||||
<br> |
||||
<br> |
||||
|
||||
<div ng-if="playlists.length === 0"> |
||||
<em>No saved playlists</em> |
||||
</div> |
||||
|
||||
<table class="grafana-options-table" ng-if="playlists.length > 0"> |
||||
<tr> |
||||
<td><strong>Title</strong></td> |
||||
<td><strong>Url</strong></td> |
||||
<td></td> |
||||
<td></td> |
||||
</tr> |
||||
<tr ng-repeat="playlist in playlists"> |
||||
<td style="width:1%"> |
||||
{{playlist.title}} |
||||
</td> |
||||
<td style="width:90%"> |
||||
<a href="{{ playlistUrl(playlist) }}">{{ playlistUrl(playlist) }}</a> |
||||
</td> |
||||
<td style="width: 1%"> |
||||
<a href="playlists/edit/{{playlist.id}}" class="btn btn-inverse btn-mini"> |
||||
<i class="fa fa-edit"></i> |
||||
Edit |
||||
</a> |
||||
</td> |
||||
<td style="width: 1%"> |
||||
<a ng-click="removePlaylist(playlist)" class="btn btn-danger btn-mini"> |
||||
<i class="fa fa-remove"></i> |
||||
</a> |
||||
</td> |
||||
</tr> |
||||
</table> |
||||
|
||||
</div> |
||||
</div> |
||||
@ -0,0 +1,111 @@ |
||||
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.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(); |
||||
}); |
||||
}); |
||||
@ -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); |
||||
}); |
||||
}); |
||||
} |
||||
} |
||||
}); |
||||
}); |
||||
}); |
||||
@ -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'); |
||||
}; |
||||
}); |
||||
}); |
||||
Loading…
Reference in new issue