├── .env.example ├── .github └── workflows │ └── main.yml ├── .gitignore ├── .php_cs ├── .phpstorm.meta.php ├── _ide_helper.php ├── _ide_helper_models.php ├── app ├── Console │ ├── Commands │ │ └── ComponentsScaffoldMakeCommand.php │ └── Kernel.php ├── Events │ └── ProfileImageUploaded.php ├── Exceptions │ ├── Handler.php │ └── MissingModel.php ├── Filters │ ├── Filter.php │ ├── RoleFilter.php │ └── UserFilter.php ├── Http │ ├── Controllers │ │ ├── Auth │ │ │ ├── ForgotPasswordController.php │ │ │ ├── LoginController.php │ │ │ ├── ResetPasswordController.php │ │ │ └── VerificationController.php │ │ ├── ConfirmedEmailController.php │ │ ├── Controller.php │ │ ├── HomeController.php │ │ └── Profile │ │ │ └── UserController.php │ ├── HasAcceptedInvitationAuth.php │ ├── Kernel.php │ ├── Livewire │ │ ├── AcceptedInvitationComponent.php │ │ ├── CanFlash.php │ │ ├── CreateRoleComponent.php │ │ ├── CreateUserComponent.php │ │ ├── DeleteRoleComponent.php │ │ ├── DeleteUserComponent.php │ │ ├── EditRoleComponent.php │ │ ├── EditUserComponent.php │ │ ├── HasLivewireAuth.php │ │ ├── HasTable.php │ │ ├── IndexRoleComponent.php │ │ ├── IndexUserComponent.php │ │ └── Profile │ │ │ ├── UpdateEmail.php │ │ │ ├── UpdateImage.php │ │ │ └── UpdatePassword.php │ └── Middleware │ │ ├── Authenticate.php │ │ ├── Authorization.php │ │ ├── CheckForMaintenanceMode.php │ │ ├── EncryptCookies.php │ │ ├── RedirectIfAuthenticated.php │ │ ├── TrimStrings.php │ │ ├── TrustProxies.php │ │ └── VerifyCsrfToken.php ├── Listeners │ └── ResizeImage.php ├── Mail │ ├── InvitationMail.php │ ├── NewEmailConfirmationMail.php │ └── PasswordChangedMail.php ├── Models │ ├── Permission.php │ ├── PermissionRole.php │ ├── Role.php │ └── User.php ├── Providers │ ├── AppServiceProvider.php │ ├── AuthServiceProvider.php │ ├── BroadcastServiceProvider.php │ ├── EventServiceProvider.php │ └── RouteServiceProvider.php ├── Rules │ ├── OwnerRestrictedRule.php │ ├── PasswordCheckRule.php │ ├── PasswordRule.php │ └── PermissionExistsRule.php ├── Scopes │ └── VisibleToScope.php ├── Services │ └── ForRouteGate.php ├── ViewModels │ └── SaveRoleViewModel.php └── helpers.php ├── artisan ├── bootstrap ├── app.php └── cache │ └── .gitignore ├── composer.json ├── composer.lock ├── config ├── app.php ├── auth.php ├── broadcasting.php ├── cache.php ├── database.php ├── filesystems.php ├── hashing.php ├── ide-helper.php ├── insights.php ├── livewire.php ├── logging.php ├── mail.php ├── queue.php ├── services.php ├── session.php └── view.php ├── database ├── .gitignore ├── factories │ ├── PermissionFactory.php │ ├── RoleFactory.php │ └── UserFactory.php ├── migrations │ ├── 2013_03_11_165941_create_roles_table.php │ ├── 2014_10_12_000000_create_users_table.php │ ├── 2014_10_12_100000_create_password_resets_table.php │ ├── 2020_02_03_125830_create_sessions_table.php │ ├── 2020_02_03_132010_create_jobs_table.php │ ├── 2020_03_26_110221_create_permissions_table.php │ └── 2023_03_26_110250_create_permission_role_table.php └── seeders │ ├── DatabaseSeeder.php │ ├── PermissionsTableSeeder.php │ ├── RolesTableSeeder.php │ └── UsersTableSeeder.php ├── package-lock.json ├── package.json ├── phpunit.xml ├── public ├── .htaccess ├── css │ └── app.css ├── favicon.ico ├── images │ └── default-user.png ├── index.php ├── js │ ├── app.js │ └── turbolinks.js ├── mix-manifest.json ├── robots.txt ├── web.config └── webfonts │ ├── fa-brands-400.eot │ ├── fa-brands-400.svg │ ├── fa-brands-400.ttf │ ├── fa-brands-400.woff │ ├── fa-brands-400.woff2 │ ├── fa-regular-400.eot │ ├── fa-regular-400.svg │ ├── fa-regular-400.ttf │ ├── fa-regular-400.woff │ ├── fa-regular-400.woff2 │ ├── fa-solid-900.eot │ ├── fa-solid-900.svg │ ├── fa-solid-900.ttf │ ├── fa-solid-900.woff │ └── fa-solid-900.woff2 ├── readme.md ├── resources ├── css │ ├── adminlte.css │ ├── custom.css │ ├── font-awesome.css │ ├── google-font.css │ └── icheck-bootstrap.css ├── images │ └── default-user.png ├── js │ └── app.js ├── lang │ └── en │ │ ├── auth.php │ │ ├── pagination.php │ │ ├── passwords.php │ │ └── validation.php ├── views │ ├── accepted-invitations │ │ └── create.blade.php │ ├── auth │ │ ├── login.blade.php │ │ ├── passwords │ │ │ ├── confirm.blade.php │ │ │ ├── email.blade.php │ │ │ └── reset.blade.php │ │ └── verify.blade.php │ ├── components │ │ ├── content-header.blade.php │ │ ├── inputs │ │ │ ├── button.blade.php │ │ │ ├── dropdown.blade.php │ │ │ ├── email.blade.php │ │ │ ├── error.blade.php │ │ │ ├── fa.blade.php │ │ │ ├── file.blade.php │ │ │ ├── password.blade.php │ │ │ └── text.blade.php │ │ ├── profile │ │ │ └── element.blade.php │ │ ├── savings │ │ │ └── content.blade.php │ │ └── tables │ │ │ ├── entries-data.blade.php │ │ │ ├── pagination.blade.php │ │ │ ├── per-page.blade.php │ │ │ ├── search.blade.php │ │ │ ├── sort-by.blade.php │ │ │ └── table.blade.php │ ├── errors │ │ ├── 401.blade.php │ │ ├── 403.blade.php │ │ ├── 404.blade.php │ │ ├── 419.blade.php │ │ ├── 429.blade.php │ │ ├── 500.blade.php │ │ ├── 503.blade.php │ │ └── app.blade.php │ ├── home.blade.php │ ├── layouts │ │ ├── _flash.blade.php │ │ ├── _head.blade.php │ │ ├── app.blade.php │ │ └── guest-app.blade.php │ ├── livewire │ │ └── profile │ │ │ ├── update-email.blade.php │ │ │ ├── update-image.blade.php │ │ │ └── update-password.blade.php │ ├── mails │ │ ├── invitation-mail.blade.php │ │ ├── new-email-confirmation-mail.blade.php │ │ └── password-changed-mail.blade.php │ ├── profile │ │ └── users │ │ │ └── index.blade.php │ ├── roles │ │ ├── create.blade.php │ │ ├── delete.blade.php │ │ ├── edit.blade.php │ │ └── index.blade.php │ └── users │ │ ├── create.blade.php │ │ ├── delete.blade.php │ │ ├── edit.blade.php │ │ └── index.blade.php └── webfonts │ ├── fa-brands-400.eot │ ├── fa-brands-400.svg │ ├── fa-brands-400.ttf │ ├── fa-brands-400.woff │ ├── fa-brands-400.woff2 │ ├── fa-regular-400.eot │ ├── fa-regular-400.svg │ ├── fa-regular-400.ttf │ ├── fa-regular-400.woff │ ├── fa-regular-400.woff2 │ ├── fa-solid-900.eot │ ├── fa-solid-900.svg │ ├── fa-solid-900.ttf │ ├── fa-solid-900.woff │ └── fa-solid-900.woff2 ├── routes ├── api.php ├── channels.php ├── console.php └── web.php ├── server.php ├── storage ├── app │ ├── .gitignore │ └── public │ │ └── .gitignore ├── debugbar │ └── .gitignore ├── framework │ ├── .gitignore │ ├── cache │ │ ├── .gitignore │ │ └── data │ │ │ └── .gitignore │ ├── sessions │ │ └── .gitignore │ ├── testing │ │ └── .gitignore │ └── views │ │ └── .gitignore └── logs │ └── .gitignore ├── stubs ├── controller.api.stub ├── controller.invokable.stub ├── controller.model.api.stub ├── controller.model.stub ├── controller.nested.api.stub ├── controller.nested.stub ├── controller.plain.stub ├── controller.stub ├── job.queued.stub ├── job.stub ├── laravellte │ ├── create.class.stub │ ├── create.test.stub │ ├── create.view.stub │ ├── delete.class.stub │ ├── delete.test.stub │ ├── delete.view.stub │ ├── edit.class.stub │ ├── edit.test.stub │ ├── edit.view.stub │ ├── index.class.stub │ ├── index.filter.stub │ ├── index.test.stub │ ├── index.view.stub │ ├── show.class.stub │ ├── show.test.stub │ └── show.view.stub ├── migration.create.stub ├── migration.stub ├── migration.update.stub ├── model.pivot.stub ├── model.stub ├── test.stub └── test.unit.stub ├── tests ├── CreatesApplication.php ├── Feature │ ├── Console │ │ └── Commands │ │ │ └── ComponentsScaffoldMakeCommandTest.php │ └── Http │ │ ├── Controllers │ │ ├── Auth │ │ │ ├── ForgotPasswordControllerTest.php │ │ │ ├── LoginControllerTest.php │ │ │ └── ResetPasswordControllerTest.php │ │ ├── ConfirmedEmailControllerTest.php │ │ ├── HomeControllerTest.php │ │ └── Profile │ │ │ └── UserControllerTest.php │ │ └── Livewire │ │ ├── AcceptedInvitationComponentTest.php │ │ ├── CreateRoleComponentTest.php │ │ ├── CreateUserComponentTest.php │ │ ├── DeleteRoleComponentTest.php │ │ ├── DeleteUserComponentTest.php │ │ ├── EditRoleComponentTest.php │ │ ├── EditUserComponentTest.php │ │ ├── IndexRoleComponentTest.php │ │ ├── IndexUserComponentTest.php │ │ ├── Profile │ │ ├── UpdateEmailTest.php │ │ ├── UpdateImageTest.php │ │ └── UpdatePasswordTest.php │ │ └── TableTest.php ├── TestCase.php ├── Unit │ ├── Filters │ │ ├── RoleFilterTest.php │ │ └── UserFilterTest.php │ ├── Http │ │ ├── AcceptedInvitationAuthTest.php │ │ ├── Livewire │ │ │ └── HasLivewireAuthTest.php │ │ └── Middleware │ │ │ ├── AuthenticateTest.php │ │ │ └── AuthorizationTest.php │ ├── Listeners │ │ └── ResizeImageTest.php │ ├── Mail │ │ ├── InvitationMailTest.php │ │ ├── NewEmailConfirmationMailTest.php │ │ └── PasswordChangedMailTest.php │ ├── Models │ │ ├── PermissionRoleTest.php │ │ ├── PermissionTest.php │ │ ├── RoleTest.php │ │ └── UserTest.php │ ├── Providers │ │ ├── AuthServiceProviderTest.php │ │ └── EventServiceProviderTest.php │ ├── Rules │ │ ├── OwnerRestrictedRuleTest.php │ │ ├── PasswordCheckRuleTest.php │ │ ├── PasswordRuleTest.php │ │ └── PermissionExistsRuleTest.php │ ├── Scopes │ │ └── VisibleToScopeTest.php │ ├── Services │ │ └── ForRouteGateTest.php │ └── ViewModels │ │ └── SaveRoleViewModelTest.php └── helpers.php └── webpack.mix.js /.env.example: -------------------------------------------------------------------------------- 1 | APP_NAME="laravellte" 2 | APP_ENV=local 3 | APP_KEY= 4 | APP_DEBUG=true 5 | APP_URL=http://laravellte.test 6 | 7 | BUGSNAG_API_KEY= 8 | 9 | LOG_CHANNEL=stack 10 | 11 | DB_CONNECTION=mysql 12 | DB_HOST=localhost 13 | DB_DATABASE=laravellte 14 | DB_USERNAME=homestead 15 | DB_PASSWORD=secret 16 | 17 | BROADCAST_DRIVER=log 18 | CACHE_DRIVER=file 19 | QUEUE_CONNECTION=redis 20 | SESSION_DRIVER=file 21 | SESSION_LIFETIME=120 22 | 23 | MAIL_DRIVER=smtp 24 | MAIL_HOST= 25 | MAIL_PORT= 26 | MAIL_USERNAME= 27 | MAIL_PASSWORD= 28 | MAIL_ENCRYPTION=tls 29 | 30 | MAIL_FROM_ADDRESS=no-replay@laravellte.com 31 | MAIL_FROM_NAME="laravellte" -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | name: Run 4 | jobs: 5 | tests: 6 | name: Run tests 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | php: ['7.4'] 11 | steps: 12 | - uses: actions/checkout@v1 13 | - name: Setup PHP 14 | uses: shivammathur/setup-php@v1 15 | with: 16 | php-version: ${{ matrix.php }} 17 | - name: Run composer install 18 | run: composer install -n --prefer-dist 19 | env: 20 | APP_ENV: testing 21 | - name: Prepare Laravel Application 22 | run: | 23 | cp .env.example .env 24 | php artisan key:generate 25 | - name: Cache composer dependencies 26 | uses: actions/cache@v1 27 | with: 28 | path: vendor 29 | key: composer-${{ hashFiles('composer.lock') }} 30 | - name: Run php cs fixer 31 | run: vendor/bin/php-cs-fixer fix --dry-run 32 | - name: Run insights 33 | run: php artisan insights --no-interaction --min-quality=90 --min-complexity=85 --min-architecture=90 --min-style=95 34 | - name: Run tests 35 | run: php artisan test --parallel 36 | env: 37 | APP_ENV: testing 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .php_cs.cache 2 | /node_modules 3 | /public/hot 4 | /public/storage 5 | /storage/*.key 6 | .env 7 | vendor/ 8 | .phpunit.result.cache 9 | npm-debug.log 10 | .idea -------------------------------------------------------------------------------- /.php_cs: -------------------------------------------------------------------------------- 1 | setFinder( 8 | PhpCsFixer\Finder::create() 9 | ->in(app_path()) 10 | ->in(config_path()) 11 | ->in(database_path()) 12 | ->notPath(database_path('migrations')) 13 | ->in(resource_path('lang')) 14 | ->in(base_path('routes')) 15 | ->in(base_path('tests')) 16 | ) 17 | ->setRules([ 18 | '@Laravel' => true, 19 | ]); 20 | -------------------------------------------------------------------------------- /app/Console/Kernel.php: -------------------------------------------------------------------------------- 1 | load(__DIR__.'/Commands'); 35 | 36 | require base_path('routes/console.php'); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/Events/ProfileImageUploaded.php: -------------------------------------------------------------------------------- 1 | user = $user; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/Exceptions/Handler.php: -------------------------------------------------------------------------------- 1 | $value) { 18 | if (! method_exists($this, $name)) { 19 | continue; 20 | } 21 | 22 | if (is_array($value)) { 23 | $this->$name($value[0], $value[1]); 24 | } else { 25 | $this->$name($value); 26 | } 27 | } 28 | 29 | return $this; 30 | } 31 | 32 | /** 33 | * Order results by field in specific order. 34 | * 35 | * @param string $field 36 | * @param string $direction 37 | * @return \Illuminate\Database\Eloquent\Builder 38 | */ 39 | public function orderByField($field, $direction) 40 | { 41 | $this->orderBy($field, $direction); 42 | 43 | return $this; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/Filters/RoleFilter.php: -------------------------------------------------------------------------------- 1 | when($term, function ($query, $term) { 16 | $query->where('name', 'LIKE', "%$term%"); 17 | }); 18 | 19 | return $this; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/Filters/UserFilter.php: -------------------------------------------------------------------------------- 1 | when($term, function ($query, $term) { 16 | $query->where('email', 'LIKE', "%$term%"); 17 | }); 18 | 19 | return $this; 20 | } 21 | 22 | /** 23 | * Filter by role. 24 | * 25 | * @param mixed $roleId 26 | * @return \Illuminate\Database\Eloquent\Builder 27 | */ 28 | public function roleId($roleId = null) 29 | { 30 | $this->when($roleId, function ($query, $roleId) { 31 | $query->where('role_id', $roleId); 32 | }); 33 | 34 | return $this; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/ForgotPasswordController.php: -------------------------------------------------------------------------------- 1 | middleware('guest'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/LoginController.php: -------------------------------------------------------------------------------- 1 | middleware('guest')->except('logout'); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/ResetPasswordController.php: -------------------------------------------------------------------------------- 1 | middleware('guest'); 40 | } 41 | 42 | /** 43 | * Get the password reset validation rules. 44 | * 45 | * @return array 46 | */ 47 | protected function rules() 48 | { 49 | return [ 50 | 'token' => 'required', 51 | 'email' => 'required|email', 52 | 'password' => new PasswordRule(request()->password_confirmation), 53 | ]; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/VerificationController.php: -------------------------------------------------------------------------------- 1 | middleware('auth'); 39 | $this->middleware('signed')->only('verify'); 40 | $this->middleware('throttle:6,1')->only('verify', 'resend'); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/Http/Controllers/ConfirmedEmailController.php: -------------------------------------------------------------------------------- 1 | logout(); 21 | 22 | abort_if(! $request->hasValidSignature(), Response::HTTP_FORBIDDEN); 23 | 24 | $user = User::find($request->user); 25 | 26 | abort_if($user === null, Response::HTTP_FORBIDDEN); 27 | 28 | $user->update([ 29 | 'email' => $request->new_email, 30 | ]); 31 | 32 | msg_success('Your email has been successfully update.'); 33 | 34 | return redirect()->route('login'); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/Http/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | auth()->user()]); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/Http/HasAcceptedInvitationAuth.php: -------------------------------------------------------------------------------- 1 | hasValidSignature()) { 22 | abort(Response::HTTP_FORBIDDEN, 'The link does not have a valid signature or it is expired.'); 23 | } 24 | 25 | if ($user === null) { 26 | abort(Response::HTTP_FORBIDDEN, 'User was not found.'); 27 | } 28 | 29 | if ($user->password !== null) { 30 | return abort(Response::HTTP_FORBIDDEN, 'The link has already been used.'); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/Http/Livewire/AcceptedInvitationComponent.php: -------------------------------------------------------------------------------- 1 | user = User::find($request->user); 35 | $this->authorizeInvitation($request, $this->user); 36 | } 37 | 38 | /** 39 | * Render the component view. 40 | * 41 | * @return \Illuminate\View\View 42 | */ 43 | public function render() 44 | { 45 | return view('accepted-invitations.create') 46 | ->extends('layouts.guest-app'); 47 | } 48 | 49 | /** 50 | * Submit the form. 51 | * 52 | * @return void 53 | */ 54 | public function submit() 55 | { 56 | $this->validate(['newPassword' => [new PasswordRule($this->newPasswordConfirmation)]]); 57 | $this->user->savePassword($this->newPassword); 58 | 59 | auth()->login($this->user); 60 | 61 | return redirect()->to('/home'); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/Http/Livewire/CanFlash.php: -------------------------------------------------------------------------------- 1 | dispatchBrowserEvent('flash', [ 16 | 'level' => 'alert-danger', 17 | 'message' => $message, 18 | ]); 19 | } 20 | 21 | /** 22 | * Dispatch browser success flash event. 23 | * 24 | * @param string $message 25 | * @return void 26 | */ 27 | protected function dispatchFlashSuccessEvent($message) 28 | { 29 | return $this->dispatchBrowserEvent('flash', [ 30 | 'level' => 'alert-success', 31 | 'message' => $message, 32 | ]); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/Http/Livewire/CreateUserComponent.php: -------------------------------------------------------------------------------- 1 | roles = Role::orderBy('name')->get(); 32 | 33 | return view('users.create') 34 | ->extends('layouts.app'); 35 | } 36 | 37 | /** 38 | * Store new user. 39 | * 40 | * @return \Illuminate\Http\Response 41 | */ 42 | public function store() 43 | { 44 | $this->validate(); 45 | 46 | $user = User::create([ 47 | 'email' => $this->user['email'], 48 | 'role_id' => $this->user['role_id'], 49 | AppServiceProvider::OWNER_FIELD => auth()->id(), 50 | ]); 51 | 52 | msg_success('User has been successfully created.'); 53 | 54 | Mail::to($user) 55 | ->queue(new InvitationMail($user, Carbon::tomorrow())); 56 | 57 | return redirect()->route('users.index'); 58 | } 59 | 60 | /** 61 | * Validation rules. 62 | * 63 | * @return array 64 | */ 65 | protected function rules() 66 | { 67 | return [ 68 | 'user.email' => [ 69 | 'required', 70 | 'email', 71 | Rule::unique('users', 'email'), 72 | ], 73 | 'user.role_id' => [ 74 | 'required', 75 | Rule::exists('roles', 'id'), 76 | ], 77 | ]; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /app/Http/Livewire/DeleteRoleComponent.php: -------------------------------------------------------------------------------- 1 | role->isAdmin()) { 28 | $this->dispatchFlashDangerEvent('Admin role cannot be deleted.'); 29 | 30 | return; 31 | } 32 | 33 | User::where('role_id', $this->role)->delete(); 34 | 35 | $this->role->delete(); 36 | 37 | $this->emit('entity-deleted'); 38 | 39 | $this->dispatchFlashSuccessEvent('Role has been successfully deleted.'); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/Http/Livewire/DeleteUserComponent.php: -------------------------------------------------------------------------------- 1 | user()->isHimself($this->user)) { 28 | throw new AuthorizationException(); 29 | } 30 | 31 | $this->dispatchFlashSuccessEvent('User has been successfully deleted.'); 32 | 33 | $this->user->delete(); 34 | 35 | $this->emit('entity-deleted'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/Http/Livewire/EditUserComponent.php: -------------------------------------------------------------------------------- 1 | user->isHimself(auth()->user())) { 29 | throw new AuthorizationException(); 30 | } 31 | } 32 | 33 | /** 34 | * Render the component view. 35 | * 36 | * @return \Illuminate\View\View 37 | */ 38 | public function render() 39 | { 40 | $this->roles = Role::orderBy('name')->get(); 41 | 42 | return view('users.edit') 43 | ->extends('layouts.app'); 44 | } 45 | 46 | /** 47 | * Update existing user. 48 | * 49 | * @return \Illuminate\Http\Response 50 | */ 51 | public function update() 52 | { 53 | $this->validate($this->rules()); 54 | 55 | $this->user->save(); 56 | 57 | msg_success('User has been successfully updated.'); 58 | 59 | return redirect()->route('users.index'); 60 | } 61 | 62 | /** 63 | * Validation rules. 64 | * 65 | * @return array 66 | */ 67 | protected function rules() 68 | { 69 | return [ 70 | 'user.email' => [ 71 | 'required', 72 | 'email', 73 | Rule::unique('users', 'email')->ignore($this->user->id), 74 | ], 75 | 'user.role_id' => [ 76 | 'required', 77 | 'exists:roles,id', 78 | ], 79 | ]; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /app/Http/Livewire/HasLivewireAuth.php: -------------------------------------------------------------------------------- 1 | guest()) { 29 | throw new AuthenticationException(); 30 | } 31 | 32 | $this->getPermissionName(); 33 | 34 | $this->authorize('for-route', [$this->permissionName, $this->getModel()]); 35 | } 36 | 37 | /** 38 | * Get bind model. 39 | * 40 | * @return mixed 41 | */ 42 | public function getModel() 43 | { 44 | if (method_exists($this, 'setModel')) { 45 | return $this->setModel(); 46 | } 47 | 48 | return collect(get_object_vars($this)) 49 | ->filter(function ($variable) { 50 | return $variable instanceof Model; 51 | })->first(); 52 | } 53 | 54 | /** 55 | * Get permission name. 56 | * 57 | * @return void 58 | */ 59 | public function getPermissionName() 60 | { 61 | if (isset($this->permissionName)) { 62 | return; 63 | } 64 | 65 | $splitted = explode('-', self::getName()); 66 | $this->permissionName = Str::plural($splitted[1]).'.'.$splitted[0]; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/Http/Livewire/HasTable.php: -------------------------------------------------------------------------------- 1 | sortField === $field && $this->sortDirection === 'asc') { 31 | $this->sortDirection = 'desc'; 32 | } elseif ($this->sortField === $field && $this->sortDirection === 'asc') { 33 | $this->sortDirection = 'asc'; 34 | } else { 35 | $this->sortDirection = 'asc'; 36 | } 37 | 38 | $this->sortField = $field; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/Http/Livewire/IndexRoleComponent.php: -------------------------------------------------------------------------------- 1 | 'render']; 20 | 21 | /** 22 | * Render the component view. 23 | * 24 | * @return \Illuminate\View\View 25 | */ 26 | public function render() 27 | { 28 | $roles = Role::filter([ 29 | 'search' => $this->search, 30 | 'orderByField' => [$this->sortField, $this->sortDirection], 31 | ])->paginate($this->perPage); 32 | 33 | return view('roles.index', ['roles' => $roles]) 34 | ->extends('layouts.app'); 35 | } 36 | 37 | /** 38 | * Reset pagination back to page one if search query is changed. 39 | * 40 | * @return void 41 | */ 42 | public function updatedSearch() 43 | { 44 | $this->resetPage(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/Http/Livewire/IndexUserComponent.php: -------------------------------------------------------------------------------- 1 | 'render']; 30 | 31 | /** 32 | * Render the component view. 33 | * 34 | * @return \Illuminate\View\View 35 | */ 36 | public function render() 37 | { 38 | $users = User::with('role') 39 | ->filter([ 40 | 'orderByField' => [$this->sortField, $this->sortDirection], 41 | 'search' => $this->search, 42 | 'roleId' => $this->roleId, 43 | ])->paginate($this->perPage); 44 | 45 | $roles = Role::orderBy('name')->get(); 46 | 47 | return view('users.index', ['users' => $users, 'roles' => $roles]) 48 | ->extends('layouts.app'); 49 | } 50 | 51 | /** 52 | * Reset pagination back to page one if search query is changed. 53 | * 54 | * @return void 55 | */ 56 | public function updatedSearch() 57 | { 58 | $this->resetPage(); 59 | } 60 | 61 | /** 62 | * Reset pagination back to page one if roleId query is changed. 63 | * 64 | * @return void 65 | */ 66 | public function updatedRoleId() 67 | { 68 | $this->resetPage(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /app/Http/Livewire/Profile/UpdateEmail.php: -------------------------------------------------------------------------------- 1 | guest()) { 35 | throw new AuthenticationException(); 36 | } 37 | } 38 | 39 | /** 40 | * Component mount. 41 | * 42 | * @return void 43 | */ 44 | public function mount() 45 | { 46 | $this->user = auth()->user(); 47 | } 48 | 49 | /** 50 | * Submit the form. 51 | * 52 | * @return void 53 | */ 54 | public function submit() 55 | { 56 | $this->validate($this->validationRules()); 57 | 58 | Mail::to($this->email) 59 | ->send(new NewEmailConfirmationMail($this->user, Carbon::tomorrow(), $this->email)); 60 | 61 | $this->dispatchFlashSuccessEvent( 62 | "Confirmation email was sent to {$this->email}. Please verify your new email address." 63 | ); 64 | 65 | $this->dispatchBrowserEvent('close'); 66 | 67 | $this->clearInput(); 68 | } 69 | 70 | /** 71 | * Render the component view. 72 | * 73 | * @return \Illuminate\View\View 74 | */ 75 | public function render() 76 | { 77 | return view('livewire.profile.update-email'); 78 | } 79 | 80 | /** 81 | * Get the validation rules. 82 | * 83 | * @return array 84 | */ 85 | private function validationRules() 86 | { 87 | return [ 88 | 'email' => [ 89 | 'required', 90 | 'email', 91 | 'unique:users,email', 92 | ], 93 | 'currentPassword' => [ 94 | 'required', 95 | new PasswordCheckRule($this->user), 96 | ], 97 | ]; 98 | } 99 | 100 | /** 101 | * Reset public properties back to empty string. 102 | * 103 | * @return void 104 | */ 105 | private function clearInput() 106 | { 107 | $this->email = ''; 108 | $this->currentPassword = ''; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /app/Http/Livewire/Profile/UpdateImage.php: -------------------------------------------------------------------------------- 1 | guest()) { 30 | throw new AuthenticationException(); 31 | } 32 | } 33 | 34 | /** 35 | * Component mount. 36 | * 37 | * @return void 38 | */ 39 | public function mount() 40 | { 41 | $this->user = auth()->user(); 42 | } 43 | 44 | /** 45 | * Submit the form. 46 | * 47 | * @return void 48 | */ 49 | public function submit() 50 | { 51 | $this->validate($this->validationRules()); 52 | 53 | if ($this->user->image !== null) { 54 | Storage::disk('avatar')->delete("{$this->user->image}"); 55 | } 56 | 57 | $imageName = $this->image->store('/', 'avatar'); 58 | 59 | $this->user->saveImage($imageName); 60 | 61 | ProfileImageUploaded::dispatch($this->user); 62 | 63 | msg_success('Your profile\'s image has been successfully updated'); 64 | 65 | return redirect()->route('profile.users.index'); 66 | } 67 | 68 | /** 69 | * Render the component view. 70 | * 71 | * @return \Illuminate\View\View 72 | */ 73 | public function render() 74 | { 75 | return view('livewire.profile.update-image'); 76 | } 77 | 78 | /** 79 | * Get the validation rules. 80 | * 81 | * @return array 82 | */ 83 | private function validationRules() 84 | { 85 | return [ 86 | 'image' => [ 87 | 'required', 88 | 'image', 89 | 'dimensions:min_width=100,min_height=100', 90 | ], 91 | ]; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /app/Http/Livewire/Profile/UpdatePassword.php: -------------------------------------------------------------------------------- 1 | guest()) { 38 | throw new AuthenticationException(); 39 | } 40 | } 41 | 42 | /** 43 | * Component mount. 44 | * 45 | * @return void 46 | */ 47 | public function mount() 48 | { 49 | $this->user = auth()->user(); 50 | } 51 | 52 | /** 53 | * Submit the form. 54 | * 55 | * @return void 56 | */ 57 | public function submit() 58 | { 59 | $this->validate($this->validationRules()); 60 | 61 | $this->user->savePassword($this->newPassword); 62 | 63 | Mail::to($this->user->email) 64 | ->send(new PasswordChangedMail()); 65 | 66 | $this->dispatchBrowserEvent('close'); 67 | 68 | $this->dispatchFlashSuccessEvent('You password has been successfully updated.'); 69 | 70 | $this->clearInput(); 71 | } 72 | 73 | /** 74 | * Render the component view. 75 | * 76 | * @return \Illuminate\View\View 77 | */ 78 | public function render() 79 | { 80 | return view('livewire.profile.update-password'); 81 | } 82 | 83 | /** 84 | * Get the validation rules. 85 | * 86 | * @return array 87 | */ 88 | private function validationRules() 89 | { 90 | return [ 91 | 'newPassword' => [ 92 | new PasswordRule($this->newPasswordConfirmation), 93 | ], 94 | 'currentPassword' => [ 95 | 'required', 96 | new PasswordCheckRule($this->user), 97 | ], 98 | ]; 99 | } 100 | 101 | /** 102 | * Reset public properties back to empty string. 103 | * 104 | * @return void 105 | */ 106 | private function clearInput() 107 | { 108 | $this->newPassword = ''; 109 | $this->newPasswordConfirmation = ''; 110 | $this->currentPassword = ''; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /app/Http/Middleware/Authenticate.php: -------------------------------------------------------------------------------- 1 | expectsJson()) { 18 | return route('login'); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/Http/Middleware/Authorization.php: -------------------------------------------------------------------------------- 1 | route()->action['controller']; 21 | 22 | $model = (new ImplicitRouteBinding(new Container()))->resolveComponentProps( 23 | $request->route(), new $component() 24 | ); 25 | 26 | Gate::authorize('for-route', [$request->route()->getName(), $model->first() ?? null]); 27 | 28 | return $next($request); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/Http/Middleware/CheckForMaintenanceMode.php: -------------------------------------------------------------------------------- 1 | check()) { 22 | return redirect(RouteServiceProvider::HOME); 23 | } 24 | 25 | return $next($request); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/Http/Middleware/TrimStrings.php: -------------------------------------------------------------------------------- 1 | get("{$event->user->image}"); 23 | 24 | Image::make($file) 25 | ->resize(100, 100) 26 | ->save(config('filesystems.disks.avatar.root')."/{$event->user->image}"); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/Mail/InvitationMail.php: -------------------------------------------------------------------------------- 1 | user = $user; 36 | $this->validUntil = $validUntil; 37 | } 38 | 39 | /** 40 | * Build the message. 41 | * 42 | * @return $this 43 | */ 44 | public function build() 45 | { 46 | return $this->from(config('mail.from.address'), config('mail.from.name')) 47 | ->subject('Welcome') 48 | ->markdown('mails.invitation-mail', ['signedUrl' => $this->createTemporarySignedRoute()]); 49 | } 50 | 51 | private function createTemporarySignedRoute() 52 | { 53 | return URL::temporarySignedRoute( 54 | 'accepted-invitations.create', 55 | $this->validUntil, 56 | ['user' => $this->user->id] 57 | ); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /app/Mail/NewEmailConfirmationMail.php: -------------------------------------------------------------------------------- 1 | user = $user; 40 | $this->validUntil = $validUntil; 41 | $this->newEmail = $newEmail; 42 | } 43 | 44 | /** 45 | * Build the message. 46 | * 47 | * @return $this 48 | */ 49 | public function build() 50 | { 51 | return $this->from(config('mail.from.address'), config('mail.from.name')) 52 | ->subject('Please confirm your new email address.') 53 | ->markdown('mails.new-email-confirmation-mail', [ 54 | 'signedUrl' => $this->createTemporarySignedRoute(), 55 | 'newEmail' => $this->newEmail, 56 | ]); 57 | } 58 | 59 | private function createTemporarySignedRoute() 60 | { 61 | return URL::temporarySignedRoute( 62 | 'confirmed-emails.store', 63 | $this->validUntil, 64 | ['user' => $this->user->id, 'new_email' => $this->newEmail] 65 | ); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /app/Mail/PasswordChangedMail.php: -------------------------------------------------------------------------------- 1 | from(config('mail.from.address'), config('mail.from.name')) 22 | ->subject('Security notification regarding your password') 23 | ->markdown('mails.password-changed-mail'); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/Models/Permission.php: -------------------------------------------------------------------------------- 1 | 'integer', 23 | ]; 24 | 25 | /** 26 | * The roles that belong to the permissions. 27 | * 28 | * @return \Illuminate\Database\Eloquent\Relations\belongsToMany 29 | */ 30 | public function roles() 31 | { 32 | return $this->belongsToMany(Role::class) 33 | ->withPivot('owner_restricted') 34 | ->using(PermissionRole::class); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/Models/PermissionRole.php: -------------------------------------------------------------------------------- 1 | 'integer', 16 | 'owner_restricted' => 'boolean', 17 | 'role_id' => 'integer', 18 | 'permission_id' => 'integer', 19 | ]; 20 | } 21 | -------------------------------------------------------------------------------- /app/Providers/AppServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->isLocal()) { 26 | $this->app->register(\Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class); 27 | } 28 | 29 | Blade::directive('errorClass', function ($field) { 30 | $ifStatement = "if(session()->has('errors') && session('errors')->has(${field}))"; 31 | 32 | return ""; 33 | }); 34 | } 35 | 36 | /** 37 | * Bootstrap any application services. 38 | * 39 | * @return void 40 | */ 41 | public function boot() 42 | { 43 | Schema::defaultStringLength(191); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/Providers/AuthServiceProvider.php: -------------------------------------------------------------------------------- 1 | registerPolicies(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/Providers/BroadcastServiceProvider.php: -------------------------------------------------------------------------------- 1 | [ 20 | SendEmailVerificationNotification::class, 21 | ], 22 | ProfileImageUploaded::class => [ 23 | ResizeImage::class, 24 | ], 25 | ]; 26 | 27 | /** 28 | * Register any events for your application. 29 | * 30 | * @return void 31 | */ 32 | public function boot() 33 | { 34 | parent::boot(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/Providers/RouteServiceProvider.php: -------------------------------------------------------------------------------- 1 | mapApiRoutes(); 33 | 34 | $this->mapWebRoutes(); 35 | } 36 | 37 | /** 38 | * Define the "web" routes for the application. 39 | * 40 | * These routes all receive session state, CSRF protection, etc. 41 | * 42 | * @return void 43 | */ 44 | protected function mapWebRoutes() 45 | { 46 | Route::middleware('web') 47 | ->group(base_path('routes/web.php')); 48 | } 49 | 50 | /** 51 | * Define the "api" routes for the application. 52 | * 53 | * These routes are typically stateless. 54 | * 55 | * @return void 56 | */ 57 | protected function mapApiRoutes() 58 | { 59 | Route::prefix('api') 60 | ->middleware('api') 61 | ->group(base_path('routes/api.php')); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/Rules/OwnerRestrictedRule.php: -------------------------------------------------------------------------------- 1 | permissions = $permissions; 21 | } 22 | 23 | /** 24 | * Determine if the validation rule passes. 25 | * 26 | * @param string $attribute 27 | * @param mixed $value 28 | * @return bool 29 | */ 30 | public function passes($attribute, $value) 31 | { 32 | $splitted = preg_split('/\./', $attribute); 33 | $index = $splitted[1]; 34 | 35 | return $value !== true || $this->permissions[$index]['allowed'] !== false; 36 | } 37 | 38 | /** 39 | * Get the validation error message. 40 | * 41 | * @return string 42 | */ 43 | public function message() 44 | { 45 | return 'The :attribute is allowed only when corresponding permission is selected.'; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/Rules/PasswordCheckRule.php: -------------------------------------------------------------------------------- 1 | user = $user; 23 | } 24 | 25 | /** 26 | * Determine if the validation rule passes. 27 | * 28 | * @param string $attribute 29 | * @param mixed $password 30 | * @return bool 31 | */ 32 | public function passes($attribute, $password) 33 | { 34 | return Hash::check($password, $this->user->password); 35 | } 36 | 37 | /** 38 | * Get the validation error message. 39 | * 40 | * @return string 41 | */ 42 | public function message() 43 | { 44 | return trans('validation.password_check'); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/Rules/PasswordRule.php: -------------------------------------------------------------------------------- 1 | confirmationValue = $confirmationValue; 28 | } 29 | 30 | /** 31 | * Determine if the validation rule passes. 32 | * 33 | * @param string $attribute 34 | * @param mixed $value 35 | * @return bool 36 | * @throws \Illuminate\Validation\ValidationException 37 | */ 38 | public function passes($attribute, $value) 39 | { 40 | $validator = Validator::make([ 41 | $attribute => $value, 42 | $attribute.'_confirmation' => $this->confirmationValue, 43 | ], [ 44 | $attribute => ['required', 'confirmed', Password::min(AppServiceProvider::MIN_PASSWORD_LENGTH)->uncompromised()], 45 | ]); 46 | 47 | try { 48 | $validator->validate(); 49 | } catch (ValidationException $exception) { 50 | $this->message = $validator->getMessageBag()->first(); 51 | 52 | return false; 53 | } 54 | 55 | return true; 56 | } 57 | 58 | /** 59 | * Get the validation error message. 60 | * 61 | * @return string 62 | */ 63 | public function message() 64 | { 65 | return $this->message; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /app/Rules/PermissionExistsRule.php: -------------------------------------------------------------------------------- 1 | hasUser()) { 23 | return $builder; 24 | } 25 | 26 | $user = auth()->user(); 27 | 28 | if ($this->returnEarly($user)) { 29 | return $builder; 30 | } 31 | 32 | if ($this->returnEarlyPermission($user, $model)) { 33 | return $builder; 34 | } 35 | 36 | return $builder->where(AppServiceProvider::OWNER_FIELD, $user->id); 37 | } 38 | 39 | /** 40 | * Should we return early form global scope base on permission and owner field. 41 | * 42 | * @param \App\Models\User $user 43 | * @param \Illuminate\Database\Eloquent\Model $model 44 | * @return bool 45 | */ 46 | public function returnEarlyPermission($user, $model) 47 | { 48 | $permission = $user->getPermission($model->getTable().'.index'); 49 | 50 | if (! $permission->pivot->owner_restricted === true) { 51 | return true; 52 | } 53 | 54 | if (! Schema::hasColumn($model->getTable(), AppServiceProvider::OWNER_FIELD)) { 55 | return true; 56 | } 57 | 58 | return false; 59 | } 60 | 61 | /** 62 | * Should we return early form global scope. 63 | * 64 | * @param \App\Models\User $user 65 | * @return bool 66 | */ 67 | private function returnEarly($user) 68 | { 69 | if ($user === null) { 70 | return true; 71 | } 72 | 73 | if ($user->isAdmin()) { 74 | return true; 75 | } 76 | 77 | return false; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /app/Services/ForRouteGate.php: -------------------------------------------------------------------------------- 1 | isAdmin()) { 23 | return true; 24 | } 25 | 26 | if ($permissionName === '' || $permissionName === null) { 27 | return false; 28 | } 29 | 30 | if (! Str::contains($permissionName, ['show', 'edit', 'delete'])) { 31 | return $user->hasPermission($permissionName); 32 | } 33 | 34 | return $this->forOwner($user, $permissionName, $model); 35 | } 36 | 37 | /** 38 | * Get permission for non-admin role where value of owner restricted fields is important. 39 | * 40 | * @param \App\Models\User $user 41 | * @param string $permissionName 42 | * @param mixed $model 43 | * @return bool 44 | */ 45 | public function forOwner($user, $permissionName, $model) 46 | { 47 | if ($model === null) { 48 | throw new MissingModel(); 49 | } 50 | 51 | $ownerField = AppServiceProvider::OWNER_FIELD; 52 | 53 | if ($model->$ownerField === null) { 54 | return $user->hasPermission($permissionName); 55 | } 56 | 57 | return $user->isModelOwner($permissionName, $model); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /app/ViewModels/SaveRoleViewModel.php: -------------------------------------------------------------------------------- 1 | get(); 19 | 20 | $assignedPermissions = []; 21 | $rolePermissions = self::getRolePermissions($roleId); 22 | 23 | foreach ($permissions as $permission) { 24 | $assignedPermissions[$permission->id]['group'] = $permission->group; 25 | $assignedPermissions[$permission->id]['description'] = $permission->description; 26 | $assignedPermissions[$permission->id]['allowed'] = key_exists($permission->id, $rolePermissions); 27 | $assignedPermissions[$permission->id]['owner_restricted'] = $rolePermissions[$permission->id] ?? false; 28 | } 29 | 30 | return $assignedPermissions; 31 | } 32 | 33 | /** 34 | * Group permissions by group name. 35 | * 36 | * @param array $permissions 37 | * @return array 38 | */ 39 | public static function groupPermissions($permissions) 40 | { 41 | return collect($permissions) 42 | ->groupBy('group', true) 43 | ->sortKeys() 44 | ->toArray(); 45 | } 46 | 47 | /** 48 | * Get current role permissions. 49 | * 50 | * @param int $roleId 51 | * @return array 52 | */ 53 | private static function getRolePermissions($roleId) 54 | { 55 | if ($roleId === null) { 56 | return []; 57 | } 58 | 59 | return PermissionRole::where('role_id', $roleId) 60 | ->get() 61 | ->pluck('owner_restricted', 'permission_id') 62 | ->toArray(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/helpers.php: -------------------------------------------------------------------------------- 1 | flash('flash', ['message' => $message, 'level' => 'success']); 13 | } 14 | } 15 | 16 | if (! function_exists('msg_error')) { 17 | /** 18 | * Flash error message. 19 | * 20 | * @param string $message 21 | * @return void 22 | */ 23 | function msg_error($message) 24 | { 25 | session()->flash('flash', ['message' => $message, 'level' => 'danger']); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /artisan: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | make(Illuminate\Contracts\Console\Kernel::class); 34 | 35 | $status = $kernel->handle( 36 | $input = new Symfony\Component\Console\Input\ArgvInput, 37 | new Symfony\Component\Console\Output\ConsoleOutput 38 | ); 39 | 40 | /* 41 | |-------------------------------------------------------------------------- 42 | | Shutdown The Application 43 | |-------------------------------------------------------------------------- 44 | | 45 | | Once Artisan has finished running, we will fire off the shutdown events 46 | | so that any final work may be done by the application before we shut 47 | | down the process. This is the last thing to happen to the request. 48 | | 49 | */ 50 | 51 | $kernel->terminate($input, $status); 52 | 53 | exit($status); 54 | -------------------------------------------------------------------------------- /bootstrap/app.php: -------------------------------------------------------------------------------- 1 | singleton( 30 | Illuminate\Contracts\Http\Kernel::class, 31 | App\Http\Kernel::class 32 | ); 33 | 34 | $app->singleton( 35 | Illuminate\Contracts\Console\Kernel::class, 36 | App\Console\Kernel::class 37 | ); 38 | 39 | $app->singleton( 40 | Illuminate\Contracts\Debug\ExceptionHandler::class, 41 | App\Exceptions\Handler::class 42 | ); 43 | 44 | /* 45 | |-------------------------------------------------------------------------- 46 | | Return The Application 47 | |-------------------------------------------------------------------------- 48 | | 49 | | This script returns the application instance. The instance is given to 50 | | the calling script so we can separate the building of the instances 51 | | from the actual running of the application and sending responses. 52 | | 53 | */ 54 | 55 | return $app; 56 | -------------------------------------------------------------------------------- /bootstrap/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /config/broadcasting.php: -------------------------------------------------------------------------------- 1 | env('BROADCAST_DRIVER', 'null'), 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Broadcast Connections 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Here you may define all of the broadcast connections that will be used 26 | | to broadcast events to other systems or over websockets. Samples of 27 | | each available type of connection are provided inside this array. 28 | | 29 | */ 30 | 31 | 'connections' => [ 32 | 33 | 'pusher' => [ 34 | 'driver' => 'pusher', 35 | 'key' => env('PUSHER_APP_KEY'), 36 | 'secret' => env('PUSHER_APP_SECRET'), 37 | 'app_id' => env('PUSHER_APP_ID'), 38 | 'options' => [ 39 | 'cluster' => env('PUSHER_APP_CLUSTER'), 40 | 'encrypted' => true, 41 | ], 42 | ], 43 | 44 | 'redis' => [ 45 | 'driver' => 'redis', 46 | 'connection' => 'default', 47 | ], 48 | 49 | 'log' => [ 50 | 'driver' => 'log', 51 | ], 52 | 53 | 'null' => [ 54 | 'driver' => 'null', 55 | ], 56 | 57 | ], 58 | 59 | ]; 60 | -------------------------------------------------------------------------------- /config/filesystems.php: -------------------------------------------------------------------------------- 1 | env('FILESYSTEM_DRIVER', 'local'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Default Cloud Filesystem Disk 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Many applications store files both locally and in the cloud. For this 24 | | reason, you may specify a default "cloud" driver here. This driver 25 | | will be bound as the Cloud disk implementation in the container. 26 | | 27 | */ 28 | 29 | 'cloud' => env('FILESYSTEM_CLOUD', 's3'), 30 | 31 | /* 32 | |-------------------------------------------------------------------------- 33 | | Filesystem Disks 34 | |-------------------------------------------------------------------------- 35 | | 36 | | Here you may configure as many filesystem "disks" as you wish, and you 37 | | may even configure multiple disks of the same driver. Defaults have 38 | | been setup for each driver as an example of the required options. 39 | | 40 | | Supported Drivers: "local", "ftp", "sftp", "s3", "rackspace" 41 | | 42 | */ 43 | 44 | 'disks' => [ 45 | 46 | 'local' => [ 47 | 'driver' => 'local', 48 | 'root' => storage_path('app'), 49 | ], 50 | 51 | 'public' => [ 52 | 'driver' => 'local', 53 | 'root' => storage_path('app/public'), 54 | 'url' => env('APP_URL').'/storage', 55 | 'visibility' => 'public', 56 | ], 57 | 58 | 'avatar' => [ 59 | 'driver' => 'local', 60 | 'root' => storage_path('app/public/avatars'), 61 | 'url' => env('APP_URL').'/storage/avatars', 62 | 'visibility' => 'public', 63 | ], 64 | 65 | 's3' => [ 66 | 'driver' => 's3', 67 | 'key' => env('AWS_ACCESS_KEY_ID'), 68 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 69 | 'region' => env('AWS_DEFAULT_REGION'), 70 | 'bucket' => env('AWS_BUCKET'), 71 | 'url' => env('AWS_URL'), 72 | ], 73 | 74 | ], 75 | 76 | ]; 77 | -------------------------------------------------------------------------------- /config/hashing.php: -------------------------------------------------------------------------------- 1 | 'bcrypt', 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Bcrypt Options 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Here you may specify the configuration options that should be used when 26 | | passwords are hashed using the Bcrypt algorithm. This will allow you 27 | | to control the amount of time it takes to hash the given password. 28 | | 29 | */ 30 | 31 | 'bcrypt' => [ 32 | 'rounds' => env('BCRYPT_ROUNDS', 10), 33 | ], 34 | 35 | /* 36 | |-------------------------------------------------------------------------- 37 | | Argon Options 38 | |-------------------------------------------------------------------------- 39 | | 40 | | Here you may specify the configuration options that should be used when 41 | | passwords are hashed using the Argon algorithm. These will allow you 42 | | to control the amount of time it takes to hash the given password. 43 | | 44 | */ 45 | 46 | 'argon' => [ 47 | 'memory' => 1024, 48 | 'threads' => 2, 49 | 'time' => 2, 50 | ], 51 | 52 | ]; 53 | -------------------------------------------------------------------------------- /config/services.php: -------------------------------------------------------------------------------- 1 | [ 18 | 'domain' => env('MAILGUN_DOMAIN'), 19 | 'secret' => env('MAILGUN_SECRET'), 20 | 'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'), 21 | ], 22 | 23 | 'postmark' => [ 24 | 'token' => env('POSTMARK_TOKEN'), 25 | ], 26 | 27 | 'ses' => [ 28 | 'key' => env('AWS_ACCESS_KEY_ID'), 29 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 30 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 31 | ], 32 | 33 | 'sparkpost' => [ 34 | 'secret' => env('SPARKPOST_SECRET'), 35 | ], 36 | 37 | 'stripe' => [ 38 | 'model' => App\Models\User::class, 39 | 'key' => env('STRIPE_KEY'), 40 | 'secret' => env('STRIPE_SECRET'), 41 | 'webhook' => [ 42 | 'secret' => env('STRIPE_WEBHOOK_SECRET'), 43 | 'tolerance' => env('STRIPE_WEBHOOK_TOLERANCE', 300), 44 | ], 45 | ], 46 | 47 | ]; 48 | -------------------------------------------------------------------------------- /config/view.php: -------------------------------------------------------------------------------- 1 | [ 17 | resource_path('views'), 18 | ], 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Compiled View Path 23 | |-------------------------------------------------------------------------- 24 | | 25 | | This option determines where all the compiled Blade templates will be 26 | | stored for your application. Typically, this is within the storage 27 | | directory. However, as usual, you are free to change this value. 28 | | 29 | */ 30 | 31 | 'compiled' => env( 32 | 'VIEW_COMPILED_PATH', 33 | realpath(storage_path('framework/views')) 34 | ), 35 | 36 | ]; 37 | -------------------------------------------------------------------------------- /database/.gitignore: -------------------------------------------------------------------------------- 1 | *.sqlite 2 | *.sqlite-journal 3 | -------------------------------------------------------------------------------- /database/factories/PermissionFactory.php: -------------------------------------------------------------------------------- 1 | strtoupper($this->faker->word), 26 | 'name' => $this->faker->word, 27 | 'description' => $this->faker->sentence, 28 | ]; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /database/factories/RoleFactory.php: -------------------------------------------------------------------------------- 1 | strtoupper($this->faker->word), 26 | 'label' => $this->faker->word, 27 | ]; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /database/factories/UserFactory.php: -------------------------------------------------------------------------------- 1 | RoleFactory::new(), 28 | AppServiceProvider::OWNER_FIELD => 1, 29 | 'email' => $this->faker->unique()->safeEmail, 30 | 'email_verified_at' => now(), 31 | 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password 32 | 'remember_token' => Str::random(10), 33 | 'image' => null, 34 | ]; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /database/migrations/2013_03_11_165941_create_roles_table.php: -------------------------------------------------------------------------------- 1 | bigIncrements('id'); 18 | $table->string('name', 255); 19 | $table->string('label', 255); 20 | $table->timestamps(); 21 | $table->softDeletes(); 22 | }); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /database/migrations/2014_10_12_000000_create_users_table.php: -------------------------------------------------------------------------------- 1 | bigIncrements('id'); 19 | $table->unsignedBigInteger(AppServiceProvider::OWNER_FIELD); 20 | $table->foreign(AppServiceProvider::OWNER_FIELD)->references('id')->on('users'); 21 | $table->unsignedBigInteger('role_id'); 22 | $table->foreign('role_id')->references('id')->on('roles'); 23 | $table->string('email')->unique(); 24 | $table->timestamp('email_verified_at')->nullable(); 25 | $table->string('password')->nullable(); 26 | $table->rememberToken(); 27 | $table->string('image')->nullable(); 28 | $table->timestamps(); 29 | $table->softDeletes(); 30 | }); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /database/migrations/2014_10_12_100000_create_password_resets_table.php: -------------------------------------------------------------------------------- 1 | string('email')->index(); 18 | $table->string('token'); 19 | $table->timestamp('created_at')->nullable(); 20 | }); 21 | } 22 | 23 | /** 24 | * Reverse the migrations. 25 | * 26 | * @return void 27 | */ 28 | public function down() 29 | { 30 | Schema::dropIfExists('password_resets'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /database/migrations/2020_02_03_125830_create_sessions_table.php: -------------------------------------------------------------------------------- 1 | string('id')->unique(); 18 | $table->unsignedBigInteger('user_id')->nullable(); 19 | $table->string('ip_address', 45)->nullable(); 20 | $table->text('user_agent')->nullable(); 21 | $table->text('payload'); 22 | $table->integer('last_activity'); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | * 29 | * @return void 30 | */ 31 | public function down() 32 | { 33 | Schema::dropIfExists('sessions'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /database/migrations/2020_02_03_132010_create_jobs_table.php: -------------------------------------------------------------------------------- 1 | bigIncrements('id'); 18 | $table->string('queue')->index(); 19 | $table->longText('payload'); 20 | $table->unsignedTinyInteger('attempts'); 21 | $table->unsignedInteger('reserved_at')->nullable(); 22 | $table->unsignedInteger('available_at'); 23 | $table->unsignedInteger('created_at'); 24 | }); 25 | } 26 | 27 | /** 28 | * Reverse the migrations. 29 | * 30 | * @return void 31 | */ 32 | public function down() 33 | { 34 | Schema::dropIfExists('jobs'); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /database/migrations/2020_03_26_110221_create_permissions_table.php: -------------------------------------------------------------------------------- 1 | bigIncrements('id'); 18 | $table->string('group'); 19 | $table->string('name'); 20 | $table->text('description'); 21 | $table->timestamps(); 22 | }); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /database/migrations/2023_03_26_110250_create_permission_role_table.php: -------------------------------------------------------------------------------- 1 | bigIncrements('id'); 18 | $table->unsignedBigInteger('permission_id'); 19 | $table->foreign('permission_id')->references('id')->on('permissions'); 20 | $table->unsignedBigInteger('role_id'); 21 | $table->foreign('role_id')->references('id')->on('roles'); 22 | $table->boolean('owner_restricted')->nullable(); 23 | }); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /database/seeders/DatabaseSeeder.php: -------------------------------------------------------------------------------- 1 | environment('production')) { 17 | return; 18 | } 19 | 20 | $this->call([ 21 | RolesTableSeeder::class, 22 | PermissionsTableSeeder::class, 23 | UsersTableSeeder::class, 24 | ]); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /database/seeders/PermissionsTableSeeder.php: -------------------------------------------------------------------------------- 1 | create([ 18 | 'group' => 'users', 19 | 'name' => 'users.index', 20 | 'description' => 'View Users', 21 | ]); 22 | 23 | PermissionFactory::new()->create([ 24 | 'group' => 'users', 25 | 'name' => 'users.create', 26 | 'description' => 'Create New User', 27 | ]); 28 | 29 | PermissionFactory::new()->create([ 30 | 'group' => 'users', 31 | 'name' => 'users.edit', 32 | 'description' => 'Edit Existing User', 33 | ]); 34 | 35 | PermissionFactory::new()->create([ 36 | 'group' => 'users', 37 | 'name' => 'users.delete', 38 | 'description' => 'Delete Existing User', 39 | ]); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /database/seeders/RolesTableSeeder.php: -------------------------------------------------------------------------------- 1 | create([ 18 | 'name' => 'admin', 19 | 'label' => 'admin', 20 | ]); 21 | 22 | RoleFactory::new()->create([ 23 | 'name' => 'manager', 24 | 'label' => 'manager', 25 | ]); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /database/seeders/UsersTableSeeder.php: -------------------------------------------------------------------------------- 1 | create([ 19 | 'email' => 'admin@lte.com', 20 | 'role_id' => Role::whereName('admin')->first(), 21 | ]); 22 | 23 | UserFactory::new()->create([ 24 | 'email' => 'manager@lte.com', 25 | 'role_id' => Role::whereName('manager')->first(), 26 | ]); 27 | 28 | for ($i = 1; $i < 10; $i++) { 29 | UserFactory::new()->create([ 30 | 'role_id' => Role::whereName('manager')->first(), 31 | ]); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "npm run development", 5 | "development": "mix", 6 | "watch": "mix watch", 7 | "watch-poll": "mix watch -- --watch-options-poll=1000", 8 | "hot": "mix watch --hot", 9 | "prod": "npm run production", 10 | "production": "mix --production" 11 | }, 12 | "devDependencies": { 13 | "cross-env": "^7.0.3", 14 | "laravel-mix": "^6.0.41", 15 | "vue-template-compiler": "^2.6.14" 16 | }, 17 | "dependencies": { 18 | "alpinejs": "^3.8.1", 19 | "axios": "^0.25.0", 20 | "websocket-driver": "^0.7.4" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | ./tests/Unit 14 | 15 | 16 | 17 | ./tests/Feature 18 | 19 | 20 | 21 | 22 | ./app 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 1000 43 | 44 | 45 | 5 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /public/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | 3 | Options -MultiViews -Indexes 4 | 5 | 6 | RewriteEngine On 7 | 8 | # Handle Authorization Header 9 | RewriteCond %{HTTP:Authorization} . 10 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] 11 | 12 | # Redirect Trailing Slashes If Not A Folder... 13 | RewriteCond %{REQUEST_FILENAME} !-d 14 | RewriteCond %{REQUEST_URI} (.+)/$ 15 | RewriteRule ^ %1 [L,R=301] 16 | 17 | # Handle Front Controller... 18 | RewriteCond %{REQUEST_FILENAME} !-d 19 | RewriteCond %{REQUEST_FILENAME} !-f 20 | RewriteRule ^ index.php [L] 21 | 22 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/santa70916112/laravelbot/f5ecad87dcf240b0f7e7eab87420b98f9de76241/public/favicon.ico -------------------------------------------------------------------------------- /public/images/default-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/santa70916112/laravelbot/f5ecad87dcf240b0f7e7eab87420b98f9de76241/public/images/default-user.png -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | 10 | define('LARAVEL_START', microtime(true)); 11 | 12 | /* 13 | |-------------------------------------------------------------------------- 14 | | Register The Auto Loader 15 | |-------------------------------------------------------------------------- 16 | | 17 | | Composer provides a convenient, automatically generated class loader for 18 | | our application. We just need to utilize it! We'll simply require it 19 | | into the script here so that we don't have to worry about manual 20 | | loading any of our classes later on. It feels great to relax. 21 | | 22 | */ 23 | 24 | require __DIR__.'/../vendor/autoload.php'; 25 | 26 | /* 27 | |-------------------------------------------------------------------------- 28 | | Turn On The Lights 29 | |-------------------------------------------------------------------------- 30 | | 31 | | We need to illuminate PHP development, so let us turn on the lights. 32 | | This bootstraps the framework and gets it ready for use, then it 33 | | will load up this application so that we can run it and send 34 | | the responses back to the browser and delight our users. 35 | | 36 | */ 37 | 38 | $app = require_once __DIR__.'/../bootstrap/app.php'; 39 | 40 | /* 41 | |-------------------------------------------------------------------------- 42 | | Run The Application 43 | |-------------------------------------------------------------------------- 44 | | 45 | | Once we have the application, we can handle the incoming request 46 | | through the kernel, and send the associated response back to 47 | | the client's browser allowing them to enjoy the creative 48 | | and wonderful application we have prepared for them. 49 | | 50 | */ 51 | 52 | $kernel = $app->make(Illuminate\Contracts\Http\Kernel::class); 53 | 54 | $response = $kernel->handle( 55 | $request = Illuminate\Http\Request::capture() 56 | ); 57 | 58 | $response->send(); 59 | 60 | $kernel->terminate($request, $response); 61 | -------------------------------------------------------------------------------- /public/mix-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "/js/app.js": "/js/app.js", 3 | "/webfonts/fa-brands-400.eot": "/webfonts/fa-brands-400.eot", 4 | "/webfonts/fa-brands-400.svg": "/webfonts/fa-brands-400.svg", 5 | "/webfonts/fa-brands-400.ttf": "/webfonts/fa-brands-400.ttf", 6 | "/webfonts/fa-brands-400.woff": "/webfonts/fa-brands-400.woff", 7 | "/webfonts/fa-brands-400.woff2": "/webfonts/fa-brands-400.woff2", 8 | "/webfonts/fa-regular-400.eot": "/webfonts/fa-regular-400.eot", 9 | "/webfonts/fa-regular-400.svg": "/webfonts/fa-regular-400.svg", 10 | "/webfonts/fa-regular-400.ttf": "/webfonts/fa-regular-400.ttf", 11 | "/webfonts/fa-regular-400.woff": "/webfonts/fa-regular-400.woff", 12 | "/webfonts/fa-regular-400.woff2": "/webfonts/fa-regular-400.woff2", 13 | "/webfonts/fa-solid-900.eot": "/webfonts/fa-solid-900.eot", 14 | "/webfonts/fa-solid-900.svg": "/webfonts/fa-solid-900.svg", 15 | "/webfonts/fa-solid-900.ttf": "/webfonts/fa-solid-900.ttf", 16 | "/webfonts/fa-solid-900.woff": "/webfonts/fa-solid-900.woff", 17 | "/webfonts/fa-solid-900.woff2": "/webfonts/fa-solid-900.woff2", 18 | "/images/default-user.png": "/images/default-user.png", 19 | "/css/app.css": "/css/app.css" 20 | } 21 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /public/web.config: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /public/webfonts/fa-brands-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/santa70916112/laravelbot/f5ecad87dcf240b0f7e7eab87420b98f9de76241/public/webfonts/fa-brands-400.eot -------------------------------------------------------------------------------- /public/webfonts/fa-brands-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/santa70916112/laravelbot/f5ecad87dcf240b0f7e7eab87420b98f9de76241/public/webfonts/fa-brands-400.ttf -------------------------------------------------------------------------------- /public/webfonts/fa-brands-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/santa70916112/laravelbot/f5ecad87dcf240b0f7e7eab87420b98f9de76241/public/webfonts/fa-brands-400.woff -------------------------------------------------------------------------------- /public/webfonts/fa-brands-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/santa70916112/laravelbot/f5ecad87dcf240b0f7e7eab87420b98f9de76241/public/webfonts/fa-brands-400.woff2 -------------------------------------------------------------------------------- /public/webfonts/fa-regular-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/santa70916112/laravelbot/f5ecad87dcf240b0f7e7eab87420b98f9de76241/public/webfonts/fa-regular-400.eot -------------------------------------------------------------------------------- /public/webfonts/fa-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/santa70916112/laravelbot/f5ecad87dcf240b0f7e7eab87420b98f9de76241/public/webfonts/fa-regular-400.ttf -------------------------------------------------------------------------------- /public/webfonts/fa-regular-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/santa70916112/laravelbot/f5ecad87dcf240b0f7e7eab87420b98f9de76241/public/webfonts/fa-regular-400.woff -------------------------------------------------------------------------------- /public/webfonts/fa-regular-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/santa70916112/laravelbot/f5ecad87dcf240b0f7e7eab87420b98f9de76241/public/webfonts/fa-regular-400.woff2 -------------------------------------------------------------------------------- /public/webfonts/fa-solid-900.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/santa70916112/laravelbot/f5ecad87dcf240b0f7e7eab87420b98f9de76241/public/webfonts/fa-solid-900.eot -------------------------------------------------------------------------------- /public/webfonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/santa70916112/laravelbot/f5ecad87dcf240b0f7e7eab87420b98f9de76241/public/webfonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /public/webfonts/fa-solid-900.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/santa70916112/laravelbot/f5ecad87dcf240b0f7e7eab87420b98f9de76241/public/webfonts/fa-solid-900.woff -------------------------------------------------------------------------------- /public/webfonts/fa-solid-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/santa70916112/laravelbot/f5ecad87dcf240b0f7e7eab87420b98f9de76241/public/webfonts/fa-solid-900.woff2 -------------------------------------------------------------------------------- /resources/css/custom.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | [x-cloak] { display: none; } 4 | 5 | .fade { 6 | opacity: 1; 7 | transition: opacity .25s ease-in-out; 8 | -moz-transition: opacity .25s ease-in-out; 9 | -webkit-transition: opacity .25s ease-in-out; 10 | } 11 | 12 | .fade:hover { 13 | opacity: 0.5; 14 | } 15 | 16 | .error { 17 | width: 100%; 18 | margin-top: 0.25rem; 19 | font-size: 100%; 20 | color: #dc3545; 21 | } 22 | 23 | .modal-backdrop.show { 24 | opacity: 0.8; 25 | } 26 | -------------------------------------------------------------------------------- /resources/images/default-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/santa70916112/laravelbot/f5ecad87dcf240b0f7e7eab87420b98f9de76241/resources/images/default-user.png -------------------------------------------------------------------------------- /resources/js/app.js: -------------------------------------------------------------------------------- 1 | import Alpine from 'alpinejs' 2 | 3 | window.Alpine = Alpine 4 | 5 | window.axios = require("axios"); 6 | 7 | window.axios.defaults.headers.common["X-Requested-With"] = "XMLHttpRequest"; 8 | 9 | window.nav = { 10 | width: 990, 11 | make() { 12 | return { 13 | collapsed: function () { 14 | if (window.innerWidth < this.width) { 15 | return true; 16 | } 17 | 18 | return false; 19 | }, 20 | resize() { 21 | if (window.innerWidth < this.width) { 22 | this.collapsed = true; 23 | } 24 | }, 25 | click() { 26 | this.collapsed = !this.collapsed; 27 | if (!this.collapsed) { 28 | this.$refs.body.classList.add("sidebar-open"); 29 | } else { 30 | this.$refs.body.classList.remove("sidebar-open"); 31 | } 32 | }, 33 | clickAway() { 34 | if (window.innerWidth < this.width) { 35 | this.$refs.body.classList.remove("sidebar-open"); 36 | this.collapsed = true; 37 | } 38 | } 39 | }; 40 | }, 41 | }; 42 | 43 | window.permissions = () => { 44 | return { 45 | checkAll(id, css) { 46 | document.getElementById(id).checked = Array.from(document.querySelectorAll('.' + css)).every((element) => { 47 | return element.checked === true 48 | }); 49 | }, 50 | reCheckedSelectAll(el, css) { 51 | let event = new Event('change'); 52 | Array.from(document.querySelectorAll('.' + css)).forEach((element) => { 53 | element.checked = el.checked 54 | element.dispatchEvent(event); 55 | }); 56 | } 57 | } 58 | }; 59 | 60 | Alpine.start() -------------------------------------------------------------------------------- /resources/lang/en/auth.php: -------------------------------------------------------------------------------- 1 | 'These credentials do not match our records.', 17 | 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', 18 | 19 | ]; 20 | -------------------------------------------------------------------------------- /resources/lang/en/pagination.php: -------------------------------------------------------------------------------- 1 | '« Previous', 17 | 'next' => 'Next »', 18 | 19 | ]; 20 | -------------------------------------------------------------------------------- /resources/lang/en/passwords.php: -------------------------------------------------------------------------------- 1 | 'Passwords must be at least eight characters and match the confirmation.', 17 | 'reset' => 'Your password has been reset!', 18 | 'sent' => 'We have e-mailed your password reset link!', 19 | 'token' => 'This password reset token is invalid.', 20 | 'user' => "We can't find a user with that e-mail address.", 21 | 22 | ]; 23 | -------------------------------------------------------------------------------- /resources/views/accepted-invitations/create.blade.php: -------------------------------------------------------------------------------- 1 | @section('title') 2 | Create New Account 3 | @endsection 4 | 5 |
6 |
7 | 8 | 9 |
10 | @csrf 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 | 19 |
20 |
21 | 22 |
23 |
24 | -------------------------------------------------------------------------------- /resources/views/auth/login.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.guest-app') 2 | 3 | @section('title') 4 | Sign In 5 | @endsection 6 | 7 | @section('content') 8 |
9 |
10 | 11 | 12 |
13 | @csrf 14 | 15 | 16 | 17 | 18 | 19 |
20 |
21 |
22 | 23 | 24 |
25 |
26 | 27 |
28 | 29 |
30 |
31 | 32 | 33 |

