From ab3f15b5bf68a39c4007fbbf5a8605b433307343 Mon Sep 17 00:00:00 2001 From: nelsonlaquet Date: Sun, 28 Jul 2013 13:50:02 -0500 Subject: [PATCH] Account Settings --- app/controllers/Api/Web/AccountController.php | 38 +++++++++ .../Commands/SaveAccountSettingsCommand.php | 79 +++++++++++++++++++ app/models/Entities/Image.php | 6 +- app/models/Entities/User.php | 18 ++--- app/routes.php | 4 + .../app/controllers/account-settings.coffee | 58 ++++++++++++++ public/styles/account-content.less | 4 + public/styles/layout.less | 7 +- public/templates/account/settings.html | 39 +++++++++ 9 files changed, 236 insertions(+), 17 deletions(-) create mode 100644 app/controllers/Api/Web/AccountController.php create mode 100644 app/models/Commands/SaveAccountSettingsCommand.php diff --git a/app/controllers/Api/Web/AccountController.php b/app/controllers/Api/Web/AccountController.php new file mode 100644 index 00000000..a933d856 --- /dev/null +++ b/app/controllers/Api/Web/AccountController.php @@ -0,0 +1,38 @@ + $user->bio, + 'can_see_explicit_content' => $user->can_see_explicit_content == 1, + 'display_name' => $user->display_name, + 'sync_names' => $user->sync_names == 1, + 'mlpforums_name' => $user->mlpforums_name, + 'gravatar' => $user->gravatar ? $user->gravatar : $user->email, + 'avatar_url' => !$user->uses_gravatar ? $user->getAvatarUrl() : null, + 'uses_gravatar' => $user->uses_gravatar == 1 + ], 200); + } + + public function postSave() { + return $this->execute(new SaveAccountSettingsCommand(Input::all())); + } + } \ No newline at end of file diff --git a/app/models/Commands/SaveAccountSettingsCommand.php b/app/models/Commands/SaveAccountSettingsCommand.php new file mode 100644 index 00000000..14089ed4 --- /dev/null +++ b/app/models/Commands/SaveAccountSettingsCommand.php @@ -0,0 +1,79 @@ +_input = $input; + } + + /** + * @return bool + */ + public function authorize() { + return Auth::user() != null; + } + + /** + * @throws \Exception + * @return CommandResponse + */ + public function execute() { + $user = Auth::user(); + + $rules = [ + 'display_name' => 'required|min:3|max:26', + 'bio' => 'textarea_length:250' + ]; + + if ($this->_input['sync_names'] == 'true') + $this->_input['display_name'] = $user->mlpforums_name; + + if ($this->_input['uses_gravatar'] == 'true') { + $rules['gravatar'] = 'email'; + } else { + $rules['avatar'] = 'image|mimes:png|min_width:350|min_height:350'; + $rules['avatar_id'] = 'exists:images,id'; + } + + $validator = Validator::make($this->_input, $rules); + + if ($validator->fails()) + return CommandResponse::fail($validator); + + if ($this->_input['uses_gravatar'] != 'true') { + if ($user->avatar_id == null && !isset($this->_input['avatar']) && !isset($this->_input['avatar_id'])) { + $validator->messages()->add('avatar', 'You must upload or select an avatar if you are not using gravatar!'); + return CommandResponse::fail($validator); + } + } + + $user->bio = $this->_input['bio']; + $user->display_name = $this->_input['display_name']; + $user->sync_names = $this->_input['sync_names'] == 'true'; + $user->can_see_explicit_content = $this->_input['can_see_explicit_content'] == 'true'; + $user->uses_gravatar = $this->_input['uses_gravatar'] == 'true'; + + if ($user->uses_gravatar) { + $user->avatar_id = null; + $user->gravatar = $this->_input['gravatar']; + } else { + if (isset($this->_input['avatar_id'])) + $user->avatar_id = $this->_input['avatar_id']; + else if (isset($this->_input['avatar'])) + $user->avatar_id = Image::upload($this->_input['avatar'], $user)->id; + } + + $user->save(); + + return CommandResponse::succeed(); + } + } \ No newline at end of file diff --git a/app/models/Entities/Image.php b/app/models/Entities/Image.php index f1b8c466..48576acf 100644 --- a/app/models/Entities/Image.php +++ b/app/models/Entities/Image.php @@ -67,16 +67,16 @@ protected $table = 'images'; - public function getUrl($type = Cover::NORMAL) { + public function getUrl($type = self::NORMAL) { $type = self::$ImageTypes[$type]; return URL::to('i' . $this->id . '/' . $type['name'] . '.png'); } - public function getFile($type = Cover::NORMAL) { + public function getFile($type = self::NORMAL) { return $this->getDirectory() . '/' . $this->getFilename($type); } - public function getFilename($type = Cover::NORMAL) { + public function getFilename($type = self::NORMAL) { $typeInfo = self::$ImageTypes[$type]; return $this->id . '_' . $typeInfo['name'] . '.png'; } diff --git a/app/models/Entities/User.php b/app/models/Entities/User.php index 462d705c..fc9166e8 100644 --- a/app/models/Entities/User.php +++ b/app/models/Entities/User.php @@ -6,16 +6,14 @@ use Gravatar; use Illuminate\Auth\UserInterface; use Illuminate\Auth\Reminders\RemindableInterface; + use Illuminate\Support\Facades\URL; + use Ratchet\Wamp\Exception; class User extends \Eloquent implements UserInterface, RemindableInterface { protected $table = 'users'; protected $hidden = ['password_hash', 'password_salt', 'bio']; public function avatar() { - return $this->hasOne('Entities\Image'); - } - - public function cover() { return $this->belongsTo('Entities\Image'); } @@ -31,9 +29,9 @@ return $this->email; } - public function getAvatarUrl($type = Cover::NORMAL) { + public function getAvatarUrl($type = Image::NORMAL) { if (!$this->uses_gravatar) - return $this->cover->getUrl(); + return $this->avatar->getUrl(); $email = $this->gravatar; if (!strlen($email)) @@ -42,11 +40,11 @@ return Gravatar::getUrl($email, Image::$ImageTypes[$type]['width']); } - public function getAvatarFile($type = Cover::NORMAL) { + public function getAvatarFile($type = Image::NORMAL) { if ($this->uses_gravatar) - return $this->user->getAvatar($type); + throw new Exception('Cannot get avatar file if this user is configured to use Gravatar!'); - $cover = Cover::$Covers[$type]; - return URL::to('t' . $this->id . '/cover_' . $cover['name'] . '.png?' . $this->cover_id); + $imageType = Image::$ImageTypes[$type]; + return URL::to('t' . $this->id . '/cover_' . $imageType['name'] . '.png?' . $this->cover_id); } } \ No newline at end of file diff --git a/app/routes.php b/app/routes.php index 78a745ae..8810836f 100644 --- a/app/routes.php +++ b/app/routes.php @@ -50,9 +50,13 @@ Route::post('/playlists/create', 'Api\Web\PlaylistsController@postCreate'); Route::post('/playlists/delete/{id}', 'Api\Web\PlaylistsController@postDelete'); Route::post('/playlists/edit/{id}', 'Api\Web\PlaylistsController@postEdit'); + + Route::post('/account/settings/save', 'Api\Web\AccountController@postSave'); }); Route::group(['before' => 'auth'], function() { + Route::get('/account/settings', 'Api\Web\AccountController@getSettings'); + Route::get('/images/owned', 'Api\Web\ImagesController@getOwned'); Route::get('/tracks/owned', 'Api\Web\TracksController@getOwned'); diff --git a/public/scripts/app/controllers/account-settings.coffee b/public/scripts/app/controllers/account-settings.coffee index 038681aa..871c0564 100644 --- a/public/scripts/app/controllers/account-settings.coffee +++ b/public/scripts/app/controllers/account-settings.coffee @@ -1,5 +1,63 @@ angular.module('ponyfm').controller "account-settings", [ '$scope', 'auth' ($scope, auth) -> + $scope.settings = {} + $scope.errors = {} + $scope.isDirty = false + $scope.touchModel = () -> + $scope.isDirty = true + + $scope.refresh = () -> + $.getJSON('/api/web/account/settings') + .done (res) -> $scope.$apply -> + $scope.settings = res + + $scope.setAvatar = (image, type) -> + delete $scope.settings.avatar_id + delete $scope.settings.avatar + + if type == 'file' + $scope.settings.avatar = image + else if type == 'gallery' + $scope.settings.avatar_id = image.id + + $scope.isDirty = true + + $scope.updateAccount = () -> + return if !$scope.isDirty + + xhr = new XMLHttpRequest() + xhr.onload = -> $scope.$apply -> + $scope.isSaving = false + response = $.parseJSON(xhr.responseText) + if xhr.status != 200 + $scope.errors = {} + _.each response.errors, (value, key) -> $scope.errors[key] = value.join ', ' + return + + $scope.isDirty = false + $scope.errors = {} + $scope.refresh() + + formData = new FormData() + + _.each $scope.settings, (value, name) -> + if name == 'avatar' + return if value == null + if typeof(value) == 'object' + formData.append name, value, value.name + else + formData.append name, value + + xhr.open 'POST', '/api/web/account/settings/save', true + xhr.setRequestHeader 'X-Token', pfm.token + $scope.isSaving = true + xhr.send formData + + $scope.refresh() + + $scope.$on '$stateChangeStart', (e) -> + return if $scope.selectedTrack == null || !$scope.isDirty + e.preventDefault() if !confirm('Are you sure you want to leave this page without saving your changes?') ] \ No newline at end of file diff --git a/public/styles/account-content.less b/public/styles/account-content.less index bebff9b9..66d3df0c 100644 --- a/public/styles/account-content.less +++ b/public/styles/account-content.less @@ -15,6 +15,10 @@ } } +.account-settings-form { + margin: 10px; +} + ul.playlists { overflow-y: auto; margin: 0px; diff --git a/public/styles/layout.less b/public/styles/layout.less index 90ee80a5..19b2cb1a 100644 --- a/public/styles/layout.less +++ b/public/styles/layout.less @@ -25,12 +25,12 @@ html body { } .site-content-animate-enter { - opacity: 0; .transition(all 400ms ease-out); + .transform(translate(0, -100%)); } .site-content-animate-enter.site-content-animate-enter-active { - opacity: 1; + .transform(translate(0, 0)); } .site-content-animate-leave { @@ -39,8 +39,7 @@ html body { } .site-content-animate-leave.site-content-animate-leave-active { - opacity: .5; - .transform(scale(.95)); + .transform(translate(0, 100%)); } header { diff --git a/public/templates/account/settings.html b/public/templates/account/settings.html index 2deaa6c8..09629ed4 100644 --- a/public/templates/account/settings.html +++ b/public/templates/account/settings.html @@ -1,3 +1,42 @@

Account Settings

+
\ No newline at end of file