mirror of
https://github.com/Poniverse/Pony.fm.git
synced 2024-11-25 06:27:59 +01:00
Admins can modify show songs
This commit is contained in:
parent
73de10cff4
commit
59b0ebd64e
19 changed files with 789 additions and 2 deletions
76
app/Commands/CreateShowSongCommand.php
Normal file
76
app/Commands/CreateShowSongCommand.php
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pony.fm - A community for pony fan music.
|
||||||
|
* Copyright (C) 2016 Josef Citrine
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Poniverse\Ponyfm\Commands;
|
||||||
|
|
||||||
|
use Gate;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Poniverse\Ponyfm\Models\ShowSong;
|
||||||
|
use Validator;
|
||||||
|
|
||||||
|
class CreateShowSongCommand extends CommandBase
|
||||||
|
{
|
||||||
|
/** @var ShowSong */
|
||||||
|
private $_songName;
|
||||||
|
|
||||||
|
public function __construct($songName)
|
||||||
|
{
|
||||||
|
$this->_songName = $songName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorize()
|
||||||
|
{
|
||||||
|
return Gate::allows('create-show-song');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws \Exception
|
||||||
|
* @return CommandResponse
|
||||||
|
*/
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$slug = Str::slug($this->_songName);
|
||||||
|
|
||||||
|
$rules = [
|
||||||
|
'title' => 'required|unique:show_songs,title,NULL,id,deleted_at,NULL|max:250',
|
||||||
|
'slug' => 'required|unique:show_songs,slug,NULL,id,deleted_at,NULL'
|
||||||
|
];
|
||||||
|
|
||||||
|
$validator = Validator::make([
|
||||||
|
'title' => $this->_songName,
|
||||||
|
'slug' => $slug
|
||||||
|
], $rules);
|
||||||
|
|
||||||
|
if ($validator->fails()) {
|
||||||
|
return CommandResponse::fail($validator);
|
||||||
|
}
|
||||||
|
|
||||||
|
ShowSong::create([
|
||||||
|
'title' => $this->_songName,
|
||||||
|
'slug' => $slug,
|
||||||
|
'lyrics' => ''
|
||||||
|
]);
|
||||||
|
|
||||||
|
return CommandResponse::succeed(['message' => 'Song created!']);
|
||||||
|
}
|
||||||
|
}
|
76
app/Commands/DeleteShowSongCommand.php
Normal file
76
app/Commands/DeleteShowSongCommand.php
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pony.fm - A community for pony fan music.
|
||||||
|
* Copyright (C) 2016 Josef Citrine
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Poniverse\Ponyfm\Commands;
|
||||||
|
|
||||||
|
use Gate;
|
||||||
|
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||||
|
use Poniverse\Ponyfm\Models\ShowSong;
|
||||||
|
use Poniverse\Ponyfm\Jobs\DeleteShowSong;
|
||||||
|
use Validator;
|
||||||
|
|
||||||
|
class DeleteShowSongCommand extends CommandBase
|
||||||
|
{
|
||||||
|
use DispatchesJobs;
|
||||||
|
|
||||||
|
|
||||||
|
/** @var ShowSong */
|
||||||
|
private $_songToDelete;
|
||||||
|
private $_destinationSong;
|
||||||
|
|
||||||
|
public function __construct($songId, $destinationSongId) {
|
||||||
|
$this->_songToDelete = ShowSong::find($songId);
|
||||||
|
$this->_destinationSong = ShowSong::find($destinationSongId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorize() {
|
||||||
|
return Gate::allows('delete', $this->_destinationSong);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws \Exception
|
||||||
|
* @return CommandResponse
|
||||||
|
*/
|
||||||
|
public function execute() {
|
||||||
|
$rules = [
|
||||||
|
'song_to_delete' => 'required',
|
||||||
|
'destination_song' => 'required',
|
||||||
|
];
|
||||||
|
|
||||||
|
// The validation will fail if the genres don't exist
|
||||||
|
// because they'll be null.
|
||||||
|
$validator = Validator::make([
|
||||||
|
'song_to_delete' => $this->_songToDelete,
|
||||||
|
'destination_song' => $this->_destinationSong,
|
||||||
|
], $rules);
|
||||||
|
|
||||||
|
|
||||||
|
if ($validator->fails()) {
|
||||||
|
return CommandResponse::fail($validator);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->dispatch(new DeleteShowSong($this->_songToDelete, $this->_destinationSong));
|
||||||
|
|
||||||
|
return CommandResponse::succeed(['message' => 'Song deleted!']);
|
||||||
|
}
|
||||||
|
}
|
83
app/Commands/RenameShowSongCommand.php
Normal file
83
app/Commands/RenameShowSongCommand.php
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pony.fm - A community for pony fan music.
|
||||||
|
* Copyright (C) 2016 Josef Citrine
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Poniverse\Ponyfm\Commands;
|
||||||
|
|
||||||
|
use Gate;
|
||||||
|
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Poniverse\Ponyfm\Jobs\UpdateTagsForRenamedShowSong;
|
||||||
|
use Poniverse\Ponyfm\Models\ShowSong;
|
||||||
|
use Validator;
|
||||||
|
|
||||||
|
class RenameShowSongCommand extends CommandBase
|
||||||
|
{
|
||||||
|
use DispatchesJobs;
|
||||||
|
|
||||||
|
/** @var Song */
|
||||||
|
private $_song;
|
||||||
|
private $_newName;
|
||||||
|
|
||||||
|
public function __construct($genreId, $newName)
|
||||||
|
{
|
||||||
|
$this->_song = ShowSong::find($genreId);
|
||||||
|
$this->_newName = $newName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorize()
|
||||||
|
{
|
||||||
|
return Gate::allows('rename', $this->_song);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws \Exception
|
||||||
|
* @return CommandResponse
|
||||||
|
*/
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$slug = Str::slug($this->_newName);
|
||||||
|
|
||||||
|
$rules = [
|
||||||
|
'title' => 'required|unique:show_songs,title,'.$this->_song->id.',id|max:250',
|
||||||
|
'slug' => 'required|unique:show_songs,slug,'.$this->_song->id.',id'
|
||||||
|
];
|
||||||
|
|
||||||
|
$validator = Validator::make([
|
||||||
|
'title' => $this->_newName,
|
||||||
|
'slug' => $slug
|
||||||
|
], $rules);
|
||||||
|
|
||||||
|
|
||||||
|
if ($validator->fails()) {
|
||||||
|
return CommandResponse::fail($validator);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_song->title = $this->_newName;
|
||||||
|
$this->_song->slug = $slug;
|
||||||
|
$this->_song->save();
|
||||||
|
|
||||||
|
$this->dispatch(new UpdateTagsForRenamedShowSong($this->_song));
|
||||||
|
|
||||||
|
return CommandResponse::succeed(['message' => 'Show song renamed!']);
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,7 +27,7 @@ class AdminController extends Controller
|
||||||
{
|
{
|
||||||
public function getIndex()
|
public function getIndex()
|
||||||
{
|
{
|
||||||
return Redirect::to('AdminController@getGenres');
|
return View::make('shared.null');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getGenres()
|
public function getGenres()
|
||||||
|
@ -39,4 +39,9 @@ class AdminController extends Controller
|
||||||
{
|
{
|
||||||
return View::make('shared.null');
|
return View::make('shared.null');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getShowSongs()
|
||||||
|
{
|
||||||
|
return View::make('shared.null');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
68
app/Http/Controllers/Api/Web/ShowSongsController.php
Normal file
68
app/Http/Controllers/Api/Web/ShowSongsController.php
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pony.fm - A community for pony fan music.
|
||||||
|
* Copyright (C) 2016 Josef Citrine
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Poniverse\Ponyfm\Http\Controllers\Api\Web;
|
||||||
|
|
||||||
|
use Input;
|
||||||
|
use Poniverse\Ponyfm\Commands\CreateShowSongCommand;
|
||||||
|
use Poniverse\Ponyfm\Commands\DeleteShowSongCommand;
|
||||||
|
use Poniverse\Ponyfm\Commands\RenameShowSongCommand;
|
||||||
|
use Poniverse\Ponyfm\Models\ShowSong;
|
||||||
|
use Poniverse\Ponyfm\Http\Controllers\ApiControllerBase;
|
||||||
|
use Response;
|
||||||
|
|
||||||
|
|
||||||
|
class ShowSongsController extends ApiControllerBase
|
||||||
|
{
|
||||||
|
public function getIndex()
|
||||||
|
{
|
||||||
|
$this->authorize('access-admin-area');
|
||||||
|
|
||||||
|
$songs = ShowSong::with(['trackCountRelation' => function($query) {
|
||||||
|
$query->withTrashed();
|
||||||
|
}])
|
||||||
|
->orderBy('title', 'asc')
|
||||||
|
->select('id', 'title', 'slug')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
return Response::json([
|
||||||
|
'showsongs' => $songs->toArray()
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function postCreate()
|
||||||
|
{
|
||||||
|
$command = new CreateShowSongCommand(Input::get('title'));
|
||||||
|
return $this->execute($command);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function putRename($songId)
|
||||||
|
{
|
||||||
|
$command = new RenameShowSongCommand($songId, Input::get('title'));
|
||||||
|
return $this->execute($command);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function deleteSong($songId)
|
||||||
|
{
|
||||||
|
$command = new DeleteShowSongCommand($songId, Input::get('destination_song_id'));
|
||||||
|
return $this->execute($command);
|
||||||
|
}
|
||||||
|
}
|
|
@ -42,7 +42,7 @@ class TaxonomiesController extends ApiControllerBase
|
||||||
->where('id', '!=', TrackType::UNCLASSIFIED_TRACK)
|
->where('id', '!=', TrackType::UNCLASSIFIED_TRACK)
|
||||||
->get()->toArray(),
|
->get()->toArray(),
|
||||||
'show_songs' => ShowSong::select('title', 'id', 'slug',
|
'show_songs' => ShowSong::select('title', 'id', 'slug',
|
||||||
DB::raw('(SELECT COUNT(tracks.id) FROM show_song_track INNER JOIN tracks ON tracks.id = show_song_track.track_id WHERE show_song_track.show_song_id = show_songs.id AND tracks.published_at IS NOT NULL) AS track_count'))->get()->toArray()
|
DB::raw('(SELECT COUNT(tracks.id) FROM show_song_track INNER JOIN tracks ON tracks.id = show_song_track.track_id WHERE show_song_track.show_song_id = show_songs.id AND tracks.published_at IS NOT NULL) AS track_count'))->orderBy('title')->get()->toArray()
|
||||||
], 200);
|
], 200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -161,6 +161,11 @@ Route::group(['prefix' => 'api/web'], function() {
|
||||||
Route::put('/genres/{id}', 'Api\Web\GenresController@putRename')->where('id', '\d+');
|
Route::put('/genres/{id}', 'Api\Web\GenresController@putRename')->where('id', '\d+');
|
||||||
Route::delete('/genres/{id}', 'Api\Web\GenresController@deleteGenre')->where('id', '\d+');
|
Route::delete('/genres/{id}', 'Api\Web\GenresController@deleteGenre')->where('id', '\d+');
|
||||||
|
|
||||||
|
Route::get('/showsongs', 'Api\Web\ShowSongsController@getIndex');
|
||||||
|
Route::post('/showsongs', 'Api\Web\ShowSongsController@postCreate');
|
||||||
|
Route::put('/showsongs/{id}', 'Api\Web\ShowSongsController@putRename')->where('id', '\d+');
|
||||||
|
Route::delete('/showsongs/{id}', 'Api\Web\ShowSongsController@deleteSong')->where('id', '\d+');
|
||||||
|
|
||||||
Route::get('/tracks', 'Api\Web\TracksController@getAllTracks');
|
Route::get('/tracks', 'Api\Web\TracksController@getAllTracks');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -171,6 +176,7 @@ Route::group(['prefix' => 'api/web'], function() {
|
||||||
Route::group(['prefix' => 'admin', 'middleware' => ['auth', 'can:access-admin-area']], function() {
|
Route::group(['prefix' => 'admin', 'middleware' => ['auth', 'can:access-admin-area']], function() {
|
||||||
Route::get('/genres', 'AdminController@getGenres');
|
Route::get('/genres', 'AdminController@getGenres');
|
||||||
Route::get('/tracks', 'AdminController@getTracks');
|
Route::get('/tracks', 'AdminController@getTracks');
|
||||||
|
Route::get('/show-songs', 'AdminController@getShowSongs');
|
||||||
Route::get('/', 'AdminController@getIndex');
|
Route::get('/', 'AdminController@getIndex');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
89
app/Jobs/DeleteShowSong.php
Normal file
89
app/Jobs/DeleteShowSong.php
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pony.fm - A community for pony fan music.
|
||||||
|
* Copyright (C) 2016 Josef Citrine
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Poniverse\Ponyfm\Jobs;
|
||||||
|
|
||||||
|
use Auth;
|
||||||
|
use DB;
|
||||||
|
use Poniverse\Ponyfm\Models\ShowSong;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Contracts\Bus\SelfHandling;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Poniverse\Ponyfm\Models\Track;
|
||||||
|
use SerializesModels;
|
||||||
|
|
||||||
|
class DeleteShowSong extends Job implements SelfHandling, ShouldQueue
|
||||||
|
{
|
||||||
|
use InteractsWithQueue, SerializesModels;
|
||||||
|
|
||||||
|
protected $executingUser;
|
||||||
|
protected $songToDelete;
|
||||||
|
protected $destinationSong;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
*
|
||||||
|
* @param ShowSong $songToDelete
|
||||||
|
* @param ShowSong $destinationSong
|
||||||
|
*/
|
||||||
|
public function __construct(ShowSong $songToDelete, ShowSong $destinationSong)
|
||||||
|
{
|
||||||
|
$this->executingUser = Auth::user();
|
||||||
|
$this->songToDelete = $songToDelete;
|
||||||
|
$this->destinationSong = $destinationSong;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$this->beforeHandle();
|
||||||
|
|
||||||
|
// The user who kicked off this job is used when generating revision log entries.
|
||||||
|
Auth::login($this->executingUser);
|
||||||
|
|
||||||
|
// This is done instead of a single UPDATE query in order to
|
||||||
|
// generate revision logs for the change.
|
||||||
|
$this->songToDelete->tracks()->chunk(200, function ($tracks) {
|
||||||
|
foreach ($tracks as $track) {
|
||||||
|
/** @var Track $track */
|
||||||
|
$oldSongs = $track->showSongs;
|
||||||
|
$newSongs = [];
|
||||||
|
|
||||||
|
foreach ($oldSongs as $key => $showSong) {
|
||||||
|
if ($showSong->id == $this->songToDelete->id) {
|
||||||
|
$newSongs[$key] = $this->destinationSong->id;
|
||||||
|
} else {
|
||||||
|
$newSongs[$key] = $showSong->id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$track->showSongs()->sync($newSongs);
|
||||||
|
$track->save();
|
||||||
|
$track->updateTags();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->songToDelete->delete();
|
||||||
|
}
|
||||||
|
}
|
99
app/Jobs/UpdateTagsForRenamedShowSong.php
Normal file
99
app/Jobs/UpdateTagsForRenamedShowSong.php
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pony.fm - A community for pony fan music.
|
||||||
|
* Copyright (C) 2016 Josef Citrine
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Poniverse\Ponyfm\Jobs;
|
||||||
|
|
||||||
|
use Auth;
|
||||||
|
use Cache;
|
||||||
|
use Log;
|
||||||
|
use Poniverse\Ponyfm\Models\ShowSong;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Contracts\Bus\SelfHandling;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Poniverse\Ponyfm\Models\Track;
|
||||||
|
use SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class RenameGenre
|
||||||
|
*
|
||||||
|
* NOTE: It is assumed that the genre passed into this job has already been renamed!
|
||||||
|
* All this job does is update the tags in that genre's tracks.
|
||||||
|
*
|
||||||
|
* @package Poniverse\Ponyfm\Jobs
|
||||||
|
*/
|
||||||
|
class UpdateTagsForRenamedShowSong extends Job implements SelfHandling, ShouldQueue
|
||||||
|
{
|
||||||
|
use InteractsWithQueue, SerializesModels;
|
||||||
|
|
||||||
|
protected $executingUser;
|
||||||
|
protected $songThatWasRenamed;
|
||||||
|
protected $lockKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
*
|
||||||
|
* @param ShowSong $songThatWasRenamed
|
||||||
|
*/
|
||||||
|
public function __construct(ShowSong $songThatWasRenamed)
|
||||||
|
{
|
||||||
|
$this->executingUser = Auth::user();
|
||||||
|
$this->songThatWasRenamed = $songThatWasRenamed;
|
||||||
|
|
||||||
|
$this->lockKey = "show-song-{$this->songThatWasRenamed->id}-tag-update-lock";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$this->beforeHandle();
|
||||||
|
|
||||||
|
// The user who kicked off this job is used when generating revision log entries.
|
||||||
|
Auth::login($this->executingUser);
|
||||||
|
|
||||||
|
// "Lock" this genre to prevent race conditions
|
||||||
|
if (Cache::has($this->lockKey)) {
|
||||||
|
Log::info("Tag updates for the \"{$this->songThatWasRenamed->title}\" song are currently in progress! Will try again in 30 seconds.");
|
||||||
|
$this->release(30);
|
||||||
|
return;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Cache::forever($this->lockKey, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$this->songThatWasRenamed->tracks()->chunk(200, function ($tracks) {
|
||||||
|
foreach ($tracks as $track) {
|
||||||
|
/** @var Track $track */
|
||||||
|
$track->updateTags();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Cache::forget($this->lockKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function failed()
|
||||||
|
{
|
||||||
|
Cache::forget($this->lockKey);
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
namespace Poniverse\Ponyfm\Models;
|
namespace Poniverse\Ponyfm\Models;
|
||||||
|
|
||||||
|
use DB;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,4 +34,15 @@ use Illuminate\Database\Eloquent\Model;
|
||||||
class ShowSong extends Model
|
class ShowSong extends Model
|
||||||
{
|
{
|
||||||
protected $table = 'show_songs';
|
protected $table = 'show_songs';
|
||||||
|
protected $fillable = ['title', 'slug', 'lyrics'];
|
||||||
|
|
||||||
|
public function trackCountRelation() {
|
||||||
|
return $this->belongsToMany(Track::class)
|
||||||
|
->select(['show_song_id', DB::raw('count(*) as track_count')])
|
||||||
|
->groupBy('show_song_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tracks(){
|
||||||
|
return $this->belongsToMany(Track::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
17
app/Policies/ShowSongPolicy.php
Normal file
17
app/Policies/ShowSongPolicy.php
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Poniverse\Ponyfm\Policies;
|
||||||
|
|
||||||
|
use Poniverse\Ponyfm\Models\ShowSong;
|
||||||
|
use Poniverse\Ponyfm\Models\User;
|
||||||
|
|
||||||
|
class ShowSongPolicy
|
||||||
|
{
|
||||||
|
public function rename(User $user, ShowSong $song) {
|
||||||
|
return $user->hasRole('admin');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete(User $user, ShowSong $song) {
|
||||||
|
return $user->hasRole('admin');
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,9 +26,11 @@ use Poniverse\Ponyfm\Models\Album;
|
||||||
use Poniverse\Ponyfm\Models\Genre;
|
use Poniverse\Ponyfm\Models\Genre;
|
||||||
use Poniverse\Ponyfm\Policies\AlbumPolicy;
|
use Poniverse\Ponyfm\Policies\AlbumPolicy;
|
||||||
use Poniverse\Ponyfm\Policies\GenrePolicy;
|
use Poniverse\Ponyfm\Policies\GenrePolicy;
|
||||||
|
use Poniverse\Ponyfm\Policies\ShowSongPolicy;
|
||||||
use Poniverse\Ponyfm\Policies\TrackPolicy;
|
use Poniverse\Ponyfm\Policies\TrackPolicy;
|
||||||
use Poniverse\Ponyfm\Models\Track;
|
use Poniverse\Ponyfm\Models\Track;
|
||||||
use Poniverse\Ponyfm\Models\User;
|
use Poniverse\Ponyfm\Models\User;
|
||||||
|
use Poniverse\Ponyfm\Models\ShowSong;
|
||||||
use Poniverse\Ponyfm\Policies\UserPolicy;
|
use Poniverse\Ponyfm\Policies\UserPolicy;
|
||||||
|
|
||||||
class AuthServiceProvider extends ServiceProvider
|
class AuthServiceProvider extends ServiceProvider
|
||||||
|
@ -43,6 +45,7 @@ class AuthServiceProvider extends ServiceProvider
|
||||||
Track::class => TrackPolicy::class,
|
Track::class => TrackPolicy::class,
|
||||||
Album::class => AlbumPolicy::class,
|
Album::class => AlbumPolicy::class,
|
||||||
User::class => UserPolicy::class,
|
User::class => UserPolicy::class,
|
||||||
|
ShowSong::class => ShowSongPolicy::class
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -61,6 +64,10 @@ class AuthServiceProvider extends ServiceProvider
|
||||||
return $user->hasRole('admin');
|
return $user->hasRole('admin');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$gate->define('create-show-song', function(User $user) {
|
||||||
|
return $user->hasRole('admin');
|
||||||
|
});
|
||||||
|
|
||||||
$this->registerPolicies($gate);
|
$this->registerPolicies($gate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
|
||||||
|
class AddTimestampsToShowSongs extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('show_songs', function (Blueprint $table) {
|
||||||
|
$table->timestamp('created_at')->default(DB::raw('CURRENT_TIMESTAMP'));
|
||||||
|
$table->timestamp('updated_at')->default(DB::raw('CURRENT_TIMESTAMP'));
|
||||||
|
$table->timestamp('deleted_at')->default(DB::raw('CURRENT_TIMESTAMP'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('show_songs', function (Blueprint $table) {
|
||||||
|
$table->dropColumn(['created_at', 'updated_at', 'deleted_at']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
<ul class="tabs">
|
<ul class="tabs">
|
||||||
<li ng-class="{active: stateIncludes('admin.tracks')}"><a href="/admin/tracks">All Tracks</a></li>
|
<li ng-class="{active: stateIncludes('admin.tracks')}"><a href="/admin/tracks">All Tracks</a></li>
|
||||||
<li ng-class="{active: stateIncludes('admin.genres')}"><a href="/admin/genres">Genres</a></li>
|
<li ng-class="{active: stateIncludes('admin.genres')}"><a href="/admin/genres">Genres</a></li>
|
||||||
|
<li ng-class="{active: stateIncludes('admin.showsongs')}"><a href="/admin/show-songs">Show Songs</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<ui-view></ui-view>
|
<ui-view></ui-view>
|
||||||
|
|
55
public/templates/admin/show-songs.html
Normal file
55
public/templates/admin/show-songs.html
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
<h1>Show Song Editor</h1>
|
||||||
|
|
||||||
|
<div class="stretch-to-bottom">
|
||||||
|
|
||||||
|
<section class="genre-creator">
|
||||||
|
<h2>Add show song</h2>
|
||||||
|
|
||||||
|
<p>Enter a show song name and press enter to create it!</p>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="x-large"
|
||||||
|
ng-class="{'x-saving': isCreating, 'x-error': hasCreationError}"
|
||||||
|
ng-model="songToCreate"
|
||||||
|
pfm-on-enter="createSong(songToCreate)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="alert alert-danger" ng-show="hasCreationError">
|
||||||
|
{{ createSongError }}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="genre-list">
|
||||||
|
<h2>Rename & delete songs</h2>
|
||||||
|
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<th>Song</th>
|
||||||
|
<th class="-status"></th>
|
||||||
|
<th># of tracks (including deleted)</th>
|
||||||
|
<th class="-actions">Actions</th>
|
||||||
|
</thead>
|
||||||
|
<tr ng-repeat="song in showsongs">
|
||||||
|
<td>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="x-large"
|
||||||
|
ng-class="{'x-saving': song.isSaving, 'x-error': song.isError}"
|
||||||
|
ng-model="song.title"
|
||||||
|
pfm-on-enter="renameSong(song)"
|
||||||
|
/>
|
||||||
|
<div class="alert alert-danger" ng-show="song.isError">
|
||||||
|
{{ song.errorMessage }}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td><i ng-show="song.isSaving" class="fa fa-cog fa-spin fa-lg"></i></td>
|
||||||
|
<td><a ng-href="{{ song.url }}">{{ song.track_count_relation[0].track_count }}</a></td>
|
||||||
|
<td class="-actions">
|
||||||
|
<button class="btn btn-warning" ng-hide="mergeInProgress" ng-click="startMerge(song)">Merge songs in…</button>
|
||||||
|
<button class="btn btn-danger" ng-show="mergeInProgress && destinationSong.id != song.id" ng-click="finishMerge(song)">Merge into <em>{{ destinationSong.title }}</em>…</button>
|
||||||
|
<button class="btn btn-warning" ng-show="mergeInProgress && destinationSong.id == song.id" ng-click="cancelMerge()">Cancel merge</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
</div>
|
|
@ -30,6 +30,8 @@
|
||||||
<li><a href="https://github.com/CodeScaleInc/ffmpeg-php" target="_blank">FFmpegPHP</a> - for providing a sweet PHP interface to ffmpeg</li>
|
<li><a href="https://github.com/CodeScaleInc/ffmpeg-php" target="_blank">FFmpegPHP</a> - for providing a sweet PHP interface to ffmpeg</li>
|
||||||
<li><a href="https://github.com/barryvdh/laravel-ide-helper" target="_blank">Laravel IDE Helper Generator</a> - for making our IDE useful</li>
|
<li><a href="https://github.com/barryvdh/laravel-ide-helper" target="_blank">Laravel IDE Helper Generator</a> - for making our IDE useful</li>
|
||||||
<li><a href="https://github.com/In-Touch/laravel-newrelic" target="_blank">Laravel NewRelic Service Provider</a> - for making it easy to monitor Pony.fm''s performance</li>
|
<li><a href="https://github.com/In-Touch/laravel-newrelic" target="_blank">Laravel NewRelic Service Provider</a> - for making it easy to monitor Pony.fm''s performance</li>
|
||||||
|
<li><a href="https://github.com/filp/whoops">Whoops</a> - for </li>
|
||||||
|
<li><a href="https://github.com/filp/whoops">Whoops</a> - for </li>
|
||||||
<li><a href="https://www.vagrantup.com/" target="_blank">Vagrant</a> - for making cross-platform dev environments possible</li>
|
<li><a href="https://www.vagrantup.com/" target="_blank">Vagrant</a> - for making cross-platform dev environments possible</li>
|
||||||
<li><a href="http://atomicparsley.sourceforge.net/" target="_blank">AtomicParsley</a> - for making it easy to work with tags in M4A files</li>
|
<li><a href="http://atomicparsley.sourceforge.net/" target="_blank">AtomicParsley</a> - for making it easy to work with tags in M4A files</li>
|
||||||
<li><a href="https://xiph.org/flac/" target="_blank">FLAC</a> - FLAC is best audio codec /)</li>
|
<li><a href="https://xiph.org/flac/" target="_blank">FLAC</a> - FLAC is best audio codec /)</li>
|
||||||
|
|
|
@ -273,6 +273,11 @@ ponyfm.config [
|
||||||
controller: 'admin-genres'
|
controller: 'admin-genres'
|
||||||
templateUrl: '/templates/admin/genres.html'
|
templateUrl: '/templates/admin/genres.html'
|
||||||
|
|
||||||
|
state.state 'admin.showsongs',
|
||||||
|
url: '/show-songs'
|
||||||
|
controller: 'admin-show-songs'
|
||||||
|
templateUrl: '/templates/admin/show-songs.html'
|
||||||
|
|
||||||
state.state 'admin.tracks',
|
state.state 'admin.tracks',
|
||||||
url: '/tracks'
|
url: '/tracks'
|
||||||
controller: 'admin-tracks'
|
controller: 'admin-tracks'
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
# Pony.fm - A community for pony fan music.
|
||||||
|
# Copyright (C) 2015 Peter Deltchev
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
module.exports = angular.module('ponyfm').controller 'admin-show-songs', [
|
||||||
|
'$scope', '$state', 'admin-show-songs'
|
||||||
|
($scope, $state, showsongs) ->
|
||||||
|
|
||||||
|
$scope.showsongs = []
|
||||||
|
|
||||||
|
$scope.isCreating = false
|
||||||
|
$scope.songToCreate = ''
|
||||||
|
$scope.hasCreationError = false
|
||||||
|
$scope.createSongError = ''
|
||||||
|
|
||||||
|
# Used for merging/deleting show songs
|
||||||
|
$scope.mergeInProgress = false
|
||||||
|
$scope.songToDelete = null
|
||||||
|
|
||||||
|
setSongs = (showsongs) ->
|
||||||
|
$scope.showsongs = []
|
||||||
|
for song in showsongs
|
||||||
|
song.isSaving = false
|
||||||
|
song.isError = false
|
||||||
|
$scope.showsongs.push(song)
|
||||||
|
|
||||||
|
loadSongs = () ->
|
||||||
|
showsongs.fetch().done setSongs
|
||||||
|
|
||||||
|
loadSongs()
|
||||||
|
|
||||||
|
|
||||||
|
$scope.createSong = (songName) ->
|
||||||
|
$scope.isCreating = true
|
||||||
|
showsongs.create(songName)
|
||||||
|
.done (response) ->
|
||||||
|
$scope.hasCreationError = false
|
||||||
|
$scope.songToCreate = ''
|
||||||
|
loadSongs()
|
||||||
|
.fail (response) ->
|
||||||
|
$scope.hasCreationError = true
|
||||||
|
$scope.createSongError = response
|
||||||
|
console.log(response)
|
||||||
|
.always (response) ->
|
||||||
|
$scope.isCreating = false
|
||||||
|
|
||||||
|
|
||||||
|
# Renames the given song
|
||||||
|
$scope.renameSong = (song) ->
|
||||||
|
song.isSaving = true
|
||||||
|
showsongs.rename(song.id, song.title)
|
||||||
|
.done (response)->
|
||||||
|
song.isError = false
|
||||||
|
.fail (response)->
|
||||||
|
song.errorMessage = response
|
||||||
|
song.isError = true
|
||||||
|
.always (response)->
|
||||||
|
song.isSaving = false
|
||||||
|
|
||||||
|
|
||||||
|
$scope.startMerge = (destinationSong) ->
|
||||||
|
$scope.destinationSong = destinationSong
|
||||||
|
$scope.mergeInProgress = true
|
||||||
|
|
||||||
|
$scope.cancelMerge = () ->
|
||||||
|
$scope.destinationSong = null
|
||||||
|
$scope.mergeInProgress = false
|
||||||
|
|
||||||
|
$scope.finishMerge = (songToDelete) ->
|
||||||
|
showsongs.merge(songToDelete.id, $scope.destinationSong.id)
|
||||||
|
.done (response) ->
|
||||||
|
loadSongs()
|
||||||
|
]
|
|
@ -0,0 +1,68 @@
|
||||||
|
# Pony.fm - A community for pony fan music.
|
||||||
|
# Copyright (C) 2015 Peter Deltchev
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
module.exports = angular.module('ponyfm').factory('admin-show-songs', [
|
||||||
|
'$rootScope', '$http'
|
||||||
|
($rootScope, $http) ->
|
||||||
|
def = null
|
||||||
|
showsongs = []
|
||||||
|
|
||||||
|
self =
|
||||||
|
fetch: () ->
|
||||||
|
url = '/api/web/admin/showsongs'
|
||||||
|
def = new $.Deferred()
|
||||||
|
$http.get(url).success (showsongs) ->
|
||||||
|
def.resolve(showsongs['showsongs'])
|
||||||
|
def.promise()
|
||||||
|
|
||||||
|
create: (name) ->
|
||||||
|
url = '/api/web/admin/showsongs'
|
||||||
|
def = new $.Deferred()
|
||||||
|
$http.post(url, {title: name})
|
||||||
|
.success (response) ->
|
||||||
|
def.resolve(response)
|
||||||
|
.error (response) ->
|
||||||
|
def.reject(response)
|
||||||
|
|
||||||
|
def.promise()
|
||||||
|
|
||||||
|
rename: (song_id, new_name) ->
|
||||||
|
url = "/api/web/admin/showsongs/#{song_id}"
|
||||||
|
def = new $.Deferred()
|
||||||
|
|
||||||
|
$http.put(url, {title: new_name})
|
||||||
|
.success (response)->
|
||||||
|
def.resolve(response)
|
||||||
|
|
||||||
|
.error (response)->
|
||||||
|
def.reject(response)
|
||||||
|
|
||||||
|
def.promise()
|
||||||
|
|
||||||
|
merge: (song_id_to_delete, destination_song_id) ->
|
||||||
|
url = "/api/web/admin/showsongs/#{song_id_to_delete}"
|
||||||
|
def = new $.Deferred()
|
||||||
|
|
||||||
|
$http.delete(url, {params: {destination_song_id: destination_song_id}})
|
||||||
|
.success (response)->
|
||||||
|
def.resolve(response)
|
||||||
|
|
||||||
|
.error (response)->
|
||||||
|
def.reject(response)
|
||||||
|
|
||||||
|
def.promise()
|
||||||
|
self
|
||||||
|
])
|
Loading…
Reference in a new issue