diff --git a/app/controllers/Api/Web/ImagesController.php b/app/controllers/Api/Web/ImagesController.php index acdd5554..019bbee5 100644 --- a/app/controllers/Api/Web/ImagesController.php +++ b/app/controllers/Api/Web/ImagesController.php @@ -20,6 +20,7 @@ $images[] = [ 'id' => $image->id, 'url' => $image->getUrl(Image::SMALL), + 'url_normal' => $image->getUrl(Image::NORMAL), 'filename' => $image->filename ]; } diff --git a/app/controllers/Api/Web/TracksController.php b/app/controllers/Api/Web/TracksController.php index 05525eae..ea0afe0e 100644 --- a/app/controllers/Api/Web/TracksController.php +++ b/app/controllers/Api/Web/TracksController.php @@ -67,13 +67,18 @@ } public function getEdit($id) { - $track = Track::find($id); + $track = Track::with('showSongs')->find($id); if (!$track) return $this->notFound('Track ' . $id . ' not found!'); if ($track->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, @@ -93,7 +98,8 @@ 'lyrics' => $track->lyrics, 'released_at' => $track->released_at, 'cover_url' => $track->hasCover() ? $track->getCoverUrl(Image::NORMAL) : null, - 'real_cover_url' => $track->getCoverUrl(Image::NORMAL) + 'real_cover_url' => $track->getCoverUrl(Image::NORMAL), + 'show_songs' => $showSongs ], 200); } diff --git a/app/database/migrations/2013_07_28_034328_create_songs_table.php b/app/database/migrations/2013_07_28_034328_create_songs_table.php index 6aaf1af9..b34e718c 100644 --- a/app/database/migrations/2013_07_28_034328_create_songs_table.php +++ b/app/database/migrations/2013_07_28_034328_create_songs_table.php @@ -11,7 +11,7 @@ class CreateSongsTable extends Migration { $table->string('slug', 200)->indexed(); }); - Schema::create('track_show_songs', function($table) { + Schema::create('show_song_track', function($table) { $table->increments('id'); $table->integer('track_id')->unsigned(); $table->integer('show_song_id')->unsigned(); @@ -2039,12 +2039,12 @@ Twilight Sparkle: Yes! Everything’s going to be just fine!", } public function down() { - Schema::table('track_show_songs', function($table){ - $table->drop_foreign('track_show_songs_track_id_foreign'); - $table->drop_foreign('track_show_songs_show_song_id_foreign'); + Schema::table('show_song_track', function($table){ + $table->drop_foreign('show_song_track_track_id_foreign'); + $table->drop_foreign('show_song_track_show_song_id_foreign'); }); - Schema::drop('track_show_songs'); + Schema::drop('show_song_track'); Schema::drop('show_songs'); } } \ No newline at end of file diff --git a/app/models/Commands/EditTrackCommand.php b/app/models/Commands/EditTrackCommand.php index 72b252ee..e6d8eb8a 100644 --- a/app/models/Commands/EditTrackCommand.php +++ b/app/models/Commands/EditTrackCommand.php @@ -34,17 +34,22 @@ public function execute() { $isVocal = isset($this->_input['is_vocal']) && $this->_input['is_vocal'] == 'true' ? true : false; - $validator = \Validator::make($this->_input, [ - 'title' => 'required|min:3|max:80', - 'released_at' => 'before:today' . ($this->_input['released_at'] != "" ? '|date' : ''), - 'lyrics' => $isVocal ? 'required' : '', - 'license_id' => 'required|exists:licenses,id', - 'genre_id' => 'required|exists:genres,id', - 'cover' => 'image|mimes:png|min_width:350|min_height:350', - 'track_type_id' => 'required|exists:track_types,id', - 'songs' => 'required_when:track_type,2|exists:songs,id', - 'cover_id' => 'exists:images,id' - ]); + $rules = [ + 'title' => 'required|min:3|max:80', + 'released_at' => 'before:today' . ($this->_input['released_at'] != "" ? '|date' : ''), + 'lyrics' => $isVocal ? 'required' : '', + 'license_id' => 'required|exists:licenses,id', + 'genre_id' => 'required|exists:genres,id', + 'cover' => 'image|mimes:png|min_width:350|min_height:350', + 'track_type_id' => 'required|exists:track_types,id', + 'songs' => 'required_when:track_type,2|exists:songs,id', + 'cover_id' => 'exists:images,id', + ]; + + if ($this->_input['track_type_id'] == 2) + $rules['show_song_ids'] = 'required|exists:show_songs,id'; + + $validator = \Validator::make($this->_input, $rules); if ($validator->fails()) return CommandResponse::fail($validator); @@ -61,6 +66,11 @@ $track->is_downloadable = $this->_input['is_downloadable'] == 'true'; $track->is_vocal = $isVocal; + if ($track->track_type_id == 2) { + $track->showSongs()->sync(explode(',', $this->_input['show_song_ids'])); + } else + $track->showSongs()->sync([]); + if ($track->published_at == null) { $track->published_at = new \DateTime(); } diff --git a/app/models/Entities/Track.php b/app/models/Entities/Track.php index e0afafc0..a044ba54 100644 --- a/app/models/Entities/Track.php +++ b/app/models/Entities/Track.php @@ -37,6 +37,10 @@ return $this->belongsTo('Entities\Image'); } + public function showSongs() { + return $this->belongsToMany('Entities\ShowSong'); + } + public function isPublished() { return $this->published_at != null && $this->deleted_at == null; } diff --git a/public/scripts/app/controllers/account-content-tracks.coffee b/public/scripts/app/controllers/account-content-tracks.coffee index b246cd7a..e1c7f9c9 100644 --- a/public/scripts/app/controllers/account-content-tracks.coffee +++ b/public/scripts/app/controllers/account-content-tracks.coffee @@ -9,24 +9,43 @@ angular.module('ponyfm').controller "account-content-tracks", [ $scope.selectedTrack = null $scope.isDirty = false $scope.isSaving = false - $scope.taxonomies = - trackTypes: taxonomies.trackTypes - licenses: taxonomies.licenses - genres: taxonomies.genres + $scope.taxonomies = taxonomies + $scope.selectedSongsTitle = 'None' + $scope.selectedSongs = {} + + updateSongDisplay = () -> + if _.size $scope.selectedSongs + $scope.selectedSongsTitle = (_.map _.values($scope.selectedSongs), (s) -> s.title).join(', ') + else + $scope.selectedSongsTitle = 'None' + + $scope.toggleSong = (song) -> + $scope.isDirty = true + if $scope.selectedSongs[song.id] + delete $scope.selectedSongs[song.id] + else + $scope.selectedSongs[song.id] = song + + updateSongDisplay() $scope.updateIsVocal = () -> delete $scope.errors.lyrics if !$scope.edit.is_vocal $scope.previewCover = () -> - return if !$scope.edit.cover - if typeof($scope.edit.cover) == 'object' - lightbox.openDataUrl $('#coverPreview').attr 'src' + return if !$scope.edit.cover && !$scope.edit.cover_id + + if $scope.edit.cover_id + lightbox.openImageUrl $scope.cover_url else - lightbox.openImageUrl $scope.edit.cover + 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 @@ -58,6 +77,9 @@ angular.module('ponyfm').controller "account-content-tracks", [ else formData.append name, value + if $scope.edit.track_type_id == 2 + formData.append 'show_song_ids', _.map(_.values($scope.selectedSongs), (s) -> s.id).join() + xhr.open 'POST', '/api/web/tracks/edit/' + $scope.edit.id, true xhr.setRequestHeader 'X-Token', pfm.token $scope.isSaving = true @@ -196,6 +218,10 @@ angular.module('ponyfm').controller "account-content-tracks", [ $('#coverPreview').attr 'src', track.cover_url $scope.isCoverLoaded = true + $scope.selectedSongs = {} + $scope.selectedSongs[song.id] = song for song in track.show_songs + updateSongDisplay() + $scope.touchModel = -> $scope.isDirty = true $.getJSON('/api/web/tracks/owned?order=created_at,desc').done (tracks) -> $scope.$apply -> diff --git a/public/scripts/app/directives/popup.coffee b/public/scripts/app/directives/popup.coffee index a0653d56..0723d387 100644 --- a/public/scripts/app/directives/popup.coffee +++ b/public/scripts/app/directives/popup.coffee @@ -2,17 +2,51 @@ angular.module('ponyfm').directive 'pfmPopup', () -> (scope, element, attrs) -> $popup = $ '#' + attrs.pfmPopup $element = $ element - $popup.remove() + $positionParent = null open = false + $popup.parents().each () -> + $this = $ this + $positionParent = $this if $positionParent == null && ($this.css('position') == 'relative' || $this.is 'body') + documentClickHandler = () -> return if !open $popup.removeClass 'open' open = false - $(document.body).bind 'click', documentClickHandler + calculatePosition = -> + position = $element.offset() + parentPosition = $positionParent.offset() - $(document.body).append $popup + left = position.left + right = left + $popup.width() + windowWidth = $(window).width() - 15 + if right > windowWidth + left -= right - windowWidth + + height = 'auto' + top = position.top + $element.height() + 10 + bottom = top + $popup.height() + windowHeight = $(window).height() + if bottom > windowHeight + height = windowHeight - top; + + return { + left: left - parentPosition.left - 2 + top: top - parentPosition.top, + height: height} + + windowResizeHandler = () -> + return if !open + $popup.css 'height', 'auto' + position = calculatePosition() + $popup.css + left: position.left + top: position.top + height: position.height + + $(document.body).bind 'click', documentClickHandler + $(window).bind 'resize', windowResizeHandler $(element).click (e) -> e.preventDefault() @@ -25,19 +59,15 @@ angular.module('ponyfm').directive 'pfmPopup', () -> $popup.addClass 'open' - position = $element.offset() - left = position.left - right = left + $popup.width() - windowWidth = $(window).width() - 15 - if right > windowWidth - left -= right - windowWidth - + $popup.css 'height', 'auto' + position = calculatePosition() $popup.css - top: position.top + $element.height() + 10 - left: left + left: position.left + top: position.top + height: position.height open = true scope.$on '$destroy', () -> $(document.body).unbind 'click', documentClickHandler - $popup.remove() \ No newline at end of file + $(window).unbind 'click', windowResizeHandler diff --git a/public/styles/account-tracks.less b/public/styles/account-tracks.less index 0ac6f3b9..11a13047 100644 --- a/public/styles/account-tracks.less +++ b/public/styles/account-tracks.less @@ -125,6 +125,62 @@ .editor { display: none; + .show-songs { + .btn { + .border-radius(0px); + display: block; + float: none; + padding: 3px 10px; + font-size: 8pt; + text-align: left; + } + + .error { + margin-top: 10px; + } + + .pfm-popup { + width: 300px; + + ul { + margin: 0px; + padding: 0px; + list-style: none; + + li { + margin: 0px; + padding: 0px; + + a { + .ellipsis(); + display: block; + padding: 3px 10px; + font-size: 8pt; + color: #333333; + + &:hover { + #gradient>.vertical(@dropdownLinkBackgroundHover, darken(@dropdownLinkBackgroundHover, 5%)); + text-decoration: none; + color: @dropdownLinkColorHover; + } + } + + &.selected { + a { + #gradient>.vertical(@green, darken(@green, 5%)); + color: #fff; + font-weight: bold; + + &:hover { + #gradient>.vertical(fadeout(@green, 20%), fadeout(darken(@green, 5%), 20%)); + } + } + } + } + } + } + } + .form-row { margin-bottom: 10px; diff --git a/public/styles/components.less b/public/styles/components.less index 28a40340..5c3d7690 100644 --- a/public/styles/components.less +++ b/public/styles/components.less @@ -84,10 +84,12 @@ html body { .pfm-popup { .box-shadow(0 5px 10px rgba(0, 0, 0, 0.2)); + z-index: 1000; position: absolute; display: none; border: 1px solid rgba(0, 0, 0, 0.2); background: #fff; + overflow-y: auto; &.open { display: block; diff --git a/public/templates/account/content/tracks.html b/public/templates/account/content/tracks.html index f478595f..bf3e87a5 100644 --- a/public/templates/account/content/tracks.html +++ b/public/templates/account/content/tracks.html @@ -105,8 +105,18 @@