Account Settings

This commit is contained in:
nelsonlaquet 2013-07-28 13:50:02 -05:00
parent 2a8bd3c85c
commit ab3f15b5bf
9 changed files with 236 additions and 17 deletions

View file

@ -0,0 +1,38 @@
<?php
namespace Api\Web;
use Commands\CreateAlbumCommand;
use Commands\DeleteAlbumCommand;
use Commands\DeleteTrackCommand;
use Commands\EditAlbumCommand;
use Commands\EditTrackCommand;
use Commands\SaveAccountSettingsCommand;
use Cover;
use Entities\Album;
use Entities\Image;
use Entities\Track;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Response;
class AccountController extends \ApiControllerBase {
public function getSettings() {
$user = Auth::user();
return Response::json([
'bio' => $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()));
}
}

View file

@ -0,0 +1,79 @@
<?php
namespace Commands;
use Entities\Image;
use Entities\Track;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
class SaveAccountSettingsCommand extends CommandBase {
private $_input;
function __construct($input) {
$this->_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();
}
}

View file

@ -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';
}

View file

@ -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);
}
}

View file

@ -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');

View file

@ -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?')
]

View file

@ -15,6 +15,10 @@
}
}
.account-settings-form {
margin: 10px;
}
ul.playlists {
overflow-y: auto;
margin: 0px;

View file

@ -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 {

View file

@ -1,3 +1,42 @@
<div>
<h1>Account Settings</h1>
<form ng-submit="updateAccount()" class="pfm-form account-settings-form">
<ul class="toolbar">
<li>
<button type="submit" class="btn" ng-class="{disabled: !isDirty || isSaving, 'btn-primary': isDirty}">
Save Changes
<i ng-show="isSaving" class="icon-cog icon-spin icon-large"></i>
</button>
</li>
</ul>
<div class="stretch-to-bottom">
<div class="form-row" ng-class="{'has-error': errors.display_name != null}">
<label for="sync_names" class="strong"><input ng-disabled="isSaving" ng-change="touchModel();" id="sync_names" type="checkbox" ng-model="settings.sync_names" /> Sync my MLP Forums display name with Pony.fm</label>
<input type="text" ng-disabled="isSaving" ng-change="touchModel()" ng-show="!settings.sync_names" placeholder="Display Name" id="display_name" ng-model="settings.display_name" />
<div ng-show="settings.sync_names" class="alert alert-info">Your current MLP Forums display name is <strong>{{settings.mlpforums_name}}</strong></div>
<div class="error">{{errors.display_name}}</div>
</div>
<div class="form-row" ng-class="{'has-error': errors.bio != null}">
<label class="strong" for="bio">Bio</label>
<textarea id="bio" placeholder="bio (optional)" ng-model="settings.bio" ng-disabled="isLoading" ng-change="touchModel()"></textarea>
<div class="error">{{errors.description}}</div>
</div>
<div class="row-fluid">
<div class="form-row span6" ng-class="{'has-error': errors.avatar != null || errors.gravatar != null}">
<label for="uses_gravatar">
<input ng-change="touchModel()" ng-disabled="isLoading" id="uses_gravatar" type="checkbox" ng-model="settings.uses_gravatar" /> Use Gravatar
</label>
<div ng-show="!settings.uses_gravatar">
<pfm-image-upload set-image="setAvatar" image="settings.avatar_url" />
</div>
<input type="text" ng-disabled="isSaving" ng-change="touchModel()" ng-show="settings.uses_gravatar" placeholder="Gravatar Email" ng-model="settings.gravatar" />
<div class="error" ng-show="errors.avatar != null">{{errors.avatar}}</div>
<div class="error" ng-show="errors.gravatar != null">{{errors.gravatar}}</div>
</div>
<div class="form-row span6">
<label for="can_see_explicit_content"><input ng-change="touchModel()" ng-disabled="isLoading" id="can_see_explicit_content" type="checkbox" ng-model="settings.can_see_explicit_content" /> Can See Explicit Content</label>
</div>
</div>
</div>
</form>
</div>