34 | I forgot my password 35 |

36 |
37 |
38 | 39 | @endsection 40 | -------------------------------------------------------------------------------- /resources/views/auth/passwords/confirm.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.guest-app') 2 | 3 | @section('title') 4 | Password Confirmation 5 | @endsection 6 | 7 | @section('content') 8 |
9 |
10 | 11 | 12 |
13 | @csrf 14 | 15 | 16 | 17 |
18 |
19 | 20 | 21 | @if (Route::has('password.request')) 22 | 23 | {{ __('Forgot Your Password?') }} 24 | 25 | @endif 26 |
27 |
28 | 29 |
30 |
31 | 32 | @endsection 33 | -------------------------------------------------------------------------------- /resources/views/auth/passwords/email.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.guest-app') 2 | 3 | @section('title') 4 | Reset Password 5 | @endsection 6 | 7 | @section('content') 8 |
9 |
10 | 11 | 12 | @include('layouts._flash') 13 | 14 |
15 | @csrf 16 | 17 | 18 | 19 |
20 |
21 | 22 |
23 |
24 | 25 |

26 | {{ __('Sing In') }} 27 |

28 |
29 |
30 | @endsection 31 | -------------------------------------------------------------------------------- /resources/views/auth/passwords/reset.blade.php: -------------------------------------------------------------------------------- 1 | 2 | @extends('layouts.guest-app') 3 | 4 | @section('title') 5 | Reset Password 6 | @endsection 7 | 8 | @section('content') 9 |
10 |
11 | 12 | 13 |
14 | @csrf 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 |
26 | 27 |
28 |
29 | 30 |
31 |
32 | @endsection 33 | -------------------------------------------------------------------------------- /resources/views/auth/verify.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.guest-app') 2 | 3 | @section('title') 4 | Verify Email Address 5 | @endsection 6 | 7 | @section('content') 8 |
9 |
10 | 11 | 12 | @if (session('resent')) 13 | 16 | @endif 17 | 18 | {{ __('Before proceeding, please check your email for a verification link.') }} 19 | {{ __('If you did not receive the email') }}, 20 |
21 | @csrf 22 | . 23 |
24 |
25 |
26 | -------------------------------------------------------------------------------- /resources/views/components/content-header.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

{{ $slot }}

5 |
6 |
7 |
-------------------------------------------------------------------------------- /resources/views/components/inputs/button.blade.php: -------------------------------------------------------------------------------- 1 | @props(['text']) 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/views/components/inputs/dropdown.blade.php: -------------------------------------------------------------------------------- 1 | @props(['options', 'key', 'textField']) 2 | 3 |
4 | 17 | 18 | 19 | 20 | 21 |
22 | -------------------------------------------------------------------------------- /resources/views/components/inputs/email.blade.php: -------------------------------------------------------------------------------- 1 | @props(['key' => 'email']) 2 | 3 |
4 | 13 | 14 | 15 | 16 | 17 |
18 | -------------------------------------------------------------------------------- /resources/views/components/inputs/error.blade.php: -------------------------------------------------------------------------------- 1 | @props(['field']) 2 | 3 | @error($field) 4 | {{ $message }} 5 | @enderror -------------------------------------------------------------------------------- /resources/views/components/inputs/fa.blade.php: -------------------------------------------------------------------------------- 1 | @props(['fontAwesome']) 2 | 3 |
4 |
5 | 6 |
7 |
-------------------------------------------------------------------------------- /resources/views/components/inputs/file.blade.php: -------------------------------------------------------------------------------- 1 | @props(['key']) 2 | 3 |
4 | 13 | 14 | 15 | 16 | 17 |
18 | -------------------------------------------------------------------------------- /resources/views/components/inputs/password.blade.php: -------------------------------------------------------------------------------- 1 | @props(['key' => 'password', 'showHidePasswordIcon' => false]) 2 | 3 |
4 | 13 | 14 | 15 | 16 | @if ($showHidePasswordIcon) 17 |
33 |
34 | 35 |
36 |
37 | @endif 38 | 39 | 40 |
41 | -------------------------------------------------------------------------------- /resources/views/components/inputs/text.blade.php: -------------------------------------------------------------------------------- 1 | @props(['key']) 2 | 3 |
4 | 12 | 13 | 14 | 15 | 16 |
17 | -------------------------------------------------------------------------------- /resources/views/components/profile/element.blade.php: -------------------------------------------------------------------------------- 1 | @props(['user', 'livewire']) 2 | 3 |
  • 4 |
    5 | {{ $element }} 6 | 7 |
    8 | Edit 9 |
    10 |
    11 | 12 | {{ $livewire }} 13 |
  • 14 | -------------------------------------------------------------------------------- /resources/views/components/savings/content.blade.php: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |
    5 | {{ $card_header }} 6 |
    7 | 8 |
    9 | {{ $card_body }} 10 |
    11 |
    12 |
    13 |
    -------------------------------------------------------------------------------- /resources/views/components/tables/entries-data.blade.php: -------------------------------------------------------------------------------- 1 | @props(['data']) 2 | 3 |
    4 | @if ($data->firstItem() !== null) 5 |
    6 | Showing {{ $data->firstItem() }} to {{ $data->lastItem() }} of {{ $data->total() }} entries 7 |
    8 | @endif 9 |
    -------------------------------------------------------------------------------- /resources/views/components/tables/pagination.blade.php: -------------------------------------------------------------------------------- 1 | @props(['data']) 2 | 3 |
    4 |
    5 | {{ $data->appends(request()->except(['_token']))->links() }} 6 |
    7 |
    8 | -------------------------------------------------------------------------------- /resources/views/components/tables/per-page.blade.php: -------------------------------------------------------------------------------- 1 |
    2 | 13 |
    14 | -------------------------------------------------------------------------------- /resources/views/components/tables/search.blade.php: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | 4 |
    5 |
    6 | -------------------------------------------------------------------------------- /resources/views/components/tables/sort-by.blade.php: -------------------------------------------------------------------------------- 1 | @props(['sortField', 'field', 'sortDirection']) 2 | 3 | @if ($sortField !== $field) 4 | 5 | @elseif ($sortDirection === 'asc') 6 | 7 | @else 8 | 9 | @endif 10 | -------------------------------------------------------------------------------- /resources/views/components/tables/table.blade.php: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | 4 | 5 | 6 | {{ $thead_tfoot }} 7 | 8 | 9 | 10 | 11 | {{ $tbody }} 12 | 13 | 14 | 15 | {{ $thead_tfoot }} 16 | 17 |
    18 |
    19 |
    -------------------------------------------------------------------------------- /resources/views/errors/401.blade.php: -------------------------------------------------------------------------------- 1 | @section('title') 2 | Unauthorized 3 | @endsection 4 | 5 | @extends('errors.app') 6 | 7 | @section('content') 8 |
    9 |
    10 |

    Unauthorized

    11 |

    12 | {{ $exception->getMessage() ?: 'You are not authorized.' }} 13 |

    14 |
    15 |
    16 | -------------------------------------------------------------------------------- /resources/views/errors/403.blade.php: -------------------------------------------------------------------------------- 1 | @section('title') 2 | Forbidden 3 | @endsection 4 | 5 | @extends('errors.app') 6 | 7 | @section('content') 8 |
    9 |
    10 |

    Forbidden

    11 |

    12 | {{ $exception->getMessage() ?: 'You are not allowed to see this page.' }} 13 |

    14 |
    15 |
    16 | 17 | @endsection 18 | -------------------------------------------------------------------------------- /resources/views/errors/404.blade.php: -------------------------------------------------------------------------------- 1 | @section('title') 2 | Not Found 3 | @endsection 4 | 5 | @extends('errors.app') 6 | 7 | @section('content') 8 |
    9 |
    10 |

    Oops! Page not found.

    11 |

    12 | We could not find the page you were looking for. 13 | Would you like to go to return to dashboard? 14 |

    15 |
    16 |
    17 | 18 | @endsection 19 | -------------------------------------------------------------------------------- /resources/views/errors/419.blade.php: -------------------------------------------------------------------------------- 1 | @section('title') 2 | Page Expired 3 | @endsection 4 | 5 | @extends('errors.app') 6 | 7 | @section('content') 8 |
    9 |
    10 |

    Page Expired

    11 |

    12 | {{ $exception->getMessage() ?: 'Please refresh the page.' }} 13 |

    14 |
    15 |
    16 | -------------------------------------------------------------------------------- /resources/views/errors/429.blade.php: -------------------------------------------------------------------------------- 1 | @section('title') 2 | Too many requests 3 | @endsection 4 | 5 | @extends('errors.app') 6 | 7 | @section('content') 8 |
    9 |
    10 |

    Too many requests

    11 |

    12 | {{ $exception->getMessage() ?: 'Too many requests.' }} 13 |

    14 |
    15 |
    16 | -------------------------------------------------------------------------------- /resources/views/errors/500.blade.php: -------------------------------------------------------------------------------- 1 | @section('title') 2 | Server Error 3 | @endsection 4 | 5 | @extends('errors.app') 6 | 7 | @section('content') 8 |
    9 |
    10 |

    Server Error

    11 |

    12 | {{ $exception->getMessage() ?: 'Something went wrong on our end.' }} 13 |

    14 |
    15 |
    16 | -------------------------------------------------------------------------------- /resources/views/errors/503.blade.php: -------------------------------------------------------------------------------- 1 | @section('title') 2 | Service Unavailable 3 | @endsection 4 | 5 | @extends('errors.app') 6 | 7 | @section('content') 8 |
    9 |
    10 |

    Service Unavailable

    11 |

    12 | {{ $exception->getMessage() ?: 'Service is not available. Please try again later.' }} 13 |

    14 |
    15 |
    16 | -------------------------------------------------------------------------------- /resources/views/errors/app.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | @include('layouts._head') 5 | 6 | 7 | 8 |
    9 | 12 | 13 | @yield('content') 14 |
    15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /resources/views/home.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('title') 4 | Home 5 | @endsection 6 | 7 | @section('content') 8 |
    9 |
    10 |

    Home

    11 |
    12 | 13 |
    14 | 15 |
    16 |
    17 | @endsection 18 | -------------------------------------------------------------------------------- /resources/views/layouts/_flash.blade.php: -------------------------------------------------------------------------------- 1 | @if (session('status')) 2 | 5 | @endif 6 | 7 | @if (session('flash')) 8 | 11 | @endif 12 | 27 | -------------------------------------------------------------------------------- /resources/views/layouts/_head.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ config('app.name', 'Laravel') }} | @yield('title', '') 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | @livewireStyles 13 | 14 | @yield('styles') 15 | 16 | -------------------------------------------------------------------------------- /resources/views/layouts/guest-app.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | @include('layouts._head') 5 | 6 | 7 | 8 |
    9 | 12 | 13 | @yield('content') 14 |
    15 | 16 | @livewireScripts 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /resources/views/livewire/profile/update-email.blade.php: -------------------------------------------------------------------------------- 1 |
    2 | @csrf 3 | 4 | 5 | 6 | 7 | 8 |
    9 |
    10 | 11 |
    12 | 13 |
    14 | 15 |
    16 |
    17 | 18 | -------------------------------------------------------------------------------- /resources/views/livewire/profile/update-image.blade.php: -------------------------------------------------------------------------------- 1 |
    2 | @csrf 3 | 4 | 5 | 6 |
    7 |
    8 | 9 |
    10 | 11 |
    12 | 13 |
    14 |
    15 | 16 | -------------------------------------------------------------------------------- /resources/views/livewire/profile/update-password.blade.php: -------------------------------------------------------------------------------- 1 |
    2 | @csrf 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
    11 |
    12 | 13 |
    14 | 15 |
    16 | 17 |
    18 |
    19 | 20 | -------------------------------------------------------------------------------- /resources/views/mails/invitation-mail.blade.php: -------------------------------------------------------------------------------- 1 | @component('mail::message') 2 | Hey, you have been invited to join. 3 | 4 | @component('mail::button', ['url' => $signedUrl]) 5 | Set up a new password and join 6 | @endcomponent 7 | 8 | Best Regards, {{ config('app.name') }} 9 | @endcomponent -------------------------------------------------------------------------------- /resources/views/mails/new-email-confirmation-mail.blade.php: -------------------------------------------------------------------------------- 1 | @component('mail::message') 2 | Hey, please confirm your new email by clicking the link below. 3 | 4 | @component('mail::button', ['url' => $signedUrl]) 5 | Confirm {{ $newEmail }} email address. 6 | @endcomponent 7 | 8 | Best Regards, {{ config('app.name') }} 9 | @endcomponent -------------------------------------------------------------------------------- /resources/views/mails/password-changed-mail.blade.php: -------------------------------------------------------------------------------- 1 | @component('mail::message') 2 | Hey, 3 |
    4 | Just want to let you know, that your password has been changed. 5 |
    6 |
    7 | If you haven't changed it please contact us. 8 | 9 | Thanks,
    10 | {{ config('app.name') }} 11 | @endcomponent 12 | -------------------------------------------------------------------------------- /resources/views/roles/delete.blade.php: -------------------------------------------------------------------------------- 1 |
    2 | @can('for-route', ['roles.delete', $this->role]) 3 | 9 | 10 | 11 | @endcan 12 |
    13 | -------------------------------------------------------------------------------- /resources/views/users/create.blade.php: -------------------------------------------------------------------------------- 1 |
    2 | @section('title') 3 | Create New User 4 | @endsection 5 | 6 | @section('content-header') 7 | 8 | Create New User 9 | 10 | @endsection 11 | 12 | 13 | 14 |

    Create New User

    15 | Back 16 |
    17 | 18 | 19 |
    20 | @csrf 21 | 22 | 23 | 24 | 25 | 26 |
    27 |
    28 | 29 |
    30 |
    31 | 32 | 33 |
    34 |
    35 | -------------------------------------------------------------------------------- /resources/views/users/delete.blade.php: -------------------------------------------------------------------------------- 1 |
    2 | @can('for-route', ['users.delete', $this->user]) 3 | @if(!$user->isHimself(auth()->user())) 4 | 10 | 11 | 12 | @endif 13 | @endcan 14 |
    15 | -------------------------------------------------------------------------------- /resources/views/users/edit.blade.php: -------------------------------------------------------------------------------- 1 | @section('title') 2 | Edit Existing User 3 | @endsection 4 | 5 | @section('content-header') 6 | 7 | Edit Existing User 8 | 9 | @endsection 10 | 11 | 12 | 13 |

    Edit Existing User

    14 | Back 15 |
    16 | 17 | 18 |
    19 | @csrf 20 | 21 | 27 | 28 | 34 | 35 |
    36 |
    37 | 38 |
    39 |
    40 | 41 | 42 |
    43 |
    44 | -------------------------------------------------------------------------------- /resources/webfonts/fa-brands-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/santa70916112/laravelbot/f5ecad87dcf240b0f7e7eab87420b98f9de76241/resources/webfonts/fa-brands-400.eot -------------------------------------------------------------------------------- /resources/webfonts/fa-brands-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/santa70916112/laravelbot/f5ecad87dcf240b0f7e7eab87420b98f9de76241/resources/webfonts/fa-brands-400.ttf -------------------------------------------------------------------------------- /resources/webfonts/fa-brands-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/santa70916112/laravelbot/f5ecad87dcf240b0f7e7eab87420b98f9de76241/resources/webfonts/fa-brands-400.woff -------------------------------------------------------------------------------- /resources/webfonts/fa-brands-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/santa70916112/laravelbot/f5ecad87dcf240b0f7e7eab87420b98f9de76241/resources/webfonts/fa-brands-400.woff2 -------------------------------------------------------------------------------- /resources/webfonts/fa-regular-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/santa70916112/laravelbot/f5ecad87dcf240b0f7e7eab87420b98f9de76241/resources/webfonts/fa-regular-400.eot -------------------------------------------------------------------------------- /resources/webfonts/fa-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/santa70916112/laravelbot/f5ecad87dcf240b0f7e7eab87420b98f9de76241/resources/webfonts/fa-regular-400.ttf -------------------------------------------------------------------------------- /resources/webfonts/fa-regular-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/santa70916112/laravelbot/f5ecad87dcf240b0f7e7eab87420b98f9de76241/resources/webfonts/fa-regular-400.woff -------------------------------------------------------------------------------- /resources/webfonts/fa-regular-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/santa70916112/laravelbot/f5ecad87dcf240b0f7e7eab87420b98f9de76241/resources/webfonts/fa-regular-400.woff2 -------------------------------------------------------------------------------- /resources/webfonts/fa-solid-900.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/santa70916112/laravelbot/f5ecad87dcf240b0f7e7eab87420b98f9de76241/resources/webfonts/fa-solid-900.eot -------------------------------------------------------------------------------- /resources/webfonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/santa70916112/laravelbot/f5ecad87dcf240b0f7e7eab87420b98f9de76241/resources/webfonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /resources/webfonts/fa-solid-900.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/santa70916112/laravelbot/f5ecad87dcf240b0f7e7eab87420b98f9de76241/resources/webfonts/fa-solid-900.woff -------------------------------------------------------------------------------- /resources/webfonts/fa-solid-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/santa70916112/laravelbot/f5ecad87dcf240b0f7e7eab87420b98f9de76241/resources/webfonts/fa-solid-900.woff2 -------------------------------------------------------------------------------- /routes/api.php: -------------------------------------------------------------------------------- 1 | get('/user', function (Request $request) { 17 | return $request->user(); 18 | }); 19 | -------------------------------------------------------------------------------- /routes/channels.php: -------------------------------------------------------------------------------- 1 | id === (int) $id; 16 | }); 17 | -------------------------------------------------------------------------------- /routes/console.php: -------------------------------------------------------------------------------- 1 | comment(Inspiring::quote()); 18 | })->describe('Display an inspiring quote'); 19 | -------------------------------------------------------------------------------- /server.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | 10 | $uri = urldecode( 11 | parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) 12 | ); 13 | 14 | // This file allows us to emulate Apache's "mod_rewrite" functionality from the 15 | // built-in PHP web server. This provides a convenient way to test a Laravel 16 | // application without having installed a "real" web server software here. 17 | if ($uri !== '/' && file_exists(__DIR__.'/public'.$uri)) { 18 | return false; 19 | } 20 | 21 | require_once __DIR__.'/public/index.php'; 22 | -------------------------------------------------------------------------------- /storage/app/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !public/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /storage/app/public/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/debugbar/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/.gitignore: -------------------------------------------------------------------------------- 1 | config.php 2 | routes.php 3 | schedule-* 4 | compiled.php 5 | services.json 6 | events.scanned.php 7 | routes.scanned.php 8 | down 9 | -------------------------------------------------------------------------------- /storage/framework/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !data/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /storage/framework/cache/data/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/sessions/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/testing/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/views/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/logs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /stubs/controller.api.stub: -------------------------------------------------------------------------------- 1 | extends('layouts.app'); 23 | } 24 | 25 | /** 26 | * Store new {{ dummyText }}. 27 | * 28 | * @return \Illuminate\Http\Response 29 | */ 30 | public function store() 31 | { 32 | $this->validate(); 33 | 34 | // create-review 35 | {{ DummyText }}::create([]); 36 | 37 | msg_success('{{ DummyText }} has been successfully created.'); 38 | 39 | return redirect()->route('{{ dummyTextPlu }}.index'); 40 | } 41 | 42 | /** 43 | * Validation rules. 44 | * 45 | * @return array 46 | */ 47 | protected function rules() 48 | { 49 | return [ 50 | // create-review 51 | ]; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /stubs/laravellte/create.test.stub: -------------------------------------------------------------------------------- 1 | admin = create_admin(); 25 | } 26 | 27 | /** @test */ 28 | public function assert_create_{{ dummyText }}_component_uses_livewire_auth_trait() 29 | { 30 | $this->assertContains(HasLivewireAuth::class, class_uses(Create{{ DummyText }}Component::class)); 31 | } 32 | 33 | /** @test */ 34 | public function render() 35 | { 36 | Livewire::actingAs($this->admin) 37 | ->test(Create{{ DummyText }}Component::class) 38 | ->assertSee('Save') 39 | ->assertStatus(Response::HTTP_OK); 40 | } 41 | 42 | /** @test */ 43 | public function store() 44 | { 45 | Livewire::actingAs($this->admin) 46 | ->test(Create{{ DummyText }}Component::class) 47 | // create-review-2 48 | ->call('store') 49 | ->assertRedirect('{{ dummyTextPlu }}'); 50 | 51 | $this->assertTrue(session()->has('flash')); 52 | 53 | // create-review 54 | ${{ dummyTextPlu }} = {{ DummyText }}::where('name', 'manager') 55 | ->where('label', 'Manager') 56 | ->get(); 57 | 58 | $this->assertCount(1, ${{ dummyTextPlu }}); 59 | } 60 | 61 | /** 62 | * @test 63 | * @dataProvider clientFormValidationProvider 64 | */ 65 | public function test_store_validation_rules($clientFormInput, $clientFormValue, $rule) 66 | { 67 | Livewire::actingAs($this->admin) 68 | ->test(Create{{ DummyText }}Component::class) 69 | ->set($clientFormInput, $clientFormValue) 70 | ->call('store') 71 | ->assertHasErrors([$clientFormInput => $rule]); 72 | } 73 | 74 | public function clientFormValidationProvider() 75 | { 76 | return [ 77 | // create-review 78 | ]; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /stubs/laravellte/create.view.stub: -------------------------------------------------------------------------------- 1 |
    2 | @section('title') 3 | Create New {{ DummyText }} 4 | @endsection 5 | 6 | @section('content-header') 7 | 8 | Create New {{ DummyText }} 9 | 10 | @endsection 11 | 12 | 13 | 14 |

    Create New {{ DummyText }}

    15 | Back 16 |
    17 | 18 | 19 |
    20 | @csrf 21 | 22 | // create-review 23 | 24 |
    25 |
    26 | 27 |
    28 |
    29 |
    30 | 31 |
    32 |
    33 | -------------------------------------------------------------------------------- /stubs/laravellte/delete.class.stub: -------------------------------------------------------------------------------- 1 | {{ dummyText }}->delete(); 29 | 30 | $this->emit('entity-deleted'); 31 | 32 | $this->dispatchFlashSuccessEvent('{{ DummyText }} has been successfully deleted.'); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /stubs/laravellte/delete.test.stub: -------------------------------------------------------------------------------- 1 | admin = create_admin(); 26 | } 27 | 28 | /** @test */ 29 | public function assert_delete_{{ dummyText }}_component_uses_livewire_auth_trait() 30 | { 31 | $this->assertContains(HasLivewireAuth::class, class_uses(Delete{{ DummyText }}Component::class)); 32 | } 33 | 34 | /** @test */ 35 | public function admin_can_delete_user() 36 | { 37 | // delete-review 38 | ${{ dummyText }} = {{ DummyText }}Factory::new()->create(); 39 | 40 | Livewire::actingAs($this->admin) 41 | ->test(Delete{{ DummyText }}Component::class, ['{{ dummyText }}' => ${{ dummyText }}]) 42 | ->call('destroy') 43 | ->assertEmitted('entity-deleted') 44 | ->assertDispatchedBrowserEvent('flash'); 45 | 46 | $this->assertNull({{ DummyText }}::find(${{ dummyText }}->id)); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /stubs/laravellte/delete.view.stub: -------------------------------------------------------------------------------- 1 |
    2 | @can('for-route', ['{{ dummyTextPlu }}.delete', $this->{{ dummyText }}]) 3 | 9 | 10 | 11 | @endcan 12 |
    13 | -------------------------------------------------------------------------------- /stubs/laravellte/edit.class.stub: -------------------------------------------------------------------------------- 1 | extends('layouts.app'); 23 | } 24 | 25 | /** 26 | * Update existing {{ dummyText }}. 27 | * 28 | * @return \Illuminate\Http\Response 29 | */ 30 | public function update() 31 | { 32 | $this->validate(); 33 | 34 | // edit-review 35 | $this->{{ dummyText }}->save(); 36 | 37 | msg_success('{{ DummyText }} has been successfully updated.'); 38 | 39 | return redirect()->route('{{ dummyTextPlu }}.index'); 40 | } 41 | 42 | /** 43 | * Validation rules. 44 | * 45 | * @return array 46 | */ 47 | protected function rules() 48 | { 49 | return [ 50 | // edit-review 51 | ]; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /stubs/laravellte/edit.view.stub: -------------------------------------------------------------------------------- 1 | @section('title') 2 | Edit Existing {{ DummyText }} 3 | @endsection 4 | 5 | @section('content-header') 6 | 7 | Edit Existing {{ DummyText }} 8 | 9 | @endsection 10 | 11 | 12 | 13 |

    Edit Existing {{ DummyText }}

    14 | Back 15 |
    16 | 17 | 18 |
    19 | @csrf 20 | 21 | // edit-review 22 | 23 |
    24 |
    25 | 26 |
    27 |
    28 |
    29 | 30 |
    31 |
    32 | -------------------------------------------------------------------------------- /stubs/laravellte/index.class.stub: -------------------------------------------------------------------------------- 1 | 'render']; 24 | 25 | /** 26 | * Render the component view. 27 | * 28 | * @return \Illuminate\View\View 29 | */ 30 | public function render() 31 | { 32 | // index-review-5 33 | ${{ dummyTextPlu }} = {{ DummyText }}::filter([ 34 | 'search' => $this->search, 35 | 'orderByField' => [$this->sortField, $this->sortDirection], 36 | ])->paginate($this->perPage); 37 | 38 | return view('{{ dummyText }}.index', ['{{ dummyTextPlu }}' => ${{ dummyTextPlu }}]) 39 | ->extends('layouts.app'); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /stubs/laravellte/index.filter.stub: -------------------------------------------------------------------------------- 1 | when($term, function ($query, $term) { 17 | $query->where(/**/, 'LIKE', "%$term%"); 18 | }); 19 | 20 | return $this; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /stubs/laravellte/show.class.stub: -------------------------------------------------------------------------------- 1 | extends('layouts.app'); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /stubs/laravellte/show.test.stub: -------------------------------------------------------------------------------- 1 | admin = create_admin(); 26 | } 27 | 28 | /** @test */ 29 | public function render() 30 | { 31 | ${{ dummyText }} = {{ DummyText }}Factory::new()->create(); 32 | 33 | Livewire::actingAs($this->admin) 34 | ->test(Show{{ DummyText }}Component::class, ['{{ dummyText }}' => ${{ dummyText }}]) 35 | // show-review 36 | ->assertStatus(Response::HTTP_OK); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /stubs/laravellte/show.view.stub: -------------------------------------------------------------------------------- 1 | @section('title') 2 | Show {{ DummyText }} 3 | @endsection 4 | 5 | @section('content-header') 6 | 7 | Show {{ DummyText }} 8 | // show-review 9 | 10 | @endsection 11 | -------------------------------------------------------------------------------- /stubs/migration.create.stub: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->timestamps(); 19 | }); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /stubs/migration.stub: -------------------------------------------------------------------------------- 1 | make(Kernel::class)->bootstrap(); 20 | 21 | Hash::setRounds(4); 22 | 23 | return $app; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/Feature/Http/Controllers/ConfirmedEmailControllerTest.php: -------------------------------------------------------------------------------- 1 | withoutExceptionHandling(); 22 | $this->expectException(HttpException::class); 23 | 24 | $this->get($this->temporarySignedUri(create_user(), 'new.email@example.com').'invalid'); 25 | } 26 | 27 | /** @test */ 28 | public function abort_if_user_does_not_exists() 29 | { 30 | $this->withoutExceptionHandling(); 31 | $this->expectException(HttpException::class); 32 | 33 | //invalid user's id 34 | $this->get($this->temporarySignedUri('100', 'new.email@example.com')); 35 | } 36 | 37 | /** @test */ 38 | public function confirm_user_email() 39 | { 40 | $user = create_user(); 41 | 42 | $response = $this->get($this->temporarySignedUri($user, 'new.email@example.com')); 43 | 44 | $response->assertStatus(Response::HTTP_FOUND) 45 | ->assertSessionHas('flash') 46 | ->assertRedirect(route('login')); 47 | 48 | $this->assertSame('new.email@example.com', $user->fresh()->email); 49 | } 50 | 51 | /** @test */ 52 | public function confirm_user_email_if_authenticated() 53 | { 54 | $user = create_user(); 55 | 56 | $response = $this->actingAs( 57 | UserFactory::new()->create(['email' => 'jane@example.com']) 58 | )->get($this->temporarySignedUri($user, 'new.email@example.com')); 59 | 60 | $response->assertStatus(Response::HTTP_FOUND) 61 | ->assertSessionHas('flash') 62 | ->assertRedirect(route('login')); 63 | 64 | $this->assertSame('new.email@example.com', $user->fresh()->email); 65 | } 66 | 67 | private function temporarySignedUri($user, $email) 68 | { 69 | return URL::temporarySignedRoute( 70 | 'confirmed-emails.store', 71 | Carbon::tomorrow(), 72 | ['user' => $user, 'new_email' => $email] 73 | ); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /tests/Feature/Http/Controllers/HomeControllerTest.php: -------------------------------------------------------------------------------- 1 | actingAs(UserFactory::new()->create()) 19 | ->get(route('home.index')); 20 | 21 | $response->assertStatus(Response::HTTP_OK) 22 | ->assertSee('Sign Out'); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/Feature/Http/Controllers/Profile/UserControllerTest.php: -------------------------------------------------------------------------------- 1 | actingAs($user = UserFactory::new()->create()) 19 | ->get(route('profile.users.index')); 20 | 21 | $response->assertStatus(Response::HTTP_OK) 22 | ->assertSee($user->email) 23 | ->assertSee('Password') 24 | ->assertSee('Image'); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Feature/Http/Livewire/DeleteRoleComponentTest.php: -------------------------------------------------------------------------------- 1 | admin = create_admin(); 26 | } 27 | 28 | /** @test */ 29 | public function assert_delete_role_component_uses_livewire_auth_trait() 30 | { 31 | $this->assertContains(HasLivewireAuth::class, class_uses(DeleteRoleComponent::class)); 32 | } 33 | 34 | /** @test */ 35 | public function admin_can_delete_role_with_attached_user() 36 | { 37 | $role = RoleFactory::new()->hasUsers()->create(); 38 | 39 | Livewire::actingAs($this->admin) 40 | ->test(DeleteRoleComponent::class, ['role' => $role]) 41 | ->call('destroy'); 42 | 43 | $this->assertNull(Role::find($role->id)); 44 | } 45 | 46 | /** @test */ 47 | public function admin_can_delete_role() 48 | { 49 | $role = RoleFactory::new()->create(); 50 | 51 | Livewire::actingAs($this->admin) 52 | ->test(DeleteRoleComponent::class, ['role' => $role]) 53 | ->call('destroy') 54 | ->assertEmitted('entity-deleted') 55 | ->assertDispatchedBrowserEvent('flash'); 56 | 57 | $this->assertNull(Role::find($role->id)); 58 | } 59 | 60 | /** @test */ 61 | public function admin_cannot_delete_admin_role() 62 | { 63 | $role = RoleFactory::new()->create([ 64 | 'name' => 'admin', 65 | ]); 66 | 67 | Livewire::actingAs($this->admin) 68 | ->test(DeleteRoleComponent::class, ['role' => $role]) 69 | ->call('destroy') 70 | ->assertDispatchedBrowserEvent('flash'); 71 | 72 | $this->count(1, Role::where('name', 'admin')->get()); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /tests/Feature/Http/Livewire/DeleteUserComponentTest.php: -------------------------------------------------------------------------------- 1 | admin = create_admin(); 26 | } 27 | 28 | /** @test */ 29 | public function assert_delete_user_component_uses_livewire_auth_trait() 30 | { 31 | $this->assertContains(HasLivewireAuth::class, class_uses(DeleteUserComponent::class)); 32 | } 33 | 34 | /** @test */ 35 | public function user_cannot_delete_himself() 36 | { 37 | Livewire::actingAs($this->admin) 38 | ->test(DeleteUserComponent::class, ['user' => $this->admin]) 39 | ->call('destroy') 40 | ->assertForbidden(); 41 | } 42 | 43 | /** @test */ 44 | public function admin_can_delete_user() 45 | { 46 | $user = UserFactory::new()->create(); 47 | 48 | Livewire::actingAs($this->admin) 49 | ->test(DeleteUserComponent::class, ['user' => $user]) 50 | ->call('destroy') 51 | ->assertEmitted('entity-deleted') 52 | ->assertDispatchedBrowserEvent('flash'); 53 | 54 | $this->assertNull(User::find($user->id)); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tests/Feature/Http/Livewire/TableTest.php: -------------------------------------------------------------------------------- 1 | sortBy('name'); 24 | 25 | $this->assertSame('name', $table->sortField); 26 | $this->assertSame('desc', $table->sortDirection); 27 | 28 | $table->sortBy('name'); 29 | 30 | $this->assertSame('name', $table->sortField); 31 | $this->assertSame('asc', $table->sortDirection); 32 | 33 | $table->sortBy('email'); 34 | 35 | $this->assertSame('email', $table->sortField); 36 | $this->assertSame('asc', $table->sortDirection); 37 | 38 | $table->sortBy('email'); 39 | 40 | $this->assertSame('email', $table->sortField); 41 | $this->assertSame('desc', $table->sortDirection); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | getByName($route)->gatherMiddleware()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/Unit/Filters/RoleFilterTest.php: -------------------------------------------------------------------------------- 1 | create([ 19 | 'name' => 'writer', 20 | ]); 21 | 22 | $manager = RoleFactory::new()->create([ 23 | 'name' => 'manager', 24 | ]); 25 | 26 | $result = Role::filter([ 27 | 'search' => 'manager', 28 | ])->get(); 29 | 30 | $this->assertCount(1, $result); 31 | 32 | $this->assertTrue($result->contains($manager)); 33 | } 34 | 35 | /** @test */ 36 | public function order_roles_by_field() 37 | { 38 | $writer = RoleFactory::new()->create([ 39 | 'name' => 'writer', 40 | ]); 41 | 42 | $manager = RoleFactory::new()->create([ 43 | 'name' => 'manager', 44 | ]); 45 | 46 | $result = Role::filter([ 47 | 'orderByField' => ['name', 'asc'], 48 | ])->get(); 49 | 50 | $this->assertTrue(collect([$manager->id, $writer->id]) == $result->pluck('id')); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /tests/Unit/Filters/UserFilterTest.php: -------------------------------------------------------------------------------- 1 | create([ 20 | 'email' => 'admin@lte.com', 21 | ]); 22 | 23 | UserFactory::new()->create([ 24 | 'email' => 'manager@lte.com', 25 | ]); 26 | 27 | $result = User::filter([ 28 | 'search' => 'admin', 29 | ])->get(); 30 | 31 | $this->assertCount(1, $result); 32 | 33 | $this->assertTrue($result->contains($admin)); 34 | } 35 | 36 | /** @test */ 37 | public function filter_users_by_role_id() 38 | { 39 | $user = UserFactory::new()->forRole([ 40 | 'name' => 'writer', 41 | ])->create([ 42 | 'email' => 'admin@lte.com', 43 | ]); 44 | 45 | UserFactory::new()->forRole([ 46 | 'name' => 'manager', 47 | ])->create([ 48 | 'email' => 'manager@lte.com', 49 | ]); 50 | 51 | $result = User::filter([ 52 | 'roleId' => Role::where('name', 'writer')->first()->id, 53 | ])->get(); 54 | 55 | $this->assertCount(1, $result); 56 | 57 | $this->assertTrue($result->contains($user)); 58 | } 59 | 60 | /** @test */ 61 | public function order_users_by_field() 62 | { 63 | $manager = UserFactory::new()->create([ 64 | 'email' => 'manager@lte.com', 65 | ]); 66 | 67 | $admin = UserFactory::new()->create([ 68 | 'email' => 'admin@lte.com', 69 | ]); 70 | 71 | $result = User::filter([ 72 | 'orderByField' => ['email', 'asc'], 73 | ])->get(); 74 | 75 | $this->assertTrue(collect([$admin->id, $manager->id]) == $result->pluck('id')); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /tests/Unit/Http/Livewire/HasLivewireAuthTest.php: -------------------------------------------------------------------------------- 1 | expectException(AuthenticationException::class); 24 | 25 | $customClass->hydrate(); 26 | } 27 | 28 | /** @test */ 29 | public function user_without_allowed_permission_cannot_access_component() 30 | { 31 | $this->withoutExceptionHandling(); 32 | 33 | $customClass = new class() { 34 | use HasLivewireAuth; 35 | }; 36 | 37 | $customClass->permissionName = 'users.index'; 38 | 39 | $this->expectException(AuthorizationException::class); 40 | 41 | $this->actingAs(create_user()); 42 | 43 | $customClass->hydrate(); 44 | } 45 | 46 | /** @test */ 47 | public function route_name_is_extracted_from_component_name() 48 | { 49 | $customClass = new class() { 50 | use HasLivewireAuth; 51 | 52 | public static function getName() 53 | { 54 | return 'index-user-component'; 55 | } 56 | }; 57 | 58 | $this->actingAs(create_admin()); 59 | 60 | $customClass->hydrate(); 61 | 62 | $this->assertSame('users.index', $customClass->permissionName); 63 | } 64 | 65 | /** @test */ 66 | public function set_model_method_is_called_if_exists() 67 | { 68 | $customClass = new class() { 69 | use HasLivewireAuth; 70 | 71 | /** @var bool */ 72 | public $called = false; 73 | 74 | protected function setModel() 75 | { 76 | $this->called = true; 77 | } 78 | 79 | protected function authorize($string, $array) 80 | { 81 | return true; 82 | } 83 | }; 84 | 85 | $customClass->permissionName = 'name'; 86 | 87 | $this->actingAs(create_user()); 88 | 89 | $customClass->hydrate(); 90 | 91 | $this->assertTrue($customClass->called); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /tests/Unit/Http/Middleware/AuthenticateTest.php: -------------------------------------------------------------------------------- 1 | assertContains('auth', $this->getMiddlewareFor($route)); 20 | } 21 | 22 | public function homeRoutesProvider() 23 | { 24 | return [ 25 | 'Route home.index doesn\'t have authenticate middleware.' => ['home.index'], 26 | ]; 27 | } 28 | 29 | public function usersRoutesProvider() 30 | { 31 | return [ 32 | 'Route users.index doesn\'t have authenticate middleware.' => ['users.index'], 33 | 'Route users.create doesn\'t have authenticate middleware.' => ['users.create'], 34 | 'Route users.edit doesn\'t have authenticate middleware.' => ['users.edit'], 35 | ]; 36 | } 37 | 38 | public function profileRoutesProvider() 39 | { 40 | return [ 41 | 'Route profile.users.index doesn\'t have authenticate middleware.' => ['profile.users.index'], 42 | ]; 43 | } 44 | 45 | public function rolesRoutesProvider() 46 | { 47 | return [ 48 | 'Route roles.index doesn\'t have authenticate middleware.' => ['roles.index'], 49 | 'Route roles.create doesn\'t have authenticate middleware.' => ['roles.create'], 50 | 'Route roles.edit doesn\'t have authenticate middleware.' => ['roles.edit'], 51 | ]; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tests/Unit/Listeners/ResizeImageTest.php: -------------------------------------------------------------------------------- 1 | putFileAs('', UploadedFile::fake()->image('abc123.jpg', 1000, 1000), 'abc123.jpg'); 23 | 24 | $event = new ProfileImageUploaded($user = UserFactory::new()->create(['image' => 'abc123.jpg'])); 25 | $listener = new ResizeImage(); 26 | $listener->handle($event); 27 | 28 | $imageProperties = getimagesize(config('filesystems.disks.avatar.root')."/{$user->image}"); 29 | 30 | $this->assertSame(100, $imageProperties[0]); 31 | $this->assertSame(100, $imageProperties[1]); 32 | 33 | Storage::disk('avatar')->delete("{$user->image}"); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/Unit/Mail/InvitationMailTest.php: -------------------------------------------------------------------------------- 1 | $user->id] 28 | ); 29 | 30 | $mail->assertSeeInHtml(htmlspecialchars($signedUrl)); 31 | } 32 | 33 | /** @test */ 34 | public function email_has_a_subject() 35 | { 36 | $mail = new InvitationMail(create_user(), Carbon::tomorrow()); 37 | $this->assertNotNull($mail->build()->subject); 38 | } 39 | 40 | /** @test */ 41 | public function email_has_a_sender() 42 | { 43 | $mail = new InvitationMail(create_user(), Carbon::tomorrow()); 44 | $this->assertTrue($mail->build()->hasFrom('no-replay@laravellte.com', 'laravellte')); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /tests/Unit/Mail/NewEmailConfirmationMailTest.php: -------------------------------------------------------------------------------- 1 | $user->id, 'new_email' => 'joe@example.com'] 28 | ); 29 | 30 | $mail->assertSeeInHtml(htmlspecialchars($signedUrl)); 31 | $mail->assertSeeInText('joe@example.com'); 32 | } 33 | 34 | /** @test */ 35 | public function email_has_a_subject() 36 | { 37 | $mail = new NewEmailConfirmationMail(create_user(), Carbon::tomorrow(), 'joe@example.com'); 38 | $this->assertNotNull($mail->build()->subject); 39 | } 40 | 41 | /** @test */ 42 | public function email_has_a_sender() 43 | { 44 | $mail = new NewEmailConfirmationMail(create_user(), Carbon::tomorrow(), 'joe@example.com'); 45 | $this->assertTrue($mail->build()->hasFrom('no-replay@laravellte.com', 'laravellte')); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tests/Unit/Mail/PasswordChangedMailTest.php: -------------------------------------------------------------------------------- 1 | assertSeeInText('your password has been changed'); 16 | } 17 | 18 | /** @test */ 19 | public function email_has_a_subject() 20 | { 21 | $mail = new PasswordChangedMail(); 22 | $this->assertEquals('Security notification regarding your password', $mail->build()->subject); 23 | } 24 | 25 | /** @test */ 26 | public function email_has_a_sender() 27 | { 28 | $mail = new PasswordChangedMail(); 29 | $this->assertTrue($mail->build()->hasFrom('no-replay@laravellte.com', 'laravellte')); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/Unit/Models/PermissionRoleTest.php: -------------------------------------------------------------------------------- 1 | assertSame('integer', $permissionRole->getCasts()['id']); 16 | } 17 | 18 | /** @test */ 19 | public function assert_owner_restricted_is_casted() 20 | { 21 | $permissionRole = new PermissionRole(); 22 | $this->assertSame('boolean', $permissionRole->getCasts()['owner_restricted']); 23 | } 24 | 25 | /** @test */ 26 | public function assert_role_id_is_casted() 27 | { 28 | $permissionRole = new PermissionRole(); 29 | $this->assertSame('integer', $permissionRole->getCasts()['role_id']); 30 | } 31 | 32 | /** @test */ 33 | public function assert_permission_id_is_casted() 34 | { 35 | $permissionRole = new PermissionRole(); 36 | $this->assertSame('integer', $permissionRole->getCasts()['permission_id']); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/Unit/Models/PermissionTest.php: -------------------------------------------------------------------------------- 1 | assertSame('integer', $permission->getCasts()['id']); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tests/Unit/Providers/AuthServiceProviderTest.php: -------------------------------------------------------------------------------- 1 | with('for-route', ForRouteGate::class) 21 | ->once(); 22 | 23 | $authServiceProvider = new AuthServiceProvider(app()); 24 | 25 | $authServiceProvider->boot(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/Unit/Providers/EventServiceProviderTest.php: -------------------------------------------------------------------------------- 1 | assertListening(ProfileImageUploaded::class, ResizeImage::class); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/Unit/Rules/OwnerRestrictedRuleTest.php: -------------------------------------------------------------------------------- 1 | [ 16 | 'allowed' => true, 17 | ], 18 | ]; 19 | 20 | $rule = new OwnerRestrictedRule($permissions); 21 | 22 | $this->assertTrue($rule->passes('permissions.1.owner_restricted', true)); 23 | $this->assertTrue($rule->passes('permissions.1.owner_restricted', false)); 24 | 25 | $permissions = [ 26 | 1 => [ 27 | 'allowed' => false, 28 | ], 29 | ]; 30 | 31 | $rule = new OwnerRestrictedRule($permissions); 32 | 33 | $this->assertTrue($rule->passes('permissions.1.owner_restricted', false)); 34 | } 35 | 36 | /** @test */ 37 | public function owner_restricted_param_cannot_be_selected_if_permission_is_not_selected() 38 | { 39 | $permissions = [ 40 | 1 => [ 41 | 'allowed' => false, 42 | ], 43 | ]; 44 | 45 | $rule = new OwnerRestrictedRule($permissions); 46 | 47 | $this->assertFalse($rule->passes('permissions.1.owner_restricted', true)); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tests/Unit/Rules/PasswordCheckRuleTest.php: -------------------------------------------------------------------------------- 1 | bcrypt('password')])); 16 | 17 | $this->assertTrue($rule->passes('password', 'password')); 18 | $this->assertFalse($rule->passes('password', 'invalid-password')); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/Unit/Rules/PasswordRuleTest.php: -------------------------------------------------------------------------------- 1 | password = password_generator(); 18 | } 19 | 20 | /** @test */ 21 | public function validation_passes() 22 | { 23 | $rule = new PasswordRule($this->password); 24 | 25 | $this->assertTrue($rule->passes('password', $this->password)); 26 | } 27 | 28 | /** @test */ 29 | public function password_is_required() 30 | { 31 | $rule = new PasswordRule(''); 32 | 33 | $this->assertFalse($rule->passes('password', '')); 34 | } 35 | 36 | /** @test */ 37 | public function password_must_be_at_least_x_char_in_length() 38 | { 39 | $password = too_short_password(); 40 | $rule = new PasswordRule($password); 41 | 42 | $this->assertFalse($rule->passes('password', $password)); 43 | } 44 | 45 | /** @test */ 46 | public function password_must_be_confirmed() 47 | { 48 | $rule = new PasswordRule($this->password.'-not-confirmed'); 49 | 50 | $this->assertFalse($rule->passes('password', $this->password)); 51 | } 52 | 53 | /** @test */ 54 | public function password_must_be_uncompromised() 55 | { 56 | $rule = new PasswordRule('password'); 57 | 58 | $this->assertFalse($rule->passes('password', 'password')); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /tests/Unit/Rules/PermissionExistsRuleTest.php: -------------------------------------------------------------------------------- 1 | create(); 19 | 20 | $rule = new PermissionExistsRule(); 21 | 22 | $this->assertTrue($rule->passes('permissions.1.owner_restricted', '1')); 23 | $this->assertTrue($rule->passes('permissions.1.owner_restricted', '5')); 24 | } 25 | 26 | /** @test */ 27 | public function fails_if_permission_does_not_exist() 28 | { 29 | PermissionFactory::new()->create(); 30 | 31 | $rule = new PermissionExistsRule(); 32 | 33 | $this->assertFalse($rule->passes('permissions.2.owner_restricted', '1')); 34 | $this->assertFalse($rule->passes('permissions.2.owner_restricted', '2')); 35 | $this->assertFalse($rule->passes('permissions.2.owner_restricted', '5')); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/helpers.php: -------------------------------------------------------------------------------- 1 | forRole([ 15 | 'name' => 'admin', 16 | ])->create([ 17 | 'email' => 'admin@admin.lte', 18 | ]); 19 | } 20 | 21 | /** 22 | * User seeder. 23 | * 24 | * @param $email 25 | * @return \App\Models\User 26 | */ 27 | function create_user($email = null) 28 | { 29 | return UserFactory::new()->forRole([ 30 | 'name' => 'manager', 31 | ])->create([ 32 | 'email' => $email ?? 'manager@admin.lte', 33 | ]); 34 | } 35 | 36 | /** 37 | * Random password generator. 38 | * 39 | * @return \App\Models\User 40 | */ 41 | function password_generator($length = 16) 42 | { 43 | return Str::random($length); 44 | } 45 | 46 | /** 47 | * Too short password random password generator. 48 | * 49 | * @return \App\Models\User 50 | */ 51 | function too_short_password() 52 | { 53 | return Str::random(AppServiceProvider::MIN_PASSWORD_LENGTH - 1); 54 | } 55 | -------------------------------------------------------------------------------- /webpack.mix.js: -------------------------------------------------------------------------------- 1 | const mix = require("laravel-mix"); 2 | 3 | mix.copy("resources/webfonts", "./public/webfonts", false) 4 | .copy("resources/images", "./public/images", false) 5 | .styles( 6 | ["resources/css/font-awesome.css", "resources/css/adminlte.css", "resources/css/icheck-bootstrap.css", "resources/css/custom.css", "resources/css/google-font.css"], 7 | "public/css/app.css" 8 | ) 9 | .js(["resources/js/app.js"], "public/js"); 10 | --------------------------------------------------------------------------------