From 4d119ff758e98df4fd01a79cf0267386c64a2e58 Mon Sep 17 00:00:00 2001 From: Peter Deltchev Date: Mon, 9 Nov 2015 11:35:30 -0800 Subject: [PATCH] #8: Implemented the track upload API. --- app/Commands/UploadTrackCommand.php | 7 +- app/Exceptions.php | 14 -- .../InvalidAccessTokenException.php | 32 +++ app/Exceptions/TrackFileNotFoundException.php | 34 +++ .../Controllers/Api/V1/TracksController.php | 49 +++- .../Controllers/Api/Web/TracksController.php | 18 +- app/Http/Controllers/ApiControllerBase.php | 7 + app/Http/Controllers/AuthController.php | 20 +- app/Http/Kernel.php | 2 + app/Http/Middleware/AuthenticateOAuth.php | 75 ++++++ app/Http/Middleware/JsonExceptions.php | 55 +++++ app/Http/Middleware/Profiler.php | 3 +- app/Http/Middleware/VerifyCsrfToken.php | 2 +- app/Http/routes.php | 10 +- app/Library/Poniverse/AccessTokenInfo.php | 220 ++++++++++++++++++ app/Library/Poniverse/Poniverse.php | 47 ++++ app/Library/Poniverse/oauth2/Client.php | 7 +- app/Policies/TrackPolicy.php | 35 +++ app/Providers/AppServiceProvider.php | 6 +- app/Providers/AuthServiceProvider.php | 5 +- app/Track.php | 15 +- app/User.php | 38 ++- 22 files changed, 647 insertions(+), 54 deletions(-) delete mode 100644 app/Exceptions.php create mode 100644 app/Exceptions/InvalidAccessTokenException.php create mode 100644 app/Exceptions/TrackFileNotFoundException.php create mode 100644 app/Http/Middleware/AuthenticateOAuth.php create mode 100644 app/Http/Middleware/JsonExceptions.php create mode 100644 app/Library/Poniverse/AccessTokenInfo.php create mode 100644 app/Policies/TrackPolicy.php diff --git a/app/Commands/UploadTrackCommand.php b/app/Commands/UploadTrackCommand.php index f801ff84..8c8fd996 100644 --- a/app/Commands/UploadTrackCommand.php +++ b/app/Commands/UploadTrackCommand.php @@ -70,7 +70,12 @@ class UploadTrackCommand extends CommandBase public function execute() { $user = \Auth::user(); - $trackFile = \Input::file('track'); + $trackFile = \Input::file('track', null); + + if (null === $trackFile) { + return CommandResponse::fail(['track' => ['You must upload an audio file!']]); + } + $audio = \AudioCache::get($trackFile->getPathname()); diff --git a/app/Exceptions.php b/app/Exceptions.php deleted file mode 100644 index 94afa01b..00000000 --- a/app/Exceptions.php +++ /dev/null @@ -1,14 +0,0 @@ -. + */ + +namespace Poniverse\Ponyfm\Exceptions; + +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; + +/** + * Class InvalidAccessTokenException + * @package Poniverse\Ponyfm + * + * This exception indicates that an access token we attempted to introspect + * through the Poniverse API is expired or otherwise unusable. + */ +class InvalidAccessTokenException extends AccessDeniedHttpException {}; diff --git a/app/Exceptions/TrackFileNotFoundException.php b/app/Exceptions/TrackFileNotFoundException.php new file mode 100644 index 00000000..c4eed2e1 --- /dev/null +++ b/app/Exceptions/TrackFileNotFoundException.php @@ -0,0 +1,34 @@ +. + */ + +namespace Poniverse\Ponyfm\Exceptions; + +use Illuminate\Database\Eloquent\ModelNotFoundException; + +/** + * Class TrackFileNotFoundException + * @package Poniverse\Ponyfm + * + * This exception is used to indicate that the requested `TrackFile` object + * does not exist. This is useful when dealing with albums or playlists that + * contain tracks for which no lossless master is available (and thus, lossless + * `TrackFiles` don't exist for). + */ +class TrackFileNotFoundException extends ModelNotFoundException {} diff --git a/app/Http/Controllers/Api/V1/TracksController.php b/app/Http/Controllers/Api/V1/TracksController.php index 773b13dc..c53df319 100644 --- a/app/Http/Controllers/Api/V1/TracksController.php +++ b/app/Http/Controllers/Api/V1/TracksController.php @@ -20,13 +20,56 @@ namespace Poniverse\Ponyfm\Http\Controllers\Api\V1; +use Poniverse\Ponyfm\Commands\UploadTrackCommand; +use Poniverse\Ponyfm\Http\Controllers\ApiControllerBase; use Poniverse\Ponyfm\Image; use Poniverse\Ponyfm\Track; -use Cover; -use Illuminate\Support\Facades\Response; +use Response; -class TracksController extends \ApiControllerBase +class TracksController extends ApiControllerBase { + public function postUploadTrack() { + session_write_close(); + + $response = $this->execute(new UploadTrackCommand()); + $commandData = $response->getData(true); + + if (200 !== $response->getStatusCode()) { + return $response; + } + + $data = [ + 'id' => $commandData['id'], + 'status_url' => action('Api\V1\TracksController@getUploadStatus', ['id' => $commandData['id']]), + 'message' => "This track has been accepted for processing! Poll the status_url to know when it's ready to publish.", + ]; + + $response->setData($data); + $response->setStatusCode(202); + return $response; + } + + + public function getUploadStatus($trackId) { + $track = Track::findOrFail($trackId); + $this->authorize('edit', $track); + + if ($track->status === Track::STATUS_PROCESSING) { + return Response::json(['message' => 'Processing...'], 202); + + } elseif ($track->status === Track::STATUS_COMPLETE) { + return Response::json([ + 'message' => 'Processing complete! The artist must publish the track by visiting its edit_url.', + 'edit_url' => action('ContentController@getTracks', ['id' => $trackId]) + ], 201); + + } else { + // something went wrong + return Response::json(['error' => 'Processing failed!'], 500); + } + } + + public function getTrackRadioDetails($hash) { $track = Track diff --git a/app/Http/Controllers/Api/Web/TracksController.php b/app/Http/Controllers/Api/Web/TracksController.php index b509fe80..9db79679 100644 --- a/app/Http/Controllers/Api/Web/TracksController.php +++ b/app/Http/Controllers/Api/Web/TracksController.php @@ -21,19 +21,19 @@ namespace Poniverse\Ponyfm\Http\Controllers\Api\Web; use Illuminate\Database\Eloquent\ModelNotFoundException; -use Illuminate\Support\Facades\File; +use File; +use Poniverse\Ponyfm\Exceptions\InvalidEncodeOptionsException; use Poniverse\Ponyfm\Commands\DeleteTrackCommand; use Poniverse\Ponyfm\Commands\EditTrackCommand; use Poniverse\Ponyfm\Commands\UploadTrackCommand; use Poniverse\Ponyfm\Http\Controllers\ApiControllerBase; use Poniverse\Ponyfm\Jobs\EncodeTrackFile; use Poniverse\Ponyfm\ResourceLogItem; -use Poniverse\Ponyfm\Track; -use Cover; -use Illuminate\Support\Facades\Auth; -use Illuminate\Support\Facades\Input; -use Illuminate\Support\Facades\Response; use Poniverse\Ponyfm\TrackFile; +use Poniverse\Ponyfm\Track; +use Auth; +use Input; +use Response; class TracksController extends ApiControllerBase { @@ -44,16 +44,15 @@ class TracksController extends ApiControllerBase try { return $this->execute(new UploadTrackCommand()); - } catch (\InvalidEncodeOptions $e) { + } catch (InvalidEncodeOptionsException $e) { } } public function getUploadStatus($trackId) { - // TODO: authorize this - $track = Track::findOrFail($trackId); + $this->authorize('edit', $track); if ($track->status === Track::STATUS_PROCESSING){ return Response::json(['message' => 'Processing...'], 202); @@ -65,7 +64,6 @@ class TracksController extends ApiControllerBase // something went wrong return Response::json(['error' => 'Processing failed!'], 500); } - } public function postDelete($id) diff --git a/app/Http/Controllers/ApiControllerBase.php b/app/Http/Controllers/ApiControllerBase.php index 4f814fa6..414f93f2 100644 --- a/app/Http/Controllers/ApiControllerBase.php +++ b/app/Http/Controllers/ApiControllerBase.php @@ -25,6 +25,13 @@ use Response; abstract class ApiControllerBase extends Controller { + /** + * NOTE: This function is used by the v1 API. If the response JSON format + * it returns changes, don't break the API! + * + * @param CommandBase $command + * @return \Illuminate\Http\JsonResponse + */ protected function execute(CommandBase $command) { if (!$command->authorize()) { diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php index 53bf1bb3..13c1f8ee 100644 --- a/app/Http/Controllers/AuthController.php +++ b/app/Http/Controllers/AuthController.php @@ -96,25 +96,13 @@ class AuthController extends Controller } // Check by login name to see if they already have an account - $localMember = User::where('username', '=', $poniverseUser['username']) - ->where('is_archived', false) - ->first(); + $user = User::findOrCreate($poniverseUser['username'], $poniverseUser['display_name'], $poniverseUser['email']); - if ($localMember) { - return $this->loginRedirect($localMember); + if ($user->wasRecentlyCreated) { + return $this->loginRedirect($user); } - $user = new User; - - $user->username = $poniverseUser['username']; - $user->display_name = $poniverseUser['display_name']; - $user->email = $poniverseUser['email']; - $user->created_at = gmdate("Y-m-d H:i:s", time()); - $user->uses_gravatar = 1; - - $user->save(); - - //We need to insert a new token row :O + // We need to insert a new token row :O $setData['user_id'] = $user->id; $setData['external_user_id'] = $poniverseUser['id']; diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 41fc55d5..9c51a069 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -47,7 +47,9 @@ class Kernel extends HttpKernel protected $routeMiddleware = [ 'auth' => \Poniverse\Ponyfm\Http\Middleware\Authenticate::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, + 'auth.oauth' => \Poniverse\Ponyfm\Http\Middleware\AuthenticateOAuth::class, 'can' => \Poniverse\Ponyfm\Http\Middleware\Authorize::class, + 'json-exceptions' => \Poniverse\Ponyfm\Http\Middleware\JsonExceptions::class, 'guest' => \Poniverse\Ponyfm\Http\Middleware\RedirectIfAuthenticated::class, ]; } diff --git a/app/Http/Middleware/AuthenticateOAuth.php b/app/Http/Middleware/AuthenticateOAuth.php new file mode 100644 index 00000000..b7506cc7 --- /dev/null +++ b/app/Http/Middleware/AuthenticateOAuth.php @@ -0,0 +1,75 @@ +. + */ + +namespace Poniverse\Ponyfm\Http\Middleware; + +use Auth; +use Closure; +use GuzzleHttp; +use Poniverse; +use Poniverse\Ponyfm\User; +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; + +class AuthenticateOAuth +{ + /** + * @var Poniverse + */ + private $poniverse; + + public function __construct(Poniverse $poniverse) { + $this->poniverse = $poniverse; + } + + /** + * Handle an incoming request. + * + * @param \Illuminate\Http\Request $request + * @param \Closure $next + * @param string $requiredScope + * @return mixed + * @throws \OAuth2\Exception + */ + public function handle($request, Closure $next, $requiredScope) + { + // Ensure this is a valid OAuth client. + $accessToken = $request->get('access_token'); + + // check that access token is valid at Poniverse.net + $accessTokenInfo = $this->poniverse->getAccessTokenInfo($accessToken); + + if (!$accessTokenInfo->getIsActive()) { + throw new AccessDeniedHttpException('This access token is expired or invalid!'); + } + + if (!in_array($requiredScope, $accessTokenInfo->getScopes())) { + throw new AccessDeniedHttpException("This access token lacks the '${requiredScope}' scope!"); + } + + // Log in as the given user, creating the account if necessary. + $this->poniverse->setAccessToken($accessToken); + $poniverseUser = $this->poniverse->getUser(); + + $user = User::findOrCreate($poniverseUser['username'], $poniverseUser['display_name'], $poniverseUser['email']); + Auth::login($user); + + return $next($request); + } +} diff --git a/app/Http/Middleware/JsonExceptions.php b/app/Http/Middleware/JsonExceptions.php new file mode 100644 index 00000000..4d74acdc --- /dev/null +++ b/app/Http/Middleware/JsonExceptions.php @@ -0,0 +1,55 @@ +. + */ + +namespace Poniverse\Ponyfm\Http\Middleware; + +use Closure; +use Symfony\Component\HttpKernel\Exception\HttpException; + +/** + * Class JsonExceptions + * @package Poniverse\Ponyfm\Http\Middleware + * + * This middleware turns any HTTP exceptions thrown during the request + * into a JSON response. To be used when implementing the API! + */ +class JsonExceptions +{ + /** + * Handle an incoming request. + * + * @param \Illuminate\Http\Request $request + * @param \Closure $next + * @return mixed + */ + public function handle($request, Closure $next) + { + try { + $response = $next($request); + + } catch (HttpException $e) { + return \Response::json([ + 'message' => $e->getMessage() + ], $e->getStatusCode()); + } + + return $response; + } +} diff --git a/app/Http/Middleware/Profiler.php b/app/Http/Middleware/Profiler.php index ba8214a6..e5c8f66d 100644 --- a/app/Http/Middleware/Profiler.php +++ b/app/Http/Middleware/Profiler.php @@ -50,8 +50,9 @@ class Profiler } catch (\Exception $e) { $response = \Response::make([ 'message' => $e->getMessage(), + 'lineNumber' => $e->getLine(), 'exception' => $e->getTrace() - ], 500); + ], method_exists($e, 'getStatusCode') ? $e->getStatusCode() : 500); $profiler->log('error', $e->__toString(), []); } diff --git a/app/Http/Middleware/VerifyCsrfToken.php b/app/Http/Middleware/VerifyCsrfToken.php index 7273c0dd..ed883077 100644 --- a/app/Http/Middleware/VerifyCsrfToken.php +++ b/app/Http/Middleware/VerifyCsrfToken.php @@ -30,6 +30,6 @@ class VerifyCsrfToken extends BaseVerifier * @var array */ protected $except = [ - // + 'api/*' ]; } diff --git a/app/Http/routes.php b/app/Http/routes.php index 1dc8b931..cc16ab6a 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -66,11 +66,19 @@ Route::get('playlist/{id}-{slug}', 'PlaylistsController@getPlaylist'); Route::get('p{id}', 'PlaylistsController@getShortlink')->where('id', '\d+'); Route::get('p{id}/dl.{extension}', 'PlaylistsController@getDownload' ); -Route::group(['prefix' => 'api/v1'], function() { + + +Route::group(['prefix' => 'api/v1', 'middleware' => 'json-exceptions'], function() { Route::get('/tracks/radio-details/{hash}', 'Api\V1\TracksController@getTrackRadioDetails'); Route::post('/tracks/radio-details/{hash}', 'Api\V1\TracksController@getTrackRadioDetails'); + + Route::group(['middleware' => 'auth.oauth:ponyfm-upload-track'], function() { + Route::post('tracks', 'Api\V1\TracksController@postUploadTrack'); + Route::get('/tracks/{id}/upload-status', 'Api\V1\TracksController@getUploadStatus'); + }); }); + Route::group(['prefix' => 'api/web'], function() { Route::get('/taxonomies/all', 'Api\Web\TaxonomiesController@getAll'); diff --git a/app/Library/Poniverse/AccessTokenInfo.php b/app/Library/Poniverse/AccessTokenInfo.php new file mode 100644 index 00000000..6c9d3917 --- /dev/null +++ b/app/Library/Poniverse/AccessTokenInfo.php @@ -0,0 +1,220 @@ +. + */ + +namespace Poniverse; + +/** + * Class AccessTokenInfo + * + * A container for the fields in the draft OAuth Token Introspection proposal. + * + * @link https://tools.ietf.org/html/draft-richer-oauth-introspection-06 + * @package Poniverse + */ +class AccessTokenInfo { + protected $token; + + protected $isActive; + protected $expiresAt; + protected $issuedAt; + protected $scopes; + protected $clientId; + protected $sub; + protected $userId; + protected $intendedAudience; + protected $issuer; + protected $tokenType; + + public function __construct($accessToken) { + $this->token = $accessToken; + } + + /** + * @return mixed + */ + public function getToken() { + return $this->token; + } + + /** + * @return bool + */ + public function getIsActive() { + return $this->isActive; + } + + /** + * @param bool $isActive + * @return AccessTokenInfo + */ + public function setIsActive($isActive) { + $this->isActive = $isActive; + return $this; + } + + /** + * @return mixed + */ + public function getExpiresAt() { + return $this->expiresAt; + } + + /** + * @param mixed $expiresAt + * @return AccessTokenInfo + */ + public function setExpiresAt($expiresAt) { + $this->expiresAt = $expiresAt; + return $this; + } + + /** + * @return mixed + */ + public function getIssuedAt() { + return $this->issuedAt; + } + + /** + * @param mixed $issuedAt + * @return AccessTokenInfo + */ + public function setIssuedAt($issuedAt) { + $this->issuedAt = $issuedAt; + return $this; + } + + /** + * @return array + */ + public function getScopes() { + return $this->scopes; + } + + /** + * @param array|string $scopes + * @return AccessTokenInfo + */ + public function setScopes($scopes) { + if (is_array($scopes)) { + $this->scopes = $scopes; + } else { + $this->scopes = mb_split(' ', $scopes); + } + + return $this; + } + + /** + * @return mixed + */ + public function getClientId() { + return $this->clientId; + } + + /** + * @param mixed $clientId + * @return AccessTokenInfo + */ + public function setClientId($clientId) { + $this->clientId = $clientId; + return $this; + } + + /** + * @return mixed + */ + public function getSub() { + return $this->sub; + } + + /** + * @param mixed $sub + * @return AccessTokenInfo + */ + public function setSub($sub) { + $this->sub = $sub; + return $this; + } + + /** + * @return mixed + */ + public function getUserId() { + return $this->userId; + } + + /** + * @param mixed $userId + * @return AccessTokenInfo + */ + public function setUserId($userId) { + $this->userId = $userId; + return $this; + } + + /** + * @return mixed + */ + public function getIntendedAudience() { + return $this->intendedAudience; + } + + /** + * @param mixed $intendedAudience + * @return AccessTokenInfo + */ + public function setIntendedAudience($intendedAudience) { + $this->intendedAudience = $intendedAudience; + return $this; + } + + /** + * @return mixed + */ + public function getIssuer() { + return $this->issuer; + } + + /** + * @param mixed $issuer + * @return AccessTokenInfo + */ + public function setIssuer($issuer) { + $this->issuer = $issuer; + return $this; + } + + /** + * @return mixed + */ + public function getTokenType() { + return $this->tokenType; + } + + /** + * @param mixed $tokenType + * @return AccessTokenInfo + */ + public function setTokenType($tokenType) { + $this->tokenType = $tokenType; + return $this; + } +} diff --git a/app/Library/Poniverse/Poniverse.php b/app/Library/Poniverse/Poniverse.php index 7e04a278..d0ec4250 100644 --- a/app/Library/Poniverse/Poniverse.php +++ b/app/Library/Poniverse/Poniverse.php @@ -18,6 +18,9 @@ * along with this program. If not, see . */ +use OAuth2\Client; +use Poniverse\Ponyfm\Exceptions\InvalidAccessTokenException; + /** * Class Poniverse * @@ -111,4 +114,48 @@ class Poniverse { return json_decode($result, true); } + + /** + * Gets information about the given access token. + * + * @link https://tools.ietf.org/html/draft-richer-oauth-introspection-06 + * + * @param $accessTokenToIntrospect + * @return \Poniverse\AccessTokenInfo + * @throws \Poniverse\Ponyfm\InvalidAccessTokenException + * @throws \Symfony\Component\HttpKernel\Exception\HttpException + */ + public function getAccessTokenInfo($accessTokenToIntrospect) + { + $token = $this->client->getAccessToken( + Config::get('poniverse.urls.token'), + Client::GRANT_TYPE_CLIENT_CREDENTIALS, + [] + )['result']['access_token']; + + + $request = \Httpful\Request::post($this->urls['api']. 'meta/introspect?token='.$accessTokenToIntrospect); + + /** @var Httpful\Response $result */ + $result = $request + ->addHeader('Accept', 'application/json') + ->addHeader('Authorization', 'Bearer '.$token) + ->send(); + $data = json_decode($result, true); + + if (404 === $result->code) { + throw new InvalidAccessTokenException('This access token is expired or invalid!'); + } + + if (200 !== $result->code) { + throw new \Symfony\Component\HttpKernel\Exception\HttpException(500, 'An unknown error occurred while contacting the Poniverse API.'); + } + + $tokenInfo = new \Poniverse\AccessTokenInfo($accessTokenToIntrospect); + $tokenInfo + ->setIsActive($data['active']) + ->setScopes($data['scope']); + + return $tokenInfo; + } } diff --git a/app/Library/Poniverse/oauth2/Client.php b/app/Library/Poniverse/oauth2/Client.php index 2b937440..655595e1 100644 --- a/app/Library/Poniverse/oauth2/Client.php +++ b/app/Library/Poniverse/oauth2/Client.php @@ -202,10 +202,11 @@ class Client /** * getAccessToken * - * @param string $token_endpoint Url of the token endpoint - * @param int $grant_type Grant Type ('authorization_code', 'password', 'client_credentials', 'refresh_token', or a custom code (@see GrantType Classes) - * @param array $parameters Array sent to the server (depend on which grant type you're using) + * @param string $token_endpoint Url of the token endpoint + * @param int $grant_type Grant Type ('authorization_code', 'password', 'client_credentials', 'refresh_token', or a custom code (@see GrantType Classes) + * @param array $parameters Array sent to the server (depend on which grant type you're using) * @return array Array of parameters required by the grant_type (CF SPEC) + * @throws Exception */ public function getAccessToken($token_endpoint, $grant_type, array $parameters) { diff --git a/app/Policies/TrackPolicy.php b/app/Policies/TrackPolicy.php new file mode 100644 index 00000000..c137954a --- /dev/null +++ b/app/Policies/TrackPolicy.php @@ -0,0 +1,35 @@ +. + */ + +namespace Poniverse\Ponyfm\Policies; + +use Poniverse\Ponyfm\Track; +use Poniverse\Ponyfm\User; + +class TrackPolicy +{ + public function edit(User $user, Track $track) { + return $user->id === $track->user_id || $user->hasRole('admin'); + } + + public function delete(User $user, Track $track) { + return $user->id === $track->user_id || $user->hasRole('admin'); + } +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index dfaf53db..4387f2ee 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -20,8 +20,10 @@ namespace Poniverse\Ponyfm\Providers; +use Illuminate\Foundation\Application; use Illuminate\Support\ServiceProvider; use PfmValidator; +use Poniverse; use Validator; class AppServiceProvider extends ServiceProvider @@ -46,6 +48,8 @@ class AppServiceProvider extends ServiceProvider */ public function register() { - // + $this->app->bind(Poniverse::class, function(Application $app) { + return new Poniverse($app['config']->get('poniverse.client_id'), $app['config']->get('poniverse.secret')); + }); } } diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 7ee54f58..5367a7ad 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -24,6 +24,8 @@ use Illuminate\Contracts\Auth\Access\Gate as GateContract; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; use Poniverse\Ponyfm\Genre; use Poniverse\Ponyfm\Policies\GenrePolicy; +use Poniverse\Ponyfm\Policies\TrackPolicy; +use Poniverse\Ponyfm\Track; use Poniverse\Ponyfm\User; class AuthServiceProvider extends ServiceProvider @@ -34,7 +36,8 @@ class AuthServiceProvider extends ServiceProvider * @var array */ protected $policies = [ - Genre::class => GenrePolicy::class + Genre::class => GenrePolicy::class, + Track::class => TrackPolicy::class, ]; /** diff --git a/app/Track.php b/app/Track.php index d5b005b6..bde4ce9b 100644 --- a/app/Track.php +++ b/app/Track.php @@ -39,7 +39,20 @@ class Track extends Model { use SoftDeletes; - protected $dates = ['deleted_at']; + protected $dates = ['deleted_at', 'published_at', 'released_at']; + protected $casts = [ + 'id' => 'integer', + 'user_id' => 'integer', + 'license_id' => 'integer', + 'genre_id' => 'integer', + 'track_type_id' => 'integer', + 'is_vocal' => 'boolean', + 'is_explicit' => 'boolean', + 'cover_id' => 'integer', + 'is_downloadable' => 'boolean', + 'is_latest' => 'boolean', + 'is_listed' => 'boolean', + ]; use SlugTrait { SlugTrait::setTitleAttribute as setTitleAttributeSlug; diff --git a/app/User.php b/app/User.php index 071d7cce..a1fe972d 100644 --- a/app/User.php +++ b/app/User.php @@ -38,7 +38,16 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon use Authenticatable, CanResetPassword, Authorizable, RevisionableTrait; protected $table = 'users'; - protected $hidden1 = ['password_hash', 'password_salt', 'bio']; + protected $casts = [ + 'id' => 'integer', + 'sync_names' => 'boolean', + 'uses_gravatar' => 'boolean', + 'can_see_explicit_content' => 'boolean', + 'track_count' => 'integer', + 'comment_count' => 'integer', + 'avatar_id' => 'integer', + 'is_archived' => 'boolean', + ]; public function scopeUserDetails($query) { @@ -53,6 +62,33 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon return !$query; } + /** + * @param string $username + * @param string $displayName + * @param string $email + * @return User + */ + public static function findOrCreate(string $username, string $displayName, string $email) { + $user = static::where('username', $username) + ->where('is_archived', false) + ->first(); + + if (null !== $user) { + return $user; + + } else { + $user = new User; + + $user->username = $username; + $user->display_name = $displayName; + $user->email = $email; + $user->uses_gravatar = true; + $user->save(); + + return $user; + } + } + public function avatar() { return $this->belongsTo('Poniverse\Ponyfm\Image');