diff --git a/.env.example b/.env.example index aa4bd857a..66d51d9b1 100644 --- a/.env.example +++ b/.env.example @@ -56,6 +56,7 @@ MIX_API_SEARCH="${API_SEARCH}" ACTIVITYPUB_INBOX=false ACTIVITYPUB_SHAREDINBOX=false +HORIZON_DARKMODE=true # Set these both "true" to enable federation. # You might need to also run: diff --git a/app/Avatar.php b/app/Avatar.php index 76ca442b9..1033935bb 100644 --- a/app/Avatar.php +++ b/app/Avatar.php @@ -15,6 +15,7 @@ class Avatar extends Model * @var array */ protected $dates = ['deleted_at']; + protected $fillable = ['profile_id']; public function profile() { diff --git a/app/Console/Commands/UserCreate.php b/app/Console/Commands/UserCreate.php new file mode 100644 index 000000000..0945aff1d --- /dev/null +++ b/app/Console/Commands/UserCreate.php @@ -0,0 +1,88 @@ +info('Creating a new user...'); + + $name = $this->ask('Name'); + + $username = $this->ask('Username'); + + if(User::whereUsername($username)->exists()) { + $this->error('Username already in use, please try again...'); + exit; + } + + $email = $this->ask('Email'); + + if(User::whereEmail($email)->exists()) { + $this->error('Email already in use, please try again...'); + exit; + } + + $password = $this->secret('Password'); + $confirm = $this->secret('Confirm Password'); + + if($password !== $confirm) { + $this->error('Password mismatch, please try again...'); + exit; + } + + $is_admin = $this->confirm('Make this user an admin?'); + $confirm_email = $this->confirm('Manually verify email address?'); + + if($this->confirm('Are you sure you want to create this user?') && + $username && + $name && + $email && + $password + ) { + $user = new User; + $user->username = $username; + $user->name = $name; + $user->email = $email; + $user->password = bcrypt($password); + $user->is_admin = $is_admin; + $user->email_verified_at = $confirm_email ? now() : null; + $user->save(); + + $this->info('Created new user!'); + } + } +} diff --git a/app/Console/Commands/UserDelete.php b/app/Console/Commands/UserDelete.php new file mode 100644 index 000000000..a11c7d750 --- /dev/null +++ b/app/Console/Commands/UserDelete.php @@ -0,0 +1,72 @@ +argument('id'); + $user = User::whereUsername($id)->orWhere('id', $id)->first(); + if(!$user) { + $this->error('Could not find any user with that username or id.'); + exit; + } + + if($user->is_admin == true) { + $this->error('Cannot delete an admin account from CLI.'); + exit; + } + + if(!$this->confirm('Are you sure you want to delete this account?')) { + exit; + } + + $confirmation = $this->ask('Enter the username to confirm deletion'); + + if($confirmation !== $user->username) { + $this->error('Username does not match, exiting...'); + exit; + } + + $profile = $user->profile; + $profile->status = $user->status = 'deleted'; + $profile->save(); + $user->save(); + + DeleteAccountPipeline::dispatchNow($user); + } +} diff --git a/app/Console/Commands/UserShow.php b/app/Console/Commands/UserShow.php new file mode 100644 index 000000000..d72bf8236 --- /dev/null +++ b/app/Console/Commands/UserShow.php @@ -0,0 +1,54 @@ +argument('id'); + $user = User::whereUsername($id)->orWhere('id', $id)->first(); + if(!$user) { + $this->error('Could not find any user with that username or id.'); + exit; + } + + $this->info('User ID: ' . $user->id); + $this->info('Username: ' . $user->username); + $this->info('Email: ' . $user->email); + $this->info('Joined: ' . $user->created_at->diffForHumans()); + $this->info('Status Count: ' . $user->statuses()->count()); + } +} diff --git a/app/Console/Commands/UserSuspend.php b/app/Console/Commands/UserSuspend.php new file mode 100644 index 000000000..17d44d2d5 --- /dev/null +++ b/app/Console/Commands/UserSuspend.php @@ -0,0 +1,56 @@ +argument('id'); + $user = User::whereUsername($id)->orWhere('id', $id)->first(); + if(!$user) { + $this->error('Could not find any user with that username or id.'); + exit; + } + $this->info('Found user, username: ' . $user->username); + if($this->confirm('Are you sure you want to suspend this user?')) { + $profile = $user->profile; + $user->status = $profile->status = 'suspended'; + $user->save(); + $profile->save(); + $this->info('User account has been suspended.'); + } + } +} diff --git a/app/Console/Commands/UserTable.php b/app/Console/Commands/UserTable.php new file mode 100644 index 000000000..fff4c86ec --- /dev/null +++ b/app/Console/Commands/UserTable.php @@ -0,0 +1,49 @@ +argument('limit'); + + $headers = ['ID', 'Username', 'Name', 'Registered']; + + $users = User::orderByDesc('id')->take($limit)->get(['id', 'username', 'name', 'created_at'])->toArray(); + + $this->table($headers, $users); + } +} diff --git a/app/Console/Commands/UserUnsuspend.php b/app/Console/Commands/UserUnsuspend.php new file mode 100644 index 000000000..afb6dee6a --- /dev/null +++ b/app/Console/Commands/UserUnsuspend.php @@ -0,0 +1,56 @@ +argument('id'); + $user = User::whereUsername($id)->orWhere('id', $id)->first(); + if(!$user) { + $this->error('Could not find any user with that username or id.'); + exit; + } + $this->info('Found user, username: ' . $user->username); + if($this->confirm('Are you sure you want to unsuspend this user?')) { + $profile = $user->profile; + $user->status = $profile->status = null; + $user->save(); + $profile->save(); + $this->info('User account has been unsuspended.'); + } + } +} diff --git a/app/Http/Controllers/Admin/AdminSettingsController.php b/app/Http/Controllers/Admin/AdminSettingsController.php index 97304f9fa..65d981627 100644 --- a/app/Http/Controllers/Admin/AdminSettingsController.php +++ b/app/Http/Controllers/Admin/AdminSettingsController.php @@ -19,8 +19,8 @@ trait AdminSettingsController public function settingsBackups(Request $request) { - $path = storage_path('app/PixelFed'); - $files = new \DirectoryIterator($path); + $path = storage_path('app/'.config('app.name')); + $files = is_dir($path) ? new \DirectoryIterator($path) : []; return view('admin.settings.backups', compact('files')); } @@ -106,7 +106,7 @@ trait AdminSettingsController $sys = [ 'pixelfed' => config('pixelfed.version'), 'php' => phpversion(), - 'redis' => explode(' ',exec('redis-cli -v'))[1], + 'laravel' => app()->version(), ]; switch (config('database.default')) { case 'pgsql': diff --git a/app/Http/Controllers/AvatarController.php b/app/Http/Controllers/AvatarController.php index f8a201b87..65b9a9731 100644 --- a/app/Http/Controllers/AvatarController.php +++ b/app/Http/Controllers/AvatarController.php @@ -30,11 +30,10 @@ class AvatarController extends Controller $dir = $path['root']; $name = $path['name']; $public = $path['storage']; - $currentAvatar = storage_path('app/'.$profile->avatar->media_path); $loc = $request->file('avatar')->storeAs($public, $name); - $avatar = Avatar::whereProfileId($profile->id)->firstOrFail(); - $opath = $avatar->media_path; + $avatar = Avatar::firstOrNew(['profile_id' => $profile->id]); + $currentAvatar = $avatar->recentlyCreated ? null : storage_path('app/'.$profile->avatar->media_path); $avatar->media_path = "$public/$name"; $avatar->thumb_path = null; $avatar->change_count = ++$avatar->change_count; diff --git a/app/Mail/EmailChange.php b/app/Mail/EmailChange.php new file mode 100644 index 000000000..0a3934fd8 --- /dev/null +++ b/app/Mail/EmailChange.php @@ -0,0 +1,33 @@ +markdown('emails.notification.email_change'); + } +} diff --git a/app/Mail/PasswordChange.php b/app/Mail/PasswordChange.php new file mode 100644 index 000000000..f90c10b51 --- /dev/null +++ b/app/Mail/PasswordChange.php @@ -0,0 +1,33 @@ +markdown('emails.notification.password_change'); + } +} diff --git a/app/Providers/HorizonServiceProvider.php b/app/Providers/HorizonServiceProvider.php new file mode 100644 index 000000000..3383835ee --- /dev/null +++ b/app/Providers/HorizonServiceProvider.php @@ -0,0 +1,50 @@ +is_admin == true; + }); + } + + /** + * Register any application services. + * + * @return void + */ + public function register() + { + if(config('horizon.darkmode') == true) { + Horizon::night(); + } + } +} diff --git a/composer.json b/composer.json index defbeac70..f7a171666 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ "greggilbert/recaptcha": "dev-master", "intervention/image": "^2.4", "laravel/framework": "5.8.*", - "laravel/horizon": "^1.2", + "laravel/horizon": "^3.0", "laravel/passport": "^7.0", "laravel/tinker": "^1.0", "league/flysystem-aws-s3-v3": "~1.0", @@ -42,7 +42,7 @@ "fzaninotto/faker": "^1.4", "mockery/mockery": "^1.0", "nunomaduro/collision": "^2.0", - "phpunit/phpunit": "^7.0" + "phpunit/phpunit": "^7.5" }, "autoload": { "classmap": [ @@ -69,11 +69,11 @@ "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" ], "post-create-project-cmd": [ - "@php artisan key:generate" + "@php artisan key:generate --ansi" ], "post-autoload-dump": [ "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", - "@php artisan package:discover" + "@php artisan package:discover --ansi" ] }, "config": { diff --git a/composer.lock b/composer.lock index ed7de6af7..6bddf7633 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "8f9feb6f0dd669b7a0974809de05d8bd", + "content-hash": "d7c9f518e63d20424dd17883057f5cb8", "packages": [ { "name": "alchemy/binary-driver", @@ -71,16 +71,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.87.19", + "version": "3.87.21", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "79366d3335649960f49694eb052cbdac6616f843" + "reference": "266641679eea15075ea13c088f9737460351f7ab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/79366d3335649960f49694eb052cbdac6616f843", - "reference": "79366d3335649960f49694eb052cbdac6616f843", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/266641679eea15075ea13c088f9737460351f7ab", + "reference": "266641679eea15075ea13c088f9737460351f7ab", "shasum": "" }, "require": { @@ -149,7 +149,7 @@ "s3", "sdk" ], - "time": "2019-02-26T19:08:43+00:00" + "time": "2019-02-28T20:02:04+00:00" }, { "name": "beyondcode/laravel-self-diagnosis", @@ -1741,40 +1741,42 @@ }, { "name": "laravel/horizon", - "version": "v1.4.3", + "version": "v3.0.0", "source": { "type": "git", "url": "https://github.com/laravel/horizon.git", - "reference": "b00da78d10158036cab2f45115ecc360f2014ed4" + "reference": "11acb6eafee4a0ea3bea87c6277f4342ebd2e1e3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/horizon/zipball/b00da78d10158036cab2f45115ecc360f2014ed4", - "reference": "b00da78d10158036cab2f45115ecc360f2014ed4", + "url": "https://api.github.com/repos/laravel/horizon/zipball/11acb6eafee4a0ea3bea87c6277f4342ebd2e1e3", + "reference": "11acb6eafee4a0ea3bea87c6277f4342ebd2e1e3", "shasum": "" }, "require": { "cakephp/chronos": "^1.0", + "ext-json": "*", "ext-pcntl": "*", "ext-posix": "*", - "illuminate/contracts": "~5.5", - "illuminate/queue": "~5.5", - "illuminate/support": "~5.5", + "illuminate/contracts": "~5.7.0|~5.8.0", + "illuminate/queue": "~5.7.0|~5.8.0", + "illuminate/support": "~5.7.0|~5.8.0", "php": ">=7.1.0", "predis/predis": "^1.1", "ramsey/uuid": "^3.5", - "symfony/debug": "~3.3|~4.0" + "symfony/debug": "^4.2", + "symfony/process": "^4.2" }, "require-dev": { - "mockery/mockery": "~1.0", - "orchestra/database": "~3.5", - "orchestra/testbench": "~3.5", - "phpunit/phpunit": "~6.0" + "mockery/mockery": "^1.0", + "orchestra/database": "^3.7", + "orchestra/testbench": "^3.7", + "phpunit/phpunit": "^7.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "3.0-dev" }, "laravel": { "providers": [ @@ -1805,7 +1807,7 @@ "laravel", "queue" ], - "time": "2018-11-01T14:03:51+00:00" + "time": "2019-02-27T15:44:24+00:00" }, { "name": "laravel/passport", diff --git a/config/horizon.php b/config/horizon.php index 1b150d9f4..585495bcd 100644 --- a/config/horizon.php +++ b/config/horizon.php @@ -91,4 +91,6 @@ return [ ], ], ], + + 'darkmode' => env('HORIZON_DARKMODE', false), ]; diff --git a/public/vendor/horizon/.gitignore b/public/vendor/horizon/.gitignore new file mode 100644 index 000000000..c96a04f00 --- /dev/null +++ b/public/vendor/horizon/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/public/vendor/horizon/css/app.css b/public/vendor/horizon/css/app.css deleted file mode 100644 index bc6de602e..000000000 Binary files a/public/vendor/horizon/css/app.css and /dev/null differ diff --git a/public/vendor/horizon/css/app.css.map b/public/vendor/horizon/css/app.css.map deleted file mode 100644 index 06d1a8850..000000000 --- a/public/vendor/horizon/css/app.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"/css/app.css","sources":[],"mappings":";;;;;A","sourceRoot":""} \ No newline at end of file diff --git a/public/vendor/horizon/img/favicon.png b/public/vendor/horizon/img/favicon.png deleted file mode 100644 index b5a88f414..000000000 Binary files a/public/vendor/horizon/img/favicon.png and /dev/null differ diff --git a/public/vendor/horizon/img/horizon.svg b/public/vendor/horizon/img/horizon.svg deleted file mode 100644 index 8cce28509..000000000 --- a/public/vendor/horizon/img/horizon.svg +++ /dev/null @@ -1,13 +0,0 @@ - - \ No newline at end of file diff --git a/public/vendor/horizon/img/sprite.svg b/public/vendor/horizon/img/sprite.svg deleted file mode 100644 index b90154aa4..000000000 --- a/public/vendor/horizon/img/sprite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/vendor/horizon/js/app.js b/public/vendor/horizon/js/app.js deleted file mode 100644 index 4badead60..000000000 Binary files a/public/vendor/horizon/js/app.js and /dev/null differ diff --git a/public/vendor/horizon/js/app.js.map b/public/vendor/horizon/js/app.js.map deleted file mode 100644 index 51243e48b..000000000 --- a/public/vendor/horizon/js/app.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"/js/app.js","sources":["webpack:////js/app.js"],"sourcesContent":["!function(t){function e(r){if(n[r])return n[r].exports;var i=n[r]={i:r,l:!1,exports:{}};return t[r].call(i.exports,i,i.exports,e),i.l=!0,i.exports}var n={};e.m=t,e.c=n,e.d=function(t,n,r){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:r})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,\"a\",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p=\"\",e(e.s=0)}({\"+ItH\":function(t,e,n){\"use strict\";t.exports=function(){var t=function(t,e){return this.construct(t,e),this};return t.defaults={global:{responsive:!0,responsiveAnimationDuration:0,maintainAspectRatio:!0,events:[\"mousemove\",\"mouseout\",\"click\",\"touchstart\",\"touchmove\"],hover:{onHover:null,mode:\"nearest\",intersect:!0,animationDuration:400},onClick:null,defaultColor:\"rgba(0,0,0,0.1)\",defaultFontColor:\"#666\",defaultFontFamily:\"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif\",defaultFontSize:12,defaultFontStyle:\"normal\",showLines:!0,elements:{},legendCallback:function(t){var e=[];e.push('