Commit for things

This commit is contained in:
nelsonlaquet 2013-07-28 03:35:31 -05:00
parent f093d7a570
commit e2bb57922a
16 changed files with 390 additions and 206 deletions

View file

@ -2,9 +2,9 @@
namespace Api\Web;
use Commands\CreateAlbumCommand;
use Commands\DeleteTrackCommand;
use Commands\EditTrackCommand;
use Commands\UploadTrackCommand;
use Cover;
use Entities\Album;
use Entities\Image;
@ -15,44 +15,42 @@
class AlbumsController extends \ApiControllerBase {
public function getOwned() {
$query = Album::summary()->where('user_id', \Auth::user()->id);
return Response::json($query->get(), 200);
$query = Album::summary()->where('user_id', \Auth::user()->id)->get();
$albums = [];
foreach ($query as $album) {
$albums[] = [
'id' => $album->id,
'title' => $album->title,
'slug' => $album->slug,
'created_at' => $album->created_at,
'cover_url' => $album->getCoverUrl(Image::SMALL)
];
}
return Response::json($albums, 200);
}
public function postCreate() {
return $this->execute(new CreateAlbumCommand(Input::all()));
}
public function getEdit($id) {
$track = Track::with('showSongs')->find($id);
if (!$track)
return $this->notFound('Track ' . $id . ' not found!');
$album = Album::find($id);
if (!$album)
return $this->notFound('Album ' . $id . ' not found!');
if ($track->user_id != Auth::user()->id)
if ($album->user_id != Auth::user()->id)
return $this->notAuthorized();
$showSongs = [];
foreach ($track->showSongs as $showSong) {
$showSongs[] = ['id' => $showSong->id, 'title' => $showSong->title];
}
return Response::json([
'id' => $track->id,
'title' => $track->title,
'user_id' => $track->user_id,
'slug' => $track->slug,
'is_vocal' => (bool)$track->is_vocal,
'is_explicit' => (bool)$track->is_explicit,
'is_downloadable' => !$track->isPublished() ? true : (bool)$track->is_downloadable,
'is_published' => $track->published_at != null,
'created_at' => $track->created_at,
'published_at' => $track->published_at,
'duration' => $track->duration,
'genre_id' => $track->genre_id,
'track_type_id' => $track->track_type_id,
'license_id' => $track->license_id != null ? $track->license_id : 3,
'description' => $track->description,
'lyrics' => $track->lyrics,
'released_at' => $track->released_at,
'cover_url' => $track->hasCover() ? $track->getCoverUrl(Image::NORMAL) : null,
'real_cover_url' => $track->getCoverUrl(Image::NORMAL),
'show_songs' => $showSongs
'id' => $album->id,
'title' => $album->title,
'user_id' => $album->user_id,
'slug' => $album->slug,
'created_at' => $album->created_at,
'published_at' => $album->published_at,
'description' => $album->description,
'cover_url' => $album->hasCover() ? $album->getCoverUrl(Image::NORMAL) : null,
'real_cover_url' => $album->getCoverUrl(Image::NORMAL)
], 200);
}

View file

@ -19,8 +19,12 @@
foreach ($query->get() as $image) {
$images[] = [
'id' => $image->id,
'url' => $image->getUrl(Image::SMALL),
'url_normal' => $image->getUrl(Image::NORMAL),
'urls' => [
'small' => $image->getUrl(Image::SMALL),
'normal' => $image->getUrl(Image::NORMAL),
'thumbnail' => $image->getUrl(Image::THUMBNAIL),
'original' => $image->getUrl(Image::ORIGINAL)
],
'filename' => $image->filename
];
}

View file

@ -0,0 +1,62 @@
<?php
namespace Commands;
use Entities\Album;
use Entities\Image;
use Entities\Track;
use External;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
class CreateAlbumCommand extends CommandBase {
private $_input;
function __construct($input) {
$this->_input = $input;
}
/**
* @return bool
*/
public function authorize() {
$user = \Auth::user();
return $user != null;
}
/**
* @throws \Exception
* @return CommandResponse
*/
public function execute() {
$rules = [
'title' => 'required|min:3|max:50',
'description' => '',
'cover' => 'image|mimes:png|min_width:350|min_height:350',
'cover_id' => 'exists:images,id',
];
$validator = \Validator::make($this->_input, $rules);
if ($validator->fails())
return CommandResponse::fail($validator);
$album = new Album();
$album->user_id = Auth::user()->id;
$album->title = $this->_input['title'];
$album->description = $this->_input['description'];
if (isset($this->_input['cover_id'])) {
$album->cover_id = $this->_input['cover_id'];
}
else if (isset($this->_input['cover'])) {
$cover = $this->_input['cover'];
$album->cover_id = Image::upload($cover, Auth::user())->id;
} else if ($this->_input['remove_cover'] == 'true')
$album->cover_id = null;
$album->save();
return CommandResponse::succeed(['id' => $album->id]);
}
}

View file

@ -9,7 +9,7 @@
protected $softDelete = true;
public static function summary() {
return self::select('id', 'title', 'user_id', 'slug', 'created_at');
return self::select('id', 'title', 'user_id', 'slug', 'created_at', 'cover_id');
}
protected $table = 'albums';

View file

@ -36,6 +36,8 @@
Route::post('/tracks/upload', 'Api\Web\TracksController@postUpload');
Route::post('/tracks/delete/{id}', 'Api\Web\TracksController@postDelete');
Route::post('/tracks/edit/{id}', 'Api\Web\TracksController@putEdit');
Route::post('/albums/create', 'Api\Web\AlbumsController@postCreate');
});
Route::group(['before' => 'auth'], function() {

View file

@ -1,17 +1,87 @@
angular.module('ponyfm').controller "account-albums-edit", [
'$scope', '$state', 'taxonomies', '$dialog', 'lightbox'
($scope, $state, taxonomies, $dialog, lightbox) ->
$scope.isNew = $state.params.album_id == null
$scope.isNew = $state.params.album_id == undefined
$scope.data.isEditorOpen = true
$scope.errors = {}
$scope.isDirty = false
$scope.album = {}
$scope.isSaving = false
$scope.touchModel = -> $scope.isDirty = true
$scope.refresh = () ->
return if $scope.isNew
$.getJSON('/api/web/albums/edit/' + $scope.data.selectedAlbum.id)
.done (album) -> $scope.$apply ->
$scope.isDirty = false
$scope.errors = {}
$scope.album =
id: album.id
title: album.title
description: album.description
remove_cover: false
cover: album.cover_url
if $scope.isNew
$scope.album =
title: ''
description: ''
else
$scope.refresh();
$scope.$on '$destroy', -> $scope.data.isEditorOpen = false
$scope.saveAlbum = ->
url =
if $scope.isNew
'/api/web/albums/create'
else
'/api/web/albums/edit' + $scope.album.id
xhr = new XMLHttpRequest()
xhr.onload = -> $scope.$apply ->
$scope.isSaving = false
response = $.parseJSON(xhr.responseText).errors
if xhr.status != 200
$scope.errors = {}
_.each response.errors, (value, key) -> $scope.errors[key] = value.join ', '
return
$scope.$emit 'album-updated'
if $scope.isNew
$state.transitionTo 'account-content.albums.edit', {album_id: response.id}
else
$scope.refresh()
formData = new FormData()
_.each $scope.album, (value, name) ->
if name == 'cover'
return if value == null
if typeof(value) == 'object'
formData.append name, value, value.name
else
formData.append name, value
xhr.open 'POST', url, true
xhr.setRequestHeader 'X-Token', pfm.token
$scope.isSaving = true
xhr.send formData
$scope.deleteAlbum = ->
$scope.setCover = (image, type) ->
delete $scope.album.cover_id
delete $scope.album.cover
if image == null
$scope.album.remove_cover = true
else if type == 'file'
$scope.album.cover = image
else if type == 'gallery'
$scope.album.cover_id = image.id
$scope.isDirty = true
]

View file

@ -1,19 +1,28 @@
angular.module('ponyfm').controller "account-albums", [
'$scope', '$state', 'taxonomies', '$dialog', 'lightbox'
($scope, $state, taxonomies, $dialog, lightbox) ->
refreshList = () ->
$.getJSON('/api/web/albums/owned')
.done (albums) ->
$scope.albums = albums
albumsDb = {}
refreshList()
$scope.albums = []
$scope.data =
isEditorOpen: false
selectedAlbum: null
refreshList = () ->
$.getJSON('/api/web/albums/owned')
.done (albums) -> $scope.$apply ->
albumsDb[album.id] = album for album in albums
$scope.albums = albums
selectAlbum albumsDb[$state.params.album_id] if $state.params.album_id != undefined
selectAlbum = (album) -> $scope.data.selectedAlbum = album
$scope.$on '$stateChangeSuccess', () ->
if $state.params.album_id
selectAlbum albumsDb[$state.params.album_id]
else
selectAlbum null
refreshList()
]

View file

@ -1,11 +1,6 @@
angular.module('ponyfm').controller "account-tracks", [
'$scope', '$state', 'taxonomies', '$dialog', 'lightbox'
($scope, $state, taxonomies, $dialog, lightbox) ->
$('#coverPreview').load () ->
$scope.$apply -> $scope.isCoverLoaded = true
window.alignVertically(this)
$scope.isCoverLoaded = false
$scope.selectedTrack = null
$scope.isDirty = false
$scope.isSaving = false
@ -13,6 +8,19 @@ angular.module('ponyfm').controller "account-tracks", [
$scope.selectedSongsTitle = 'None'
$scope.selectedSongs = {}
$scope.setCover = (image, type) ->
delete $scope.edit.cover_id
delete $scope.edit.cover
if image == null
$scope.edit.remove_cover = true
else if type == 'file'
$scope.edit.cover = image
else if type == 'gallery'
$scope.edit.cover_id = image.id
$scope.isDirty = true
updateSongDisplay = () ->
if _.size $scope.selectedSongs
$scope.selectedSongsTitle = (_.map _.values($scope.selectedSongs), (s) -> s.title).join(', ')
@ -31,25 +39,6 @@ angular.module('ponyfm').controller "account-tracks", [
$scope.updateIsVocal = () ->
delete $scope.errors.lyrics if !$scope.edit.is_vocal
$scope.previewCover = () ->
return if !$scope.edit.cover && !$scope.edit.cover_id
if $scope.edit.cover_id
lightbox.openImageUrl $scope.cover_url
else
if typeof($scope.edit.cover) == 'object'
lightbox.openDataUrl $('#coverPreview').attr 'src'
else
lightbox.openImageUrl $scope.edit.cover
$scope.selectGalleryImage = (image) ->
$('#coverPreview').attr 'src', image.url
$scope.edit.cover_id = image.id
$scope.cover_url = image.url_normal
$scope.edit.remove_cover = false
$scope.edit.cover = null
$scope.isDirty = true
$scope.updateTrack = (track) ->
xhr = new XMLHttpRequest()
xhr.onload = -> $scope.$apply ->
@ -85,35 +74,6 @@ angular.module('ponyfm').controller "account-tracks", [
$scope.isSaving = true
xhr.send formData
$scope.uploadTrackCover = () ->
$("#coverImage").trigger 'click'
$scope.setCoverImage = (input) ->
$scope.$apply ->
delete $scope.edit.cover_id
previewElement = $('#coverPreview')[0]
file = input.files[0]
if file.type != 'image/png'
$scope.errors.cover = 'Cover image must be a png!'
$scope.isCoverLoaded = false
$scope.edit.cover = null
return
delete $scope.errors.cover
$scope.isDirty = true
reader = new FileReader()
reader.onload = (e) -> previewElement.src = e.target.result
reader.readAsDataURL file
$scope.edit.cover = file
$scope.clearTrackCover = () ->
$scope.isCoverLoaded = false
$scope.isDirty = true
$scope.edit.remove_cover = true
delete $scope.edit.cover_id
delete $scope.edit.cover
$scope.filters =
published: [
{title: 'Either', query: ''},
@ -185,7 +145,6 @@ angular.module('ponyfm').controller "account-tracks", [
selectTrack = (t) ->
$scope.selectedTrack = t
$scope.isCoverLoaded = false
return if !t
$.getJSON('/api/web/tracks/edit/' + t.id)
.done (track) -> $scope.$apply ->
@ -214,10 +173,6 @@ angular.module('ponyfm').controller "account-tracks", [
trackDbItem.is_published = track.is_published
trackDbItem.cover_url = track.real_cover_url
if track.cover_url
$('#coverPreview').attr 'src', track.cover_url
$scope.isCoverLoaded = true
$scope.selectedSongs = {}
$scope.selectedSongs[song.id] = song for song in track.show_songs
updateSongDisplay()

View file

@ -1,9 +1,86 @@
angular.module('ponyfm').directive 'pfmImageUpload',
angular.module('ponyfm').directive 'pfmImageUpload', () ->
$image = null
$uploader = null
restrict: 'E'
templateUrl: '/templates/directives/image-upload.html'
scope:
setUploadedImage: '&'
setGalleryImage: '&'
setImage: '=setImage'
image: '=image'
compile: (element) ->
$image = element.find 'img'
$uploader = element.find 'input'
controller: [
'upload'
(upload) -> (scope) ->
'images', '$scope', 'lightbox'
(images, $scope, lightbox) ->
$scope.imageObject = null
$scope.imageFile = null
$scope.imageUrl = null
$scope.isImageLoaded = false
$scope.error = null
$scope.$watch 'image', (val) ->
$scope.imageObject = $scope.imageFile = $scope.imageUrl = null
$scope.isImageLoaded = false
return if !val
$scope.imageUrl = val
$image.attr 'src', val
$scope.isImageLoaded = true
$image.load () -> $scope.$apply ->
$scope.isImageLoaded = true
window.setTimeout (() -> window.alignVertically($image)), 0
images.refresh().done (images) -> $scope.images = images
$scope.previewImage = () ->
return if !$scope.isImageLoaded
if $scope.imageObject
lightbox.openImageUrl $scope.imageObject.urls.normal
else if $scope.imageFile
lightbox.openDataUrl $image.attr 'src'
else if $scope.imageUrl
lightbox.openImageUrl $scope.imageUrl
$scope.uploadImage = () ->
$uploader.trigger 'click'
$scope.clearImage = () ->
$scope.imageObject = $scope.imageFile = $scope.imageUrl = null
$scope.isImageLoaded = false
$scope.setImage null
$scope.selectGalleryImage = (image) ->
$scope.imageObject = image
$scope.imageFile = null
$scope.imageUrl = image.urls.small
$image.attr 'src', image.urls.small
$scope.isImageLoaded = true
$scope.setImage image, 'gallery'
$scope.setImageFile = (input) ->
$scope.$apply ->
file = input.files[0]
$scope.imageObject = null
$scope.imageFile = file
if file.type != 'image/png'
$scope.error = 'Image must be a png!'
$scope.isImageLoaded = false
$scope.imageObject = $scope.imageFile = $scope.imageUrl = null
return
$scope.error = null
$scope.setImage file, 'file'
reader = new FileReader()
reader.onload = (e) -> $scope.$apply ->
$image[0].src = e.target.result
$scope.isImageLoaded = true
reader.readAsDataURL file
]

View file

@ -0,0 +1,25 @@
angular.module('ponyfm').factory('images', [
'$rootScope'
($rootScope) ->
def = null
self =
images: []
isLoading: true
refresh: () ->
return def if def
def = new $.Deferred()
self.images = []
self.isLoading = true
$.getJSON('/api/web/images/owned').done (images) -> $rootScope.$apply ->
self.images = images
self.isLoading = false
def.resolve images
return def
self.refresh()
return self
])

View file

@ -201,66 +201,15 @@
padding: 3px;
font-size: 8pt;
}
&.has-error {
label {
color: @red;
}
.error {
display: block;
}
}
}
.cover-upload {
overflow: hidden;
.has-error {
label {
color: @red;
}
.error {
clear: left;
margin-top: 14px;
}
input[type=file] {
display: none;
}
.btn {
.border-radius(0px);
}
.preview {
.img-polaroid();
overflow: hidden;
width: 46px;
height: 46px;
float: left;
img {
position: relative;
display: block;
width: 100%;
}
&.canOpen {
cursor: pointer;
&:hover {
border-color: @blue;
border-style: solid;
}
}
}
p, .btn-group {
color: #555;
margin-left: 60px;
display: block;
margin-bottom: 1px;
}
p {
font-size: 9pt;
}
}

View file

@ -94,4 +94,55 @@ html body {
&.open {
display: block;
}
}
.image-upload {
overflow: hidden;
.error {
clear: left;
margin-top: 14px;
}
input[type=file] {
display: none;
}
.btn {
.border-radius(0px);
}
.preview {
.img-polaroid();
overflow: hidden;
width: 46px;
height: 46px;
float: left;
img {
position: relative;
display: block;
width: 100%;
}
&.canOpen {
cursor: pointer;
&:hover {
border-color: @blue;
border-style: solid;
}
}
}
p, .btn-group {
color: #555;
margin-left: 60px;
display: block;
margin-bottom: 1px;
}
p {
font-size: 9pt;
}
}

View file

@ -1,4 +1,4 @@
<form novalidate ng-submit="updateAlbum()">
<form novalidate ng-submit="saveAlbum()">
<ul class="toolbar">
<li>
<button type="submit" class="btn" ng-class="{disabled: !isDirty || isSaving, 'btn-primary': isDirty}">
@ -6,7 +6,7 @@
<i ng-show="isSaving" class="icon-cog icon-spin icon-large"></i>
</button>
</li>
<li class="delete"><a ng-class="{disabled: isSaving}" class="btn btn-danger" href="#" ng-click="deleteAlbum(selectedAlbum)" pfm-eat-click>Delete Album</a></li>
<li class="delete" ng-show="!isNew"><a ng-class="{disabled: isSaving}" class="btn btn-danger" href="#" ng-click="deleteAlbum()" pfm-eat-click>Delete Album</a></li>
</ul>
<div class="strech-to-bottom">
<div class="form-row" ng-class="{'has-error': errors.title != null}">
@ -14,33 +14,14 @@
<input ng-disabled="isSaving" ng-change="touchModel()" placeholder="Album Title" type="text" id="title" ng-model="album.title" />
<div class="error">{{errors.title}}</div>
</div>
<div class="form-row" ng-class="{'has-error': errors.description != null}">
<div class="form-row">
<label for="description" class="strong">Description:</label>
<textarea ng-disabled="isSaving" ng-change="touchModel()" placeholder="Description (optional)" id="description" ng-model="album.description"></textarea>
<div class="error">{{errors.description}}</div>
</div>
<div class="form-row" ng-class="{'has-error': errors.cover != null}">
<label class="strong">Album Cover: </label>
<div class="cover-upload">
<div class="preview" ng-class="{canOpen: isCoverLoaded}" ng-click="previewCover()"><img id="coverPreview" ng-show="isCoverLoaded" /></div>
<p>
Image must be a PNG that is at least 350x350. <br />
<input type="file" id="coverImage" onchange="angular.element(this).scope().setCoverImage(this)" />
</p>
<div class="btn-group">
<a href="#" pfm-popup="image-selector" class="btn btn-small"><i class="icon-picture"></i> Gallery</a>
<a href="#" pfm-eat-click ng-click="uploadAlbumCover()" class="btn btn-info btn-small"><i class="icon-upload"></i> Upload</a>
<a href="#" pfm-eat-click ng-click="clearAlbumCover()" class="btn btn-danger btn-small" ng-show="album.cover || album.cover_id"><i class="icon-remove"></i></a>
</div>
<div id="image-selector" class="pfm-popup image-selector" ng-controller="account-image-select">
<ul>
<li ng-repeat="image in images" ng-click="selectGalleryImage(image)">
<img ng-src="{{image.url}}" />
</li>
</ul>
</div>
<div class="error">{{errors.cover}}</div>
</div>
<pfm-image-upload />
</div>
</div>
</form>

View file

@ -9,11 +9,11 @@
<div class="two-pane-view" ng-class="{open: data.isEditorOpen, closed: !data.isEditorOpen}">
<div class="list">
<ul class="account-albums-listing strech-to-bottom">
<li ng-repeat="album in albums" ng-class="{selected: album.id == data.selectedAlbum.id">
<li ng-repeat="album in albums" ng-class="{selected: album.id == data.selectedAlbum.id}">
<a href="/account/albums/edit/{{album.id}}">
<img class="image" ng-src="{{album.cover_url}}" />
<span class="title">{{albums.title}}</span>
<span class="published">{{albums.created_at | pfmdate:'MM/dd/yyyy'}}</span>
<span class="title">{{album.title}}</span>
<span class="published">{{album.created_at | pfmdate:'MM/dd/yyyy'}}</span>
</a>
</li>
<li ng-show="!albums.length" class="empty">

View file

@ -121,28 +121,9 @@
<div class="row-fluid">
<div class="form-row span6" ng-class="{'has-error': errors.cover != null}">
<label class="strong">Track Cover: </label>
<div class="cover-upload">
<div class="preview" ng-class="{canOpen: isCoverLoaded}" ng-click="previewCover()"><img id="coverPreview" ng-show="isCoverLoaded" /></div>
<p>
Track cover must be a PNG that is at least 350x350. <br />
<input type="file" id="coverImage" onchange="angular.element(this).scope().setCoverImage(this)" />
</p>
<div class="btn-group">
<a href="#" pfm-popup="image-selector" class="btn btn-small"><i class="icon-picture"></i> Gallery</a>
<a href="#" pfm-eat-click ng-click="uploadTrackCover()" class="btn btn-info btn-small"><i class="icon-upload"></i> Upload</a>
<a href="#" pfm-eat-click ng-click="clearTrackCover()" class="btn btn-danger btn-small" ng-show="edit.cover || edit.cover_id"><i class="icon-remove"></i></a>
</div>
<div id="image-selector" class="pfm-popup image-selector" ng-controller="account-image-select">
<ul>
<li ng-repeat="image in images" ng-click="selectGalleryImage(image)">
<img ng-src="{{image.url}}" />
</li>
</ul>
</div>
<div class="error">{{errors.cover}}</div>
</div>
<pfm-image-upload set-image="setCover" image="edit.cover" />
</div>
<div class="form-row span6" ng-class="{'has-error': errors.released_at != null}">
<div class="form-row span6">
<label for="released_at" class="strong">Release Date:</label>
<input ng-disabled="isSaving" type="text" id="released_at" ui-date ng-model="edit.released_at" ng-change="touchModel()" ui-date-format="yy-mm-dd" />
<div class="error">{{errors.released_at}}</div>

View file

@ -0,0 +1,20 @@
<div class="image-upload" ng-class="{'has-error': error != null}">
<div class="preview" ng-class="{canOpen: isImageLoaded}" ng-click="previewImage()"><img ng-show="isImageLoaded" /></div>
<p>
Image must be a PNG that is at least 350x350. <br />
<input type="file" onchange="angular.element(this).scope().setImageFile(this)" />
</p>
<div class="btn-group">
<a href="#" pfm-popup="image-selector" class="btn btn-small"><i class="icon-picture"></i> Gallery</a>
<a href="#" pfm-eat-click ng-click="uploadImage()" class="btn btn-info btn-small"><i class="icon-upload"></i> Upload</a>
<a href="#" pfm-eat-click ng-click="clearImage()" class="btn btn-danger btn-small" ng-show="isImageLoaded"><i class="icon-remove"></i></a>
</div>
<div id="image-selector" class="pfm-popup image-selector">
<ul>
<li ng-repeat="image in images" ng-click="selectGalleryImage(image)">
<img ng-src="{{image.urls.small}}" />
</li>
</ul>
</div>
<div class="error" ng-show="error != null">{{error}}</div>
</div>