├── .styleci.yml
├── LICENSE
├── README.md
├── UPGRADE.md
├── composer.json
├── database
├── factories
│ ├── CompanyFactory.php
│ └── UserFactory.php
├── migrations
│ ├── 0001_01_01_000000_create_users_table.php
│ ├── 2020_05_21_100000_create_companies_table.php
│ ├── 2020_05_21_200000_create_company_user_table.php
│ ├── 2020_05_21_300000_create_company_invitations_table.php
│ └── 2020_12_22_000000_create_connected_accounts_table.php
└── seeders
│ └── DatabaseSeeder.php
├── lang
├── ar
│ └── default.php
├── cs
│ └── default.php
├── de
│ └── default.php
├── en
│ └── default.php
├── es
│ └── default.php
├── fr
│ └── default.php
├── id
│ └── default.php
├── ja
│ └── default.php
├── ko
│ └── default.php
├── nl
│ └── default.php
├── pt_BR
│ └── default.php
└── pt_PT
│ └── default.php
├── pint.json
├── resources
└── views
│ ├── auth
│ ├── login.blade.php
│ ├── policy.blade.php
│ ├── register.blade.php
│ └── terms.blade.php
│ ├── companies
│ ├── company-employee-manager.blade.php
│ ├── delete-company-form.blade.php
│ └── update-company-name-form.blade.php
│ ├── components
│ ├── connected-account.blade.php
│ ├── grid-section.blade.php
│ ├── input-error.blade.php
│ ├── input.blade.php
│ ├── section-border.blade.php
│ ├── socialite-icons
│ │ ├── bitbucket.blade.php
│ │ ├── facebook.blade.php
│ │ ├── github.blade.php
│ │ ├── gitlab.blade.php
│ │ ├── google.blade.php
│ │ ├── linkedin.blade.php
│ │ ├── slack.blade.php
│ │ └── twitter.blade.php
│ └── socialite.blade.php
│ ├── filament
│ └── pages
│ │ ├── companies
│ │ ├── company_settings.blade.php
│ │ └── create_company.blade.php
│ │ └── user
│ │ ├── personal-access-tokens.blade.php
│ │ └── profile.blade.php
│ ├── mail
│ └── company-invitation.blade.php
│ └── profile
│ ├── connected-accounts-form.blade.php
│ ├── delete-user-form.blade.php
│ ├── logout-other-browser-sessions-form.blade.php
│ ├── set-password-form.blade.php
│ ├── update-password-form.blade.php
│ └── update-profile-information-form.blade.php
├── src
├── Actions
│ ├── GenerateRedirectForProvider.php
│ ├── UpdateCompanyEmployeeRole.php
│ └── ValidateCompanyDeletion.php
├── Company.php
├── Concerns
│ ├── Base
│ │ ├── HasAddedProfileComponents.php
│ │ ├── HasAutoAcceptInvitations.php
│ │ ├── HasBaseActionBindings.php
│ │ ├── HasBaseModels.php
│ │ ├── HasBaseProfileComponents.php
│ │ ├── HasBaseProfileFeatures.php
│ │ ├── HasCompanyFeatures.php
│ │ ├── HasModals.php
│ │ ├── HasNotifications.php
│ │ ├── HasPanels.php
│ │ ├── HasPermissions.php
│ │ ├── HasRoutes.php
│ │ └── HasTermsAndPrivacyPolicy.php
│ ├── ManagesProfileComponents.php
│ └── Socialite
│ │ ├── CanEnableSocialite.php
│ │ ├── HasConnectedAccountModel.php
│ │ ├── HasProviderFeatures.php
│ │ ├── HasProviders.php
│ │ ├── HasSocialiteActionBindings.php
│ │ ├── HasSocialiteComponents.php
│ │ └── HasSocialiteProfileFeatures.php
├── ConnectedAccount.php
├── Console
│ └── InstallCommand.php
├── Contracts
│ ├── AddsCompanyEmployees.php
│ ├── CreatesCompanies.php
│ ├── CreatesConnectedAccounts.php
│ ├── CreatesNewUsers.php
│ ├── CreatesUserFromProvider.php
│ ├── Credentials.php
│ ├── DeletesCompanies.php
│ ├── DeletesUsers.php
│ ├── GeneratesProviderRedirect.php
│ ├── HandlesInvalidState.php
│ ├── InvitesCompanyEmployees.php
│ ├── RemovesCompanyEmployees.php
│ ├── ResolvesSocialiteUsers.php
│ ├── SetsUserPasswords.php
│ ├── UpdatesCompanyNames.php
│ ├── UpdatesConnectedAccounts.php
│ ├── UpdatesUserPasswords.php
│ └── UpdatesUserProfileInformation.php
├── Credentials.php
├── Employeeship.php
├── Enums
│ ├── Feature.php
│ └── Provider.php
├── Events
│ ├── AddingCompany.php
│ ├── AddingCompanyEmployee.php
│ ├── CompanyCreated.php
│ ├── CompanyDeleted.php
│ ├── CompanyEmployeeAdded.php
│ ├── CompanyEmployeeRemoved.php
│ ├── CompanyEmployeeUpdated.php
│ ├── CompanyEvent.php
│ ├── CompanyUpdated.php
│ ├── ConnectedAccountCreated.php
│ ├── ConnectedAccountDeleted.php
│ ├── ConnectedAccountEvent.php
│ ├── ConnectedAccountUpdated.php
│ ├── InvitingCompanyEmployee.php
│ └── RemovingCompanyEmployee.php
├── FilamentCompanies.php
├── FilamentCompaniesServiceProvider.php
├── HasCompanies.php
├── HasConnectedAccounts.php
├── HasProfilePhoto.php
├── Http
│ ├── Controllers
│ │ ├── CompanyInvitationController.php
│ │ └── OAuthController.php
│ ├── Livewire
│ │ ├── CompanyEmployeeManager.php
│ │ ├── ConnectedAccountsForm.php
│ │ ├── CreateCompanyForm.php
│ │ ├── DeleteCompanyForm.php
│ │ ├── DeleteUserForm.php
│ │ ├── LogoutOtherBrowserSessionsForm.php
│ │ ├── SetPasswordForm.php
│ │ ├── UpdateCompanyNameForm.php
│ │ ├── UpdatePasswordForm.php
│ │ └── UpdateProfileInformationForm.php
│ └── Responses
│ │ └── Auth
│ │ └── FilamentCompaniesRegistrationResponse.php
├── Listeners
│ └── SwitchCurrentCompany.php
├── Mail
│ └── CompanyInvitation.php
├── OwnerRole.php
├── Pages
│ ├── Auth
│ │ ├── Login.php
│ │ ├── PrivacyPolicy.php
│ │ ├── Register.php
│ │ └── Terms.php
│ ├── Company
│ │ ├── CompanySettings.php
│ │ └── CreateCompany.php
│ └── User
│ │ ├── PersonalAccessTokens.php
│ │ └── Profile.php
├── RedirectsActions.php
├── Role.php
├── Rules
│ └── Role.php
└── SetsProfilePhotoFromUrl.php
└── stubs
├── app
├── Actions
│ └── FilamentCompanies
│ │ ├── AddCompanyEmployee.php
│ │ ├── CreateCompany.php
│ │ ├── CreateConnectedAccount.php
│ │ ├── CreateNewUser.php
│ │ ├── CreateUserFromProvider.php
│ │ ├── DeleteCompany.php
│ │ ├── DeleteUser.php
│ │ ├── DeleteUserWithSocialite.php
│ │ ├── HandleInvalidState.php
│ │ ├── InviteCompanyEmployee.php
│ │ ├── RemoveCompanyEmployee.php
│ │ ├── ResolveSocialiteUser.php
│ │ ├── SetUserPassword.php
│ │ ├── UpdateCompanyName.php
│ │ ├── UpdateConnectedAccount.php
│ │ ├── UpdateUserPassword.php
│ │ └── UpdateUserProfileInformation.php
├── Models
│ ├── Company.php
│ ├── CompanyInvitation.php
│ ├── ConnectedAccount.php
│ ├── Employeeship.php
│ ├── User.php
│ └── UserWithSocialite.php
├── Policies
│ ├── CompanyPolicy.php
│ └── ConnectedAccountPolicy.php
└── Providers
│ ├── FilamentCompaniesServiceProvider.php
│ └── FilamentCompaniesWithSocialiteServiceProvider.php
└── resources
└── markdown
├── policy.md
└── terms.md
/.styleci.yml:
--------------------------------------------------------------------------------
1 | php:
2 | preset: laravel
3 | disabled:
4 | - no_unused_imports
5 | js:
6 | finder:
7 | not-name:
8 | - vite.config.js
9 | css: true
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 andrewdwallo
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/UPGRADE.md:
--------------------------------------------------------------------------------
1 | # Upgrade Guide
2 |
3 | ## Upgrading from FilamentCompanies 3.x to 4.x
4 |
5 | This major release introduces significant changes designed to streamline the usage of FilamentCompanies. Here’s how to migrate your project from 3.x to 4.x.
6 |
7 | ### Breaking Changes
8 |
9 | 1. Removal of Classes:
10 | - `Wallo\FilamentCompanies\Features`
11 | - `Wallo\FilamentCompanies\Providers`
12 | - `Wallo\FilamentCompanies\Socialite`
13 |
14 | 2. Introduction of Enums:
15 | - Functionality previously available through the `Providers` class has been replaced by the `Wallo\FilamentCompanies\Enums\Provider` enum.
16 | - Some functionality previously available through the `Socialite` class has been replaced by the `Wallo\FilamentCompanies\Enums\Feature` enum.
17 |
18 | 3. Removal of the `MakeUserCommand` command.
19 | - It isn't necessary and was removed to simplify the package.
20 |
21 | ### Migration Steps
22 |
23 | #### Dependency Versions
24 |
25 | You should first make sure you follow the [Laravel 11 Upgrade Guide](https://laravel.com/docs/11.x/upgrade) and then upgrade your `andrewdwallo/filament-companies` dependency to `^4.0` within your application's `composer.json` file. Then, run the `composer update` command:
26 |
27 | composer update
28 |
29 | #### Socialite Providers
30 |
31 | For Socialite providers, replace the usage of the `Wallo\FilamentCompanies\Providers` class with the `Wallo\FilamentCompanies\Enums\Provider` enum.
32 |
33 | Before:
34 |
35 | ```php
36 |
37 | use Wallo\FilamentCompanies\Providers;
38 |
39 | // Enable the following Socialite providers.
40 | Providers::github(),
41 | // And so on for the other providers...
42 |
43 | // Determine if the following Socialite providers are enabled.
44 | Providers::hasGithub(),
45 | // And so on for the other providers...
46 | ```
47 |
48 | After:
49 |
50 | ```php
51 |
52 | use Wallo\FilamentCompanies\Enums\Provider;
53 |
54 | // Enable the following Socialite providers.
55 | Provider::Github,
56 | // And so on for the other providers...
57 |
58 | // Determine if the following Socialite providers are enabled.
59 | Provider::Github->isEnabled(),
60 | // And so on for the other providers...
61 |
62 | ```
63 |
64 | #### Socialite Features
65 |
66 | For Socialite features, replace the usage of the `Wallo\FilamentCompanies\Socialite` class with the `Wallo\FilamentCompanies\Enums\Feature` enum.
67 |
68 | Before:
69 |
70 | ```php
71 |
72 | use Wallo\FilamentCompanies\Socialite;
73 |
74 | // Enable the following features.
75 | Socialite::rememberSession(),
76 | // And so on for the other features...
77 |
78 | // Determine if the following features are enabled.
79 | Socialite::hasRememberSessionFeature(),
80 | // And so on for the other features...
81 |
82 | ```
83 |
84 | After:
85 |
86 | ```php
87 |
88 | use Wallo\FilamentCompanies\Enums\Feature;
89 |
90 | // Enable the following features.
91 | Feature::RememberSession,
92 | // And so on for the other features...
93 |
94 | // Determine if the following features are enabled.
95 | Feature::RememberSession->isEnabled(),
96 | // And so on for the other features...
97 | ```
98 |
99 | ### Important Notes
100 | > The rest of the methods previously available in the `Socialite` and `Features` classes are still available and were moved to the main `Wallo\FilamentCompanies\FilamentCompanies` class.
101 |
102 | ### Further Assistance
103 | Should you encounter any issues during the upgrade process, please don’t hesitate to reach out Discord or by creating a new Discussion on GitHub.
104 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "andrewdwallo/filament-companies",
3 | "description": "A comprehensive Laravel authentication and authorization system designed for Filament, focusing on multi-tenant company management.",
4 | "keywords": [
5 | "andrewdwallo",
6 | "filament-companies",
7 | "filament companies",
8 | "laravel",
9 | "socialite",
10 | "authentication",
11 | "filament",
12 | "multi-tenant",
13 | "company management",
14 | "erp",
15 | "saas",
16 | "crm",
17 | "business management"
18 | ],
19 | "homepage": "https://github.com/andrewdwallo/filament-companies",
20 | "license": "MIT",
21 | "authors": [
22 | {
23 | "name": "Andrew Wallo",
24 | "email": "andrewdwallo@gmail.com",
25 | "role": "Developer"
26 | }
27 | ],
28 | "require": {
29 | "php": "^8.2",
30 | "ext-json": "*",
31 | "filament/filament": "^3.2.29",
32 | "illuminate/console": "^11.0|^12.0",
33 | "illuminate/contracts": "^11.0|^12.0",
34 | "illuminate/support": "^11.0|^12.0",
35 | "laravel/socialite": "^5.12",
36 | "matomo/device-detector": "^6.1"
37 | },
38 | "require-dev": {
39 | "laravel/pint": "^1.14",
40 | "laravel/sanctum": "^4.0",
41 | "livewire/livewire": "^3.4.9",
42 | "mockery/mockery": "^1.6",
43 | "orchestra/testbench": "^9.0|^10.0",
44 | "phpunit/phpunit": "^10.5|^11.5.3"
45 | },
46 | "autoload": {
47 | "psr-4": {
48 | "Wallo\\FilamentCompanies\\": "src/"
49 | }
50 | },
51 | "autoload-dev": {
52 | "psr-4": {
53 | "App\\": "stubs/app/",
54 | "Database\\Factories\\": "database/factories/"
55 | }
56 | },
57 | "extra": {
58 | "laravel": {
59 | "providers": [
60 | "Wallo\\FilamentCompanies\\FilamentCompaniesServiceProvider"
61 | ]
62 | }
63 | },
64 | "config": {
65 | "sort-packages": true
66 | },
67 | "minimum-stability": "dev",
68 | "prefer-stable": true
69 | }
70 |
--------------------------------------------------------------------------------
/database/factories/CompanyFactory.php:
--------------------------------------------------------------------------------
1 |
22 | */
23 | public function definition(): array
24 | {
25 | return [
26 | 'name' => $this->faker->unique()->company(),
27 | 'user_id' => User::factory(),
28 | 'personal_company' => true,
29 | ];
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/database/factories/UserFactory.php:
--------------------------------------------------------------------------------
1 |
30 | */
31 | public function definition(): array
32 | {
33 | return [
34 | 'name' => fake()->name(),
35 | 'email' => fake()->unique()->safeEmail(),
36 | 'email_verified_at' => now(),
37 | 'password' => static::$password ??= Hash::make('password'),
38 | 'remember_token' => Str::random(10),
39 | 'profile_photo_path' => null,
40 | 'current_company_id' => null,
41 | ];
42 | }
43 |
44 | /**
45 | * Indicate that the model's email address should be unverified.
46 | */
47 | public function unverified(): static
48 | {
49 | return $this->state(fn (array $attributes) => [
50 | 'email_verified_at' => null,
51 | ]);
52 | }
53 |
54 | /**
55 | * Indicate that the user should have a personal company.
56 | */
57 | public function withPersonalCompany(?callable $callback = null): static
58 | {
59 | if (! FilamentCompanies::hasCompanyFeatures()) {
60 | return $this->state([]);
61 | }
62 |
63 | return $this->has(
64 | Company::factory()
65 | ->state(fn (array $attributes, User $user) => [
66 | 'name' => $user->name . '\'s Company',
67 | 'user_id' => $user->id,
68 | 'personal_company' => true,
69 | ])
70 | ->when(is_callable($callback), $callback),
71 | 'ownedCompanies'
72 | );
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/database/migrations/0001_01_01_000000_create_users_table.php:
--------------------------------------------------------------------------------
1 | id();
17 | $table->string('name');
18 | $table->string('email')->unique();
19 | $table->timestamp('email_verified_at')->nullable();
20 | $table->string('password')->nullable(
21 | FilamentCompanies::hasSocialiteFeatures()
22 | );
23 | $table->rememberToken();
24 | $table->foreignId('current_company_id')->nullable();
25 | $table->foreignId('current_connected_account_id')->nullable();
26 | $table->string('profile_photo_path', 2048)->nullable();
27 | $table->timestamps();
28 | });
29 |
30 | Schema::create('password_reset_tokens', function (Blueprint $table) {
31 | $table->string('email')->primary();
32 | $table->string('token');
33 | $table->timestamp('created_at')->nullable();
34 | });
35 |
36 | Schema::create('sessions', function (Blueprint $table) {
37 | $table->string('id')->primary();
38 | $table->foreignId('user_id')->nullable()->index();
39 | $table->string('ip_address', 45)->nullable();
40 | $table->text('user_agent')->nullable();
41 | $table->longText('payload');
42 | $table->integer('last_activity')->index();
43 | });
44 | }
45 |
46 | /**
47 | * Reverse the migrations.
48 | */
49 | public function down(): void
50 | {
51 | Schema::dropIfExists('users');
52 | Schema::dropIfExists('password_reset_tokens');
53 | Schema::dropIfExists('sessions');
54 | }
55 | };
56 |
--------------------------------------------------------------------------------
/database/migrations/2020_05_21_100000_create_companies_table.php:
--------------------------------------------------------------------------------
1 | id();
16 | $table->foreignId('user_id')->index();
17 | $table->string('name');
18 | $table->boolean('personal_company');
19 | $table->timestamps();
20 | });
21 | }
22 |
23 | /**
24 | * Reverse the migrations.
25 | */
26 | public function down(): void
27 | {
28 | Schema::dropIfExists('companies');
29 | }
30 | };
31 |
--------------------------------------------------------------------------------
/database/migrations/2020_05_21_200000_create_company_user_table.php:
--------------------------------------------------------------------------------
1 | id();
16 | $table->foreignId('company_id');
17 | $table->foreignId('user_id');
18 | $table->string('role')->nullable();
19 | $table->timestamps();
20 |
21 | $table->unique(['company_id', 'user_id']);
22 | });
23 | }
24 |
25 | /**
26 | * Reverse the migrations.
27 | */
28 | public function down(): void
29 | {
30 | Schema::dropIfExists('company_user');
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/database/migrations/2020_05_21_300000_create_company_invitations_table.php:
--------------------------------------------------------------------------------
1 | id();
16 | $table->foreignId('company_id')->constrained()->cascadeOnDelete();
17 | $table->string('email');
18 | $table->string('role')->nullable();
19 | $table->timestamps();
20 |
21 | $table->unique(['company_id', 'email']);
22 | });
23 | }
24 |
25 | /**
26 | * Reverse the migrations.
27 | */
28 | public function down(): void
29 | {
30 | Schema::dropIfExists('company_invitations');
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/database/migrations/2020_12_22_000000_create_connected_accounts_table.php:
--------------------------------------------------------------------------------
1 | id();
16 | $table->foreignId('user_id');
17 | $table->string('provider');
18 | $table->string('provider_id');
19 | $table->string('name')->nullable();
20 | $table->string('nickname')->nullable();
21 | $table->string('email')->nullable();
22 | $table->string('telephone')->nullable();
23 | $table->text('avatar_path')->nullable();
24 | $table->string('token', 1000);
25 | $table->string('secret')->nullable(); // OAuth1
26 | $table->string('refresh_token', 1000)->nullable(); // OAuth2
27 | $table->dateTime('expires_at')->nullable(); // OAuth2
28 | $table->timestamps();
29 |
30 | $table->index(['user_id', 'id']);
31 | $table->index(['provider', 'provider_id']);
32 | });
33 | }
34 |
35 | /**
36 | * Reverse the migrations.
37 | */
38 | public function down(): void
39 | {
40 | Schema::dropIfExists('connected_accounts');
41 | }
42 | };
43 |
--------------------------------------------------------------------------------
/database/seeders/DatabaseSeeder.php:
--------------------------------------------------------------------------------
1 | withPersonalCompany()->create();
17 |
18 | User::factory()->withPersonalCompany()->create([
19 | 'name' => 'Test User',
20 | 'email' => 'test@example.com',
21 | ]);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/pint.json:
--------------------------------------------------------------------------------
1 | {
2 | "preset": "laravel",
3 | "rules": {
4 | "blank_line_before_statement": true,
5 | "concat_space": {
6 | "spacing": "one"
7 | },
8 | "method_argument_space": true,
9 | "nullable_type_declaration_for_default_null_value": {
10 | "use_nullable_type_declaration": true
11 | },
12 | "single_trait_insert_per_statement": true,
13 | "types_spaces": {
14 | "space": "single"
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/resources/views/auth/login.blade.php:
--------------------------------------------------------------------------------
1 |
2 | @if (filament()->hasRegistration())
3 |
4 | {{ __('filament-panels::pages/auth/login.actions.register.before') }}
5 |
6 | {{ $this->registerAction }}
7 |
8 | @endif
9 |
10 | {{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::AUTH_LOGIN_FORM_BEFORE, scopes: $this->getRenderHookScopes()) }}
11 |
12 |
13 | {{ $this->form }}
14 |
15 |
19 |
20 |
21 | @if (Wallo\FilamentCompanies\FilamentCompanies::hasSocialiteFeatures())
22 |
23 | @endif
24 |
25 | {{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::AUTH_LOGIN_FORM_AFTER, scopes: $this->getRenderHookScopes()) }}
26 |
27 |
--------------------------------------------------------------------------------
/resources/views/auth/policy.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 | {!! $policy !!}
4 |
5 |
6 |
--------------------------------------------------------------------------------
/resources/views/auth/register.blade.php:
--------------------------------------------------------------------------------
1 |
2 | @if (filament()->hasLogin())
3 |
4 | {{ __('filament-panels::pages/auth/register.actions.login.before') }}
5 |
6 | {{ $this->loginAction }}
7 |
8 | @endif
9 |
10 | {{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::AUTH_REGISTER_FORM_BEFORE, scopes: $this->getRenderHookScopes()) }}
11 |
12 |
13 | {{ $this->form }}
14 |
15 |
19 |
20 |
21 | {{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::AUTH_REGISTER_FORM_AFTER, scopes: $this->getRenderHookScopes()) }}
22 |
23 | @if (\Wallo\FilamentCompanies\FilamentCompanies::hasSocialiteFeatures())
24 |
25 | @endif
26 |
27 |
--------------------------------------------------------------------------------
/resources/views/auth/terms.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 | {!! $terms !!}
4 |
5 |
6 |
--------------------------------------------------------------------------------
/resources/views/companies/delete-company-form.blade.php:
--------------------------------------------------------------------------------
1 | @php
2 | $modals = \Wallo\FilamentCompanies\FilamentCompanies::getModals();
3 | @endphp
4 |
5 |
6 |
7 | {{ __('filament-companies::default.action_section_titles.delete_company') }}
8 |
9 |
10 |
11 | {{ __('filament-companies::default.action_section_descriptions.delete_company') }}
12 |
13 |
14 |
15 |
16 |
17 | {{ __('filament-companies::default.subheadings.companies.delete_company') }}
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | {{ __('filament-companies::default.buttons.delete_company') }}
26 |
27 |
28 |
29 |
30 |
31 | {{ __('filament-companies::default.modal_titles.delete_company') }}
32 |
33 |
34 |
35 | {{ __('filament-companies::default.modal_descriptions.delete_company') }}
36 |
37 |
38 |
39 | @if($modals['cancelButtonAction'])
40 |
41 | {{ __('filament-companies::default.buttons.cancel') }}
42 |
43 | @endif
44 |
45 |
46 | {{ __('filament-companies::default.buttons.delete_company') }}
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/resources/views/companies/update-company-name-form.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ __('filament-companies::default.grid_section_titles.company_name') }}
4 |
5 |
6 |
7 | {{ __('filament-companies::default.grid_section_descriptions.company_name') }}
8 |
9 |
10 |
11 |
12 |
13 |
14 | {{ __('filament-companies::default.labels.company_owner') }}
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
{{ $company->owner->name }}
23 |
{{ $company->owner->email }}
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | @if (Gate::check('update', $company))
35 |
36 |
37 | {{ __('filament-companies::default.buttons.save') }}
38 |
39 |
40 | @endif
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/resources/views/components/connected-account.blade.php:
--------------------------------------------------------------------------------
1 | @props(['provider', 'createdAt' => null])
2 |
3 | @php
4 | $providerEnum = \Wallo\FilamentCompanies\Enums\Provider::tryFrom($provider);
5 | @endphp
6 |
7 | @if($providerEnum?->isEnabled())
8 |
9 |
10 |
11 |
12 | {{ $providerEnum->getIconView() }}
13 |
14 |
15 |
16 |
17 | {{ $providerEnum->getLabel() }}
18 |
19 |
20 | @if (!empty($createdAt))
21 |
23 | {{ __('filament-companies::default.labels.connected') }}
24 |
26 | {{ $createdAt }}
27 |
28 |
29 | @else
30 |
31 | {{ __('filament-companies::default.labels.not_connected') }}
32 |
33 | @endif
34 |
35 |
36 |
37 |
38 | {{ $action }}
39 |
40 |
41 |
42 | @error($provider.'_connect_error')
43 |
44 | {{ $message }}
45 |
46 | @enderror
47 |
48 | @endif
49 |
--------------------------------------------------------------------------------
/resources/views/components/grid-section.blade.php:
--------------------------------------------------------------------------------
1 | @props(['title','description'])
2 |
3 |
4 |
5 | {{$title}}
6 |
7 |
8 | {{$description}}
9 |
10 |
11 |
12 |
13 | {{ $slot }}
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/resources/views/components/input-error.blade.php:
--------------------------------------------------------------------------------
1 | @props(['for'])
2 |
3 | @error($for)
4 | merge(['class' => 'filament-companies-input-error text-sm text-danger-600 dark:text-danger-400']) }}>{{ $message }}
5 | @enderror
6 |
--------------------------------------------------------------------------------
/resources/views/components/input.blade.php:
--------------------------------------------------------------------------------
1 | @props(['disabled' => false, 'id' => null])
2 |
3 | class([
6 | 'filament-companies-input block w-full transition duration-75 rounded-lg shadow-sm outline-none focus:ring-1 focus:ring-inset disabled:opacity-70',
7 | 'dark:bg-gray-700 dark:text-white' => config('forms.dark_mode'),
8 | 'border-gray-300 focus:border-primary-500 focus:ring-primary-500' => ! $errors->has($id),
9 | 'dark:border-gray-600 dark:focus:border-primary-500' => ! $errors->has($id) && config('forms.dark_mode'),
10 | 'border-danger-600 ring-danger-600 focus:border-danger-500 focus:ring-danger-500' => $errors->has($id),
11 | 'dark:border-danger-400 dark:ring-danger-400 dark:focus:border-danger-400 dark:focus:ring-danger-400' => $errors->has($id) && config('forms.dark_mode'),
12 | ]) !!}
13 | />
14 |
--------------------------------------------------------------------------------
/resources/views/components/section-border.blade.php:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/resources/views/components/socialite-icons/bitbucket.blade.php:
--------------------------------------------------------------------------------
1 |
14 |
--------------------------------------------------------------------------------
/resources/views/components/socialite-icons/facebook.blade.php:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/resources/views/components/socialite-icons/github.blade.php:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/resources/views/components/socialite-icons/gitlab.blade.php:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/resources/views/components/socialite-icons/google.blade.php:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/resources/views/components/socialite-icons/linkedin.blade.php:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/resources/views/components/socialite-icons/slack.blade.php:
--------------------------------------------------------------------------------
1 |
15 |
--------------------------------------------------------------------------------
/resources/views/components/socialite-icons/twitter.blade.php:
--------------------------------------------------------------------------------
1 |
4 |
5 |
--------------------------------------------------------------------------------
/resources/views/components/socialite.blade.php:
--------------------------------------------------------------------------------
1 | @props([
2 | 'errorMessage' => null,
3 | ])
4 |
5 |
6 |
7 |
8 | {{ __('filament-companies::default.subheadings.auth.login') }}
9 |
10 |
11 |
12 | @if ($errorMessage)
13 |
{!! $errorMessage !!}
14 | @endif
15 |
16 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/resources/views/filament/pages/companies/company_settings.blade.php:
--------------------------------------------------------------------------------
1 |
2 | @livewire(\Wallo\FilamentCompanies\Http\Livewire\UpdateCompanyNameForm::class, compact('company'))
3 |
4 | @livewire(\Wallo\FilamentCompanies\Http\Livewire\CompanyEmployeeManager::class, compact('company'))
5 |
6 | @if (!$company->personal_company && Gate::check('delete', $company))
7 |
8 | @livewire(\Wallo\FilamentCompanies\Http\Livewire\DeleteCompanyForm::class, compact('company'))
9 | @endif
10 |
11 |
--------------------------------------------------------------------------------
/resources/views/filament/pages/companies/create_company.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ $this->form }}
4 |
5 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/resources/views/filament/pages/user/personal-access-tokens.blade.php:
--------------------------------------------------------------------------------
1 | @php
2 | $modals = \Wallo\FilamentCompanies\FilamentCompanies::getModals();
3 | @endphp
4 |
5 |
6 | {{ $this->table }}
7 |
8 |
9 |
10 | {{ __('filament-companies::default.modal_titles.token') }}
11 |
12 |
13 |
14 |
15 | {{ __('filament-companies::default.modal_descriptions.copy_token') }}
16 |
17 |
18 |
19 | $refs.plaintextToken.select(), 250)"
23 | />
24 |
25 | @if($modals['cancelButtonAction'])
26 |
27 |
28 | {{ __('filament-companies::default.buttons.close') }}
29 |
30 |
31 | @endif
32 |
33 |
34 |
--------------------------------------------------------------------------------
/resources/views/filament/pages/user/profile.blade.php:
--------------------------------------------------------------------------------
1 |
2 | @php
3 | $components = \Wallo\FilamentCompanies\FilamentCompanies::getProfileComponents();
4 | @endphp
5 |
6 | @foreach($components as $index => $component)
7 | @livewire($component)
8 |
9 | @if($loop->remaining)
10 |
11 | @endif
12 | @endforeach
13 |
14 |
--------------------------------------------------------------------------------
/resources/views/mail/company-invitation.blade.php:
--------------------------------------------------------------------------------
1 | @component('mail::message')
2 | {{ __('You have been invited to join the :company company!', ['company' => $invitation->company->name]) }}
3 |
4 | @if (filament()->getRegistrationUrl())
5 | {{ __('If you do not have an account, you may create one by clicking the button below. After creating an account, you may click the invitation acceptance button in this email to accept the company invitation:') }}
6 |
7 | @component('mail::button', ['url' => url(filament()->getRegistrationUrl())])
8 | {{ __('Create Account') }}
9 | @endcomponent
10 |
11 | {{ __('If you already have an account, you may accept this invitation by clicking the button below:') }}
12 |
13 | @else
14 | {{ __('You may accept this invitation by clicking the button below:') }}
15 | @endif
16 |
17 |
18 | @component('mail::button', ['url' => $acceptUrl])
19 | {{ __('Accept Invitation') }}
20 | @endcomponent
21 |
22 | {{ __('If you did not expect to receive an invitation to this company, you may discard this email.') }}
23 | @endcomponent
24 |
--------------------------------------------------------------------------------
/resources/views/profile/delete-user-form.blade.php:
--------------------------------------------------------------------------------
1 | @php
2 | $modals = \Wallo\FilamentCompanies\FilamentCompanies::getModals();
3 | @endphp
4 |
5 |
6 |
7 | {{ __('filament-companies::default.grid_section_titles.delete_account') }}
8 |
9 |
10 |
11 | {{ __('filament-companies::default.grid_section_descriptions.delete_account') }}
12 |
13 |
14 |
15 |
16 |
17 | {{ __('filament-companies::default.subheadings.profile.delete_user') }}
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | {{ __('filament-companies::default.buttons.delete_account') }}
26 |
27 |
28 |
29 |
30 |
31 | {{ __('filament-companies::default.modal_titles.delete_account') }}
32 |
33 |
34 |
35 | {{ __('filament-companies::default.modal_descriptions.delete_account') }}
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | @if($modals['cancelButtonAction'])
46 |
47 | {{ __('filament-companies::default.buttons.cancel') }}
48 |
49 | @endif
50 |
51 |
52 | {{ __('filament-companies::default.buttons.delete_account') }}
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/resources/views/profile/set-password-form.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ __('filament-companies::default.grid_section_titles.set_password') }}
4 |
5 |
6 |
7 | {{ __('filament-companies::default.grid_section_descriptions.set_password') }}
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | {{ __('filament-companies::default.buttons.save') }}
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/resources/views/profile/update-password-form.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ __('filament-companies::default.grid_section_titles.update_password') }}
4 |
5 |
6 |
7 | {{ __('filament-companies::default.grid_section_descriptions.update_password') }}
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | {{ __('filament-companies::default.buttons.save') }}
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/src/Actions/GenerateRedirectForProvider.php:
--------------------------------------------------------------------------------
1 | redirect();
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Actions/UpdateCompanyEmployeeRole.php:
--------------------------------------------------------------------------------
1 | authorize('updateCompanyEmployee', $company);
22 |
23 | Validator::make(compact('role'), [
24 | 'role' => ['required', 'string', new Role],
25 | ])->validate();
26 |
27 | $company->users()->updateExistingPivot($companyEmployeeId, compact('role'));
28 |
29 | CompanyEmployeeUpdated::dispatch($company->fresh(), FilamentCompanies::findUserByIdOrFail($companyEmployeeId));
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Actions/ValidateCompanyDeletion.php:
--------------------------------------------------------------------------------
1 | authorize('delete', $company);
19 |
20 | if ($company->personal_company) {
21 | throw ValidationException::withMessages([
22 | 'company' => __('filament-companies::default.errors.company_deletion'),
23 | ])->errorBag('deleteCompany');
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Company.php:
--------------------------------------------------------------------------------
1 | belongsTo(FilamentCompanies::userModel(), 'user_id');
19 | }
20 |
21 | /**
22 | * Get all the company's users including its owner.
23 | */
24 | public function allUsers(): Collection
25 | {
26 | return $this->users->merge([$this->owner]);
27 | }
28 |
29 | /**
30 | * Get all the users that belong to the company.
31 | */
32 | public function users(): BelongsToMany
33 | {
34 | return $this->belongsToMany(FilamentCompanies::userModel(), FilamentCompanies::employeeshipModel())
35 | ->withPivot('role')
36 | ->withTimestamps()
37 | ->as('employeeship');
38 | }
39 |
40 | /**
41 | * Determine if the given user belongs to the company.
42 | */
43 | public function hasUser(mixed $user): bool
44 | {
45 | return $this->users->contains($user) || $user->ownsCompany($this);
46 | }
47 |
48 | /**
49 | * Determine if the given email address belongs to a user on the company.
50 | */
51 | public function hasUserWithEmail(string $email): bool
52 | {
53 | return $this->allUsers()->contains(static function ($user) use ($email) {
54 | return $user->email === $email;
55 | });
56 | }
57 |
58 | /**
59 | * Determine if the given user has the given permission on the company.
60 | */
61 | public function userHasPermission(mixed $user, string $permission): bool
62 | {
63 | return $user->hasCompanyPermission($this, $permission);
64 | }
65 |
66 | /**
67 | * Get all the pending user invitations for the company.
68 | */
69 | public function companyInvitations(): HasMany
70 | {
71 | return $this->hasMany(FilamentCompanies::companyInvitationModel());
72 | }
73 |
74 | /**
75 | * Remove the given user from the company.
76 | */
77 | public function removeUser(mixed $user): void
78 | {
79 | if ($user->current_company_id === $this->id) {
80 | $user->forceFill([
81 | 'current_company_id' => null,
82 | ])->save();
83 | }
84 |
85 | $this->users()->detach($user);
86 | }
87 |
88 | /**
89 | * Purge all the company's resources.
90 | */
91 | public function purge(): void
92 | {
93 | $this->owner()->where('current_company_id', $this->id)
94 | ->update(['current_company_id' => null]);
95 |
96 | $this->users()->where('current_company_id', $this->id)
97 | ->update(['current_company_id' => null]);
98 |
99 | $this->users()->detach();
100 |
101 | $this->delete();
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/Concerns/Base/HasAddedProfileComponents.php:
--------------------------------------------------------------------------------
1 | $component) {
12 | static::$addedProfileComponents[] = $component;
13 | static::$componentSortOrder[$component] = $sort;
14 | }
15 |
16 | return $this;
17 | }
18 |
19 | /**
20 | * Get the added profile page components.
21 | */
22 | public static function getAddedProfileComponents(): array
23 | {
24 | return static::$addedProfileComponents;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Concerns/Base/HasAutoAcceptInvitations.php:
--------------------------------------------------------------------------------
1 | singleton(CreatesNewUsers::class, $class);
24 | }
25 |
26 | /**
27 | * Register a class / callback that should be used to update user profile information.
28 | */
29 | public static function updateUserProfileInformationUsing(string $class): void
30 | {
31 | app()->singleton(UpdatesUserProfileInformation::class, $class);
32 | }
33 |
34 | /**
35 | * Register a class / callback that should be used to update user passwords.
36 | */
37 | public static function updateUserPasswordsUsing(string $class): void
38 | {
39 | app()->singleton(UpdatesUserPasswords::class, $class);
40 | }
41 |
42 | /**
43 | * Register a class / callback that should be used to create companies.
44 | */
45 | public static function createCompaniesUsing(string $class): void
46 | {
47 | app()->singleton(CreatesCompanies::class, $class);
48 | }
49 |
50 | /**
51 | * Register a class / callback that should be used to update company names.
52 | */
53 | public static function updateCompanyNamesUsing(string $class): void
54 | {
55 | app()->singleton(UpdatesCompanyNames::class, $class);
56 | }
57 |
58 | /**
59 | * Register a class / callback that should be used to add company employees.
60 | */
61 | public static function addCompanyEmployeesUsing(string $class): void
62 | {
63 | app()->singleton(AddsCompanyEmployees::class, $class);
64 | }
65 |
66 | /**
67 | * Register a class / callback that should be used to add company employees.
68 | */
69 | public static function inviteCompanyEmployeesUsing(string $class): void
70 | {
71 | app()->singleton(InvitesCompanyEmployees::class, $class);
72 | }
73 |
74 | /**
75 | * Register a class / callback that should be used to remove company employees.
76 | */
77 | public static function removeCompanyEmployeesUsing(string $class): void
78 | {
79 | app()->singleton(RemovesCompanyEmployees::class, $class);
80 | }
81 |
82 | /**
83 | * Register a class / callback that should be used to delete companies.
84 | */
85 | public static function deleteCompaniesUsing(string $class): void
86 | {
87 | app()->singleton(DeletesCompanies::class, $class);
88 | }
89 |
90 | /**
91 | * Register a class / callback that should be used to delete users.
92 | */
93 | public static function deleteUsersUsing(string $class): void
94 | {
95 | app()->singleton(DeletesUsers::class, $class);
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/Concerns/Base/HasBaseModels.php:
--------------------------------------------------------------------------------
1 | where('id', $id)->firstOrFail();
130 | }
131 |
132 | /**
133 | * Find a user instance by the given email address or fail.
134 | */
135 | public static function findUserByEmailOrFail(string $email): mixed
136 | {
137 | return static::newUserModel()->where('email', $email)->firstOrFail();
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/src/Concerns/Base/HasBaseProfileComponents.php:
--------------------------------------------------------------------------------
1 | user();
72 | $passwordIsSet = $user?->getAuthPassword() !== null;
73 |
74 | if (static::canUpdateProfileInformation()) {
75 | $components[] = static::getUpdateProfileInformationForm();
76 | }
77 |
78 | if ($passwordIsSet && static::canUpdatePasswords()) {
79 | $components[] = static::getUpdatePasswordForm();
80 | }
81 |
82 | if ($passwordIsSet && static::canManageBrowserSessions()) {
83 | $components[] = static::getLogoutOtherBrowserSessionsForm();
84 | }
85 |
86 | if ($passwordIsSet && static::hasAccountDeletionFeatures()) {
87 | $components[] = static::getDeleteUserForm();
88 | }
89 |
90 | return $components;
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/Concerns/Base/HasCompanyFeatures.php:
--------------------------------------------------------------------------------
1 | 0;
30 | }
31 |
32 | /**
33 | * Find the role with the given key.
34 | */
35 | public static function findRole(string $key): ?Role
36 | {
37 | return static::$roles[$key] ?? null;
38 | }
39 |
40 | /**
41 | * Define a role.
42 | */
43 | public static function role(string $key, string $name, array $permissions): Role
44 | {
45 | static::$permissions = collect([...static::$permissions, ...$permissions])
46 | ->unique()
47 | ->sort()
48 | ->values()
49 | ->all();
50 |
51 | return tap(new Role($key, $name, $permissions), static function ($role) use ($key) {
52 | static::$roles[$key] = $role;
53 | });
54 | }
55 |
56 | /**
57 | * Determine if any permissions have been registered with Company.
58 | */
59 | public static function hasPermissions(): bool
60 | {
61 | return count(static::$permissions) > 0;
62 | }
63 |
64 | /**
65 | * Define the available API token permissions.
66 | */
67 | public static function permissions(array $permissions): static
68 | {
69 | static::$permissions = $permissions;
70 |
71 | return new static;
72 | }
73 |
74 | /**
75 | * Define the default permissions that should be available to new API tokens.
76 | */
77 | public static function defaultApiTokenPermissions(array $permissions): static
78 | {
79 | static::$defaultPermissions = $permissions;
80 |
81 | return new static;
82 | }
83 |
84 | /**
85 | * Return the permissions in the given list that are actually defined permissions for the application.
86 | */
87 | public static function validPermissions(array $permissions): array
88 | {
89 | return array_values(array_intersect($permissions, static::$permissions));
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/Concerns/Base/HasRoutes.php:
--------------------------------------------------------------------------------
1 | name('oauth.redirect');
34 | Route::get('/oauth/{provider}/callback', [OAuthController::class, 'handleProviderCallback'])->name('oauth.callback');
35 | }
36 |
37 | if (static::hasTermsAndPrivacyPolicyFeature()) {
38 | Route::get(Terms::getSlug(), Terms::class)->name(Terms::getRouteName());
39 | Route::get(PrivacyPolicy::getSlug(), PrivacyPolicy::class)->name(PrivacyPolicy::getRouteName());
40 | }
41 | }
42 |
43 | protected function registerAuthenticatedRoutes(): void
44 | {
45 | if (static::sendsCompanyInvitations()) {
46 | Route::get('/invitations/{invitation}', [CompanyInvitationController::class, 'accept'])
47 | ->middleware(['signed'])
48 | ->name('invitations.accept');
49 | }
50 | }
51 |
52 | public static function route(string $name, mixed $parameters = [], bool $absolute = true): string
53 | {
54 | return route(static::generateRouteName($name), $parameters, $absolute);
55 | }
56 |
57 | public static function generateRouteName(string $name): string
58 | {
59 | return 'filament.' . static::getCompanyPanel() . ".{$name}";
60 | }
61 |
62 | public static function generateOAuthRedirectUrl(string $provider): string
63 | {
64 | return static::route('oauth.redirect', compact('provider'));
65 | }
66 |
67 | public static function generateAcceptInvitationUrl(CompanyInvitation $invitation): string
68 | {
69 | return URL::signedRoute(static::generateRouteName('invitations.accept'), compact('invitation'));
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/Concerns/Base/HasTermsAndPrivacyPolicy.php:
--------------------------------------------------------------------------------
1 | getLocale() . '$1', $name);
39 |
40 | return Arr::first([
41 | resource_path('markdown/' . $localName),
42 | resource_path('markdown/' . $name),
43 | ], static function ($path) {
44 | return file_exists($path);
45 | });
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Concerns/ManagesProfileComponents.php:
--------------------------------------------------------------------------------
1 | static::$componentSortOrder[$b];
22 | });
23 |
24 | return $components;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Concerns/Socialite/CanEnableSocialite.php:
--------------------------------------------------------------------------------
1 | enableSocialite($condition);
20 | $this->setProviders($providers);
21 | $this->setFeatures($features);
22 |
23 | return $this;
24 | }
25 |
26 | public function enableSocialite(bool | Closure | null $condition = true): static
27 | {
28 | $isEnabled = $condition instanceof Closure ? $condition() : $condition;
29 | static::$hasSocialiteFeatures = $isEnabled;
30 |
31 | if (! $isEnabled) {
32 | static::disableAllSocialiteFeatures();
33 | }
34 |
35 | return $this;
36 | }
37 |
38 | private static function disableAllSocialiteFeatures(): void
39 | {
40 | static::$hasSocialiteFeatures = false;
41 | static::$canSetPasswords = false;
42 | static::$canManageConnectedAccounts = false;
43 | }
44 |
45 | /**
46 | * Determine if the application has support for socialite.
47 | */
48 | public static function hasSocialiteFeatures(): bool
49 | {
50 | return static::$hasSocialiteFeatures;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Concerns/Socialite/HasConnectedAccountModel.php:
--------------------------------------------------------------------------------
1 | where('provider', $provider)
49 | ->where('provider_id', $providerId)
50 | ->first();
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Concerns/Socialite/HasProviderFeatures.php:
--------------------------------------------------------------------------------
1 | $feature->value, static::$enabledFeatures);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Concerns/Socialite/HasProviders.php:
--------------------------------------------------------------------------------
1 | $provider->value, static::$enabledProviders);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Concerns/Socialite/HasSocialiteActionBindings.php:
--------------------------------------------------------------------------------
1 | singleton(ResolvesSocialiteUsers::class, $class);
21 | }
22 |
23 | /**
24 | * Register a class / callback that should be used to create users from social providers.
25 | */
26 | public static function createUsersFromProviderUsing(string $class): void
27 | {
28 | app()->singleton(CreatesUserFromProvider::class, $class);
29 | }
30 |
31 | /**
32 | * Register a class / callback that should be used to create connected accounts.
33 | */
34 | public static function createConnectedAccountsUsing(string $class): void
35 | {
36 | app()->singleton(CreatesConnectedAccounts::class, $class);
37 | }
38 |
39 | /**
40 | * Register a class / callback that should be used to update connected accounts.
41 | */
42 | public static function updateConnectedAccountsUsing(string $class): void
43 | {
44 | app()->singleton(UpdatesConnectedAccounts::class, $class);
45 | }
46 |
47 | /**
48 | * Register a class / callback that should be used to set user passwords.
49 | */
50 | public static function setUserPasswordsUsing(callable | string $callback): void
51 | {
52 | app()->singleton(SetsUserPasswords::class, $callback);
53 | }
54 |
55 | /**
56 | * Register a class / callback that should be used to set user passwords.
57 | */
58 | public static function handlesInvalidStateUsing(callable | string $callback): void
59 | {
60 | app()->singleton(HandlesInvalidState::class, $callback);
61 | }
62 |
63 | /**
64 | * Register a class / callback that should be used for generating provider redirects.
65 | */
66 | public static function generatesProvidersRedirectsUsing(callable | string $callback): void
67 | {
68 | app()->singleton(GeneratesProviderRedirect::class, $callback);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/Concerns/Socialite/HasSocialiteComponents.php:
--------------------------------------------------------------------------------
1 | user();
41 | $passwordIsNull = $user?->getAuthPassword() === null;
42 |
43 | if ($passwordIsNull && static::canSetPasswords()) {
44 | $components[] = static::getSetPasswordForm();
45 | }
46 |
47 | if (static::canManageConnectedAccounts()) {
48 | $components[] = static::getConnectedAccountsForm();
49 | }
50 |
51 | return $components;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/Concerns/Socialite/HasSocialiteProfileFeatures.php:
--------------------------------------------------------------------------------
1 | belongsTo(FilamentCompanies::userModel(), 'user_id', FilamentCompanies::newUserModel()->getAuthIdentifierName());
35 | }
36 |
37 | /**
38 | * Get the data that should be shared.
39 | *
40 | * @return array
41 | */
42 | public function getSharedData(): array
43 | {
44 | return [
45 | 'id' => $this->id,
46 | 'provider' => $this->provider,
47 | 'avatar_path' => $this->avatar_path,
48 | 'created_at' => $this->created_at?->diffForHumans(),
49 | ];
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/Contracts/AddsCompanyEmployees.php:
--------------------------------------------------------------------------------
1 | $input
13 | */
14 | public function create(array $input): User;
15 | }
16 |
--------------------------------------------------------------------------------
/src/Contracts/CreatesUserFromProvider.php:
--------------------------------------------------------------------------------
1 | id = $connectedAccount->provider_id;
47 | $this->token = $connectedAccount->token;
48 | $this->tokenSecret = $connectedAccount->secret;
49 | $this->refreshToken = $connectedAccount->refresh_token;
50 | $this->expiry = $connectedAccount->expires_at;
51 | }
52 |
53 | /**
54 | * Get the ID for the credentials.
55 | */
56 | public function getId(): string
57 | {
58 | return $this->id;
59 | }
60 |
61 | /**
62 | * Get token for the credentials.
63 | */
64 | public function getToken(): string
65 | {
66 | return $this->token;
67 | }
68 |
69 | /**
70 | * Get the token secret for the credentials.
71 | */
72 | public function getTokenSecret(): ?string
73 | {
74 | return $this->tokenSecret;
75 | }
76 |
77 | /**
78 | * Get the refresh token for the credentials.
79 | */
80 | public function getRefreshToken(): ?string
81 | {
82 | return $this->refreshToken;
83 | }
84 |
85 | /**
86 | * Get the expiry date for the credentials.
87 | *
88 | * @throws Exception
89 | */
90 | public function getExpiry(): ?DateTimeInterface
91 | {
92 | if ($this->expiry === null) {
93 | return null;
94 | }
95 |
96 | return new DateTime($this->expiry);
97 | }
98 |
99 | /**
100 | * Get the instance as an array.
101 | *
102 | * @return array
103 | *
104 | * @throws Exception
105 | */
106 | public function toArray(): array
107 | {
108 | return [
109 | 'id' => $this->getId(),
110 | 'token' => $this->getToken(),
111 | 'token_secret' => $this->getTokenSecret(),
112 | 'refresh_token' => $this->getRefreshToken(),
113 | 'expiry' => $this->getExpiry(),
114 | ];
115 | }
116 |
117 | /**
118 | * Convert the object to its JSON representation.
119 | *
120 | * @param int $options
121 | *
122 | * @throws Exception
123 | */
124 | public function toJson($options = 0): string
125 | {
126 | return json_encode($this->toArray(), JSON_THROW_ON_ERROR | $options);
127 | }
128 |
129 | /**
130 | * Specify data which should be serialized to JSON.
131 | *
132 | * @return array
133 | *
134 | * @throws Exception
135 | */
136 | public function jsonSerialize(): array
137 | {
138 | return $this->toArray();
139 | }
140 |
141 | /**
142 | * Convert the object instance to a string.
143 | *
144 | * @throws JsonException
145 | * @throws Exception
146 | */
147 | public function __toString(): string
148 | {
149 | return json_encode($this->toJson(), JSON_THROW_ON_ERROR);
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/src/Employeeship.php:
--------------------------------------------------------------------------------
1 | 'Remember Session',
21 | self::RefreshOAuthTokens => 'Refresh OAuth Tokens',
22 | self::ProviderAvatars => 'Provider Avatars',
23 | self::GenerateMissingEmails => 'Generate Missing Emails',
24 | self::LoginOnRegistration => 'Login on Registration',
25 | self::CreateAccountOnFirstLogin => 'Create Account on First Login',
26 | };
27 | }
28 |
29 | public function isEnabled(): bool
30 | {
31 | return FilamentCompanies::isFeatureEnabled($this);
32 | }
33 |
34 | public function isDisabled(): bool
35 | {
36 | return $this->isEnabled() === false;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/Enums/Provider.php:
--------------------------------------------------------------------------------
1 | 'Bitbucket',
26 | self::Facebook => 'Facebook',
27 | self::Gitlab => 'GitLab',
28 | self::Github => 'GitHub',
29 | self::Google => 'Google',
30 | self::LinkedIn, self::LinkedInOpenId => 'LinkedIn',
31 | self::Slack => 'Slack',
32 | self::Twitter, self::TwitterOAuth2 => 'X',
33 | };
34 | }
35 |
36 | public function isEnabled(): bool
37 | {
38 | return FilamentCompanies::isProviderEnabled($this);
39 | }
40 |
41 | public function getIconView(): View
42 | {
43 | $viewName = match ($this) {
44 | self::Bitbucket => 'filament-companies::components.socialite-icons.bitbucket',
45 | self::Facebook => 'filament-companies::components.socialite-icons.facebook',
46 | self::Gitlab => 'filament-companies::components.socialite-icons.gitlab',
47 | self::Github => 'filament-companies::components.socialite-icons.github',
48 | self::Google => 'filament-companies::components.socialite-icons.google',
49 | self::LinkedIn, self::LinkedInOpenId => 'filament-companies::components.socialite-icons.linkedin',
50 | self::Slack => 'filament-companies::components.socialite-icons.slack',
51 | self::Twitter, self::TwitterOAuth2 => 'filament-companies::components.socialite-icons.twitter',
52 | };
53 |
54 | return view($viewName);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Events/AddingCompany.php:
--------------------------------------------------------------------------------
1 | owner = $owner;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Events/AddingCompanyEmployee.php:
--------------------------------------------------------------------------------
1 | company = $company;
29 | $this->user = $user;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Events/CompanyCreated.php:
--------------------------------------------------------------------------------
1 | company = $company;
29 | $this->user = $user;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Events/CompanyEmployeeRemoved.php:
--------------------------------------------------------------------------------
1 | company = $company;
29 | $this->user = $user;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Events/CompanyEmployeeUpdated.php:
--------------------------------------------------------------------------------
1 | company = $company;
29 | $this->user = $user;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Events/CompanyEvent.php:
--------------------------------------------------------------------------------
1 | company = $company;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Events/CompanyUpdated.php:
--------------------------------------------------------------------------------
1 | connectedAccount = $connectedAccount;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Events/ConnectedAccountUpdated.php:
--------------------------------------------------------------------------------
1 | company = $company;
34 | $this->email = $email;
35 | $this->role = $role;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Events/RemovingCompanyEmployee.php:
--------------------------------------------------------------------------------
1 | company = $company;
29 | $this->user = $user;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/FilamentCompanies.php:
--------------------------------------------------------------------------------
1 | getId();
59 |
60 | if (static::hasCompanyFeatures()) {
61 | Livewire::component('filament.pages.companies.create_company', CreateCompany::class);
62 | Livewire::component('filament.pages.companies.company_settings', CompanySettings::class);
63 | }
64 |
65 | app()->bind(RegistrationResponseContract::class, FilamentCompaniesRegistrationResponse::class);
66 |
67 | if (static::hasSocialiteFeatures()) {
68 | app()->bind(OAuthController::class, static function (Application $app) {
69 | return new OAuthController(
70 | $app->make(CreatesUserFromProvider::class),
71 | $app->make(CreatesConnectedAccounts::class),
72 | $app->make(UpdatesConnectedAccounts::class),
73 | $app->make(HandlesInvalidState::class),
74 | );
75 | });
76 | }
77 |
78 | if (static::$registersRoutes) {
79 | $panel->routes(fn () => $this->registerPublicRoutes());
80 | $panel->authenticatedRoutes(fn () => $this->registerAuthenticatedRoutes());
81 | }
82 | }
83 |
84 | public function boot(Panel $panel): void
85 | {
86 | if (static::switchesCurrentCompany()) {
87 | Event::listen(TenantSet::class, SwitchCurrentCompany::class);
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/HasConnectedAccounts.php:
--------------------------------------------------------------------------------
1 | id === $this->currentConnectedAccount->id;
17 | }
18 |
19 | /**
20 | * Get the current connected account of the user's context.
21 | */
22 | public function currentConnectedAccount(): BelongsTo
23 | {
24 | if ($this->current_connected_account_id === null && $this->id) {
25 | $this->switchConnectedAccount(
26 | $this->connectedAccounts()->orderBy('created_at')->first()
27 | );
28 | }
29 |
30 | return $this->belongsTo(FilamentCompanies::connectedAccountModel(), 'current_connected_account_id');
31 | }
32 |
33 | /**
34 | * Switch the user's context to the given connected account.
35 | */
36 | public function switchConnectedAccount(mixed $connectedAccount): bool
37 | {
38 | if (! $this->ownsConnectedAccount($connectedAccount)) {
39 | return false;
40 | }
41 |
42 | $this->forceFill([
43 | 'current_connected_account_id' => $connectedAccount->id,
44 | ])->save();
45 |
46 | $this->setRelation('currentConnectedAccount', $connectedAccount);
47 |
48 | return true;
49 | }
50 |
51 | /**
52 | * Determine if the user owns the given connected account.
53 | */
54 | public function ownsConnectedAccount(mixed $connectedAccount): bool
55 | {
56 | return $this->id === $connectedAccount->user_id;
57 | }
58 |
59 | /**
60 | * Determine if the user has a specific account type.
61 | */
62 | public function hasTokenFor(string $provider): bool
63 | {
64 | return $this->connectedAccounts->contains('provider', Str::lower($provider));
65 | }
66 |
67 | /**
68 | * Attempt to retrieve the token for a given provider.
69 | */
70 | public function getTokenFor(string $provider, ?string $default = null): ?string
71 | {
72 | if ($this->hasTokenFor($provider)) {
73 | return $this->connectedAccounts
74 | ->where('provider', Str::lower($provider))
75 | ->first()
76 | ->token;
77 | }
78 |
79 | return $default;
80 | }
81 |
82 | /**
83 | * Attempt to find a connected account that belongs to the user,
84 | * for the given provider and ID.
85 | */
86 | public function getConnectedAccountFor(string $provider, string $id): mixed
87 | {
88 | return $this->connectedAccounts
89 | ->where('provider', $provider)
90 | ->where('provider_id', $id)
91 | ->first();
92 | }
93 |
94 | /**
95 | * Get all the connected accounts belonging to the user.
96 | */
97 | public function connectedAccounts(): HasMany
98 | {
99 | return $this->hasMany(FilamentCompanies::connectedAccountModel(), 'user_id', $this->getAuthIdentifierName());
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/HasProfilePhoto.php:
--------------------------------------------------------------------------------
1 | profile_photo_path, function ($previous) use ($photo, $storagePath) {
19 | $this->forceFill([
20 | 'profile_photo_path' => $photo->storePublicly(
21 | $storagePath,
22 | ['disk' => $this->profilePhotoDisk()]
23 | ),
24 | ])->save();
25 |
26 | if ($previous) {
27 | Storage::disk($this->profilePhotoDisk())->delete($previous);
28 | }
29 | });
30 | }
31 |
32 | /**
33 | * Delete the user's profile photo.
34 | */
35 | public function deleteProfilePhoto(): void
36 | {
37 | if ($this->profile_photo_path === null || ! FilamentCompanies::managesProfilePhotos()) {
38 | return;
39 | }
40 |
41 | Storage::disk($this->profilePhotoDisk())->delete($this->profile_photo_path);
42 |
43 | $this->forceFill([
44 | 'profile_photo_path' => null,
45 | ])->save();
46 | }
47 |
48 | /**
49 | * Get the URL to the user's profile photo.
50 | */
51 | public function profilePhotoUrl(): Attribute
52 | {
53 | return Attribute::get(function () {
54 | return $this->profile_photo_path
55 | ? Storage::disk($this->profilePhotoDisk())->url($this->profile_photo_path)
56 | : $this->defaultProfilePhotoUrl();
57 | });
58 | }
59 |
60 | /**
61 | * Get the default profile photo URL if no profile photo has been uploaded.
62 | */
63 | protected function defaultProfilePhotoUrl(): string
64 | {
65 | $name = trim(collect(explode(' ', $this->name))->map(static function ($segment) {
66 | return mb_substr($segment, 0, 1);
67 | })->join(' '));
68 |
69 | return sprintf('https://ui-avatars.com/api/?name=%s&color=FFFFFF&background=111827', urlencode($name));
70 | }
71 |
72 | /**
73 | * Get the disk that profile photos should be stored on.
74 | */
75 | protected function profilePhotoDisk(): string
76 | {
77 | return isset($_ENV['VAPOR_ARTIFACT_NAME']) ? 's3' : FilamentCompanies::profilePhotoDisk();
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/Http/Controllers/CompanyInvitationController.php:
--------------------------------------------------------------------------------
1 | firstOrFail();
27 | $user = FilamentCompanies::userModel()::where('email', $invitation->email)->first();
28 |
29 | app(AddsCompanyEmployees::class)->add(
30 | $invitation->company->owner,
31 | $invitation->company,
32 | $invitation->email,
33 | $invitation->role
34 | );
35 |
36 | $invitation->delete();
37 |
38 | $title = __('filament-companies::default.banner.company_invitation_accepted', ['company' => $invitation->company->name]);
39 | $notification = Notification::make()->title(Str::inlineMarkdown($title))->success()->persistent()->send();
40 |
41 | if ($user) {
42 | Filament::auth()->login($user);
43 |
44 | return redirect(url(filament()->getHomeUrl()))->with('notification.success.company_invitation_accepted', $notification);
45 | }
46 |
47 | return redirect(url(filament()->getLoginUrl()));
48 | }
49 |
50 | /**
51 | * Cancel the given company invitation.
52 | *
53 | * @throws AuthorizationException
54 | */
55 | public function destroy(Request $request, int $invitationId): Redirector | RedirectResponse
56 | {
57 | $model = FilamentCompanies::companyInvitationModel();
58 |
59 | $invitation = $model::whereKey($invitationId)->firstOrFail();
60 |
61 | if (! Gate::forUser($request->user())->check('removeCompanyEmployee', $invitation->company)) {
62 | throw new AuthorizationException;
63 | }
64 |
65 | $invitation->delete();
66 |
67 | return back(303);
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/Http/Livewire/CreateCompanyForm.php:
--------------------------------------------------------------------------------
1 | resetErrorBag();
32 |
33 | $creator->create($this->user, $this->state);
34 |
35 | $name = $this->state['name'];
36 |
37 | $this->companyCreated($name);
38 |
39 | return $this->redirectPath($creator);
40 | }
41 |
42 | /**
43 | * Get the current user of the application.
44 | */
45 | public function getUserProperty(): ?Authenticatable
46 | {
47 | return Auth::user();
48 | }
49 |
50 | /**
51 | * Render the component.
52 | */
53 | public function render(): View
54 | {
55 | return view('filament-companies::companies.create-company-form');
56 | }
57 |
58 | public function companyCreated($name): void
59 | {
60 | Notification::make()
61 | ->title(__('filament-companies::default.notifications.company_created.title'))
62 | ->success()
63 | ->body(Str::inlineMarkdown(__('filament-companies::default.notifications.company_created.body', compact('name'))))
64 | ->send();
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/Http/Livewire/DeleteCompanyForm.php:
--------------------------------------------------------------------------------
1 | company = $company;
34 | }
35 |
36 | /**
37 | * Delete the company.
38 | *
39 | * @throws AuthorizationException
40 | */
41 | public function deleteCompany(ValidateCompanyDeletion $validator, DeletesCompanies $deleter): Response | Redirector | RedirectResponse
42 | {
43 | $validator->validate(Auth::user(), $this->company);
44 |
45 | $deleter->delete($this->company);
46 |
47 | if (FilamentCompanies::hasNotificationsFeature()) {
48 | if (method_exists($deleter, 'companyDeleted')) {
49 | $deleter->companyDeleted($this->company);
50 | } else {
51 | $this->companyDeleted($this->company);
52 | }
53 | }
54 |
55 | $this->company = null;
56 |
57 | return $this->redirectPath($deleter);
58 | }
59 |
60 | /**
61 | * Cancel the company deletion.
62 | */
63 | public function cancelCompanyDeletion(): void
64 | {
65 | $this->dispatch('close-modal', id: 'confirmingCompanyDeletion');
66 | }
67 |
68 | /**
69 | * Render the component.
70 | */
71 | public function render(): View
72 | {
73 | return view('filament-companies::companies.delete-company-form');
74 | }
75 |
76 | public function companyDeleted($company): void
77 | {
78 | $name = $company->name;
79 |
80 | Notification::make()
81 | ->title(__('filament-companies::default.notifications.company_deleted.title'))
82 | ->success()
83 | ->body(Str::inlineMarkdown(__('filament-companies::default.notifications.company_deleted.body', compact('name'))))
84 | ->send();
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/Http/Livewire/DeleteUserForm.php:
--------------------------------------------------------------------------------
1 | resetErrorBag();
28 |
29 | $this->password = '';
30 |
31 | $this->dispatch('confirming-delete-user');
32 |
33 | $this->dispatch('open-modal', id: 'confirmingUserDeletion');
34 | }
35 |
36 | /**
37 | * Delete the current user.
38 | */
39 | public function deleteUser(DeletesUsers $deleter): RedirectResponse | Redirector
40 | {
41 | $this->resetErrorBag();
42 |
43 | $auth = Filament::auth();
44 |
45 | if (! Hash::check($this->password, Auth::user()->password)) {
46 | throw ValidationException::withMessages([
47 | 'password' => [__('filament-companies::default.errors.invalid_password')],
48 | ]);
49 | }
50 |
51 | $deleter->delete(Auth::user()?->fresh());
52 |
53 | $auth->logout();
54 |
55 | if (session() !== null) {
56 | session()->invalidate();
57 | session()->regenerateToken();
58 | }
59 |
60 | return redirect()->to(Filament::hasLogin() ? Filament::getLoginUrl() : Filament::getUrl());
61 | }
62 |
63 | /**
64 | * Cancel the user deletion.
65 | */
66 | public function cancelUserDeletion(): void
67 | {
68 | $this->dispatch('close-modal', id: 'confirmingUserDeletion');
69 | }
70 |
71 | /**
72 | * Render the component.
73 | */
74 | public function render(): View
75 | {
76 | return view('filament-companies::profile.delete-user-form');
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/Http/Livewire/SetPasswordForm.php:
--------------------------------------------------------------------------------
1 |
20 | */
21 | public $state = [
22 | 'password' => '',
23 | 'password_confirmation' => '',
24 | ];
25 |
26 | /**
27 | * Update the user's password.
28 | */
29 | public function setPassword(SetsUserPasswords $setter): void
30 | {
31 | $this->resetErrorBag();
32 |
33 | $setter->set($this->user, $this->state);
34 |
35 | $this->state = [
36 | 'password' => '',
37 | 'password_confirmation' => '',
38 | ];
39 |
40 | if (FilamentCompanies::hasNotificationsFeature()) {
41 | if (method_exists($setter, 'passwordSet')) {
42 | $setter->passwordSet($this->user, $this->state);
43 | } else {
44 | $this->passwordSet();
45 | }
46 | }
47 | }
48 |
49 | /**
50 | * Get the current user of the application.
51 | */
52 | public function getUserProperty(): ?Authenticatable
53 | {
54 | return Auth::user();
55 | }
56 |
57 | /**
58 | * Render the component.
59 | */
60 | public function render(): View
61 | {
62 | return view('filament-companies::profile.set-password-form');
63 | }
64 |
65 | public function passwordSet(): void
66 | {
67 | Notification::make()
68 | ->title(__('filament-companies::default.notifications.password_set.title'))
69 | ->success()
70 | ->color(Color::Green)
71 | ->body(__('filament-companies::default.notifications.password_set.body'))
72 | ->duration(3000)
73 | ->send();
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/Http/Livewire/UpdateCompanyNameForm.php:
--------------------------------------------------------------------------------
1 | company = $company;
32 |
33 | $this->state = $company->withoutRelations()->toArray();
34 | }
35 |
36 | /**
37 | * Update the company's name.
38 | */
39 | public function updateCompanyName(UpdatesCompanyNames $updater): void
40 | {
41 | $this->resetErrorBag();
42 |
43 | $updater->update($this->user, $this->company, $this->state);
44 |
45 | if (FilamentCompanies::hasNotificationsFeature()) {
46 | if (method_exists($updater, 'companyNameUpdated')) {
47 | $updater->companyNameUpdated($this->user, $this->company, $this->state);
48 | } else {
49 | $this->companyNameUpdated($this->company);
50 | }
51 | }
52 | }
53 |
54 | protected function companyNameUpdated($company): void
55 | {
56 | $name = $company->name;
57 |
58 | Notification::make()
59 | ->title(__('filament-companies::default.notifications.company_name_updated.title'))
60 | ->success()
61 | ->body(Str::inlineMarkdown(__('filament-companies::default.notifications.company_name_updated.body', compact('name'))))
62 | ->send();
63 | }
64 |
65 | /**
66 | * Get the current user of the application.
67 | */
68 | public function getUserProperty(): ?Authenticatable
69 | {
70 | return Auth::user();
71 | }
72 |
73 | /**
74 | * Render the component.
75 | */
76 | public function render(): View
77 | {
78 | return view('filament-companies::companies.update-company-name-form');
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/Http/Livewire/UpdatePasswordForm.php:
--------------------------------------------------------------------------------
1 |
19 | */
20 | public $state = [
21 | 'current_password' => '',
22 | 'password' => '',
23 | 'password_confirmation' => '',
24 | ];
25 |
26 | /**
27 | * Update the user's password.
28 | */
29 | public function updatePassword(UpdatesUserPasswords $updater): void
30 | {
31 | $this->resetErrorBag();
32 |
33 | $updater->update($this->user, $this->state);
34 |
35 | if (session() !== null) {
36 | session()->put([
37 | 'password_hash_' . Auth::getDefaultDriver() => $this->user?->getAuthPassword(),
38 | ]);
39 | }
40 |
41 | $this->state = [
42 | 'current_password' => '',
43 | 'password' => '',
44 | 'password_confirmation' => '',
45 | ];
46 |
47 | if (FilamentCompanies::hasNotificationsFeature()) {
48 | if (method_exists($updater, 'passwordUpdated')) {
49 | $updater->passwordUpdated($this->user, $this->state);
50 | } else {
51 | $this->passwordUpdated();
52 | }
53 | }
54 | }
55 |
56 | /**
57 | * Get the current user of the application.
58 | */
59 | public function getUserProperty(): ?Authenticatable
60 | {
61 | return Auth::user();
62 | }
63 |
64 | /**
65 | * Render the component.
66 | */
67 | public function render(): View
68 | {
69 | return view('filament-companies::profile.update-password-form');
70 | }
71 |
72 | public function passwordUpdated(): void
73 | {
74 | Notification::make()
75 | ->title(__('filament-companies::default.notifications.password_updated.title'))
76 | ->success()
77 | ->body(__('filament-companies::default.notifications.password_updated.body'))
78 | ->send();
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/Http/Livewire/UpdateProfileInformationForm.php:
--------------------------------------------------------------------------------
1 | user;
40 |
41 | $this->state = ['email' => $user?->email, ...$user?->withoutRelations()->toArray()];
42 | }
43 |
44 | /**
45 | * Update the user's profile information.
46 | */
47 | public function updateProfileInformation(UpdatesUserProfileInformation $updater): void
48 | {
49 | $this->resetErrorBag();
50 |
51 | $updater->update(
52 | $this->user,
53 | $this->photo
54 | ? [...$this->state, 'photo' => $this->photo]
55 | : $this->state
56 | );
57 |
58 | if (isset($this->photo)) {
59 | redirect(Profile::getUrl());
60 | }
61 |
62 | if (FilamentCompanies::hasNotificationsFeature()) {
63 | if (method_exists($updater, 'profileInformationUpdated')) {
64 | $updater->profileInformationUpdated($this->user, $this->state);
65 | } else {
66 | $this->profileInformationUpdated();
67 | }
68 | }
69 | }
70 |
71 | protected function profileInformationUpdated(): void
72 | {
73 | Notification::make()
74 | ->title(__('filament-companies::default.notifications.profile_information_updated.title'))
75 | ->success()
76 | ->body(__('filament-companies::default.notifications.profile_information_updated.body'))
77 | ->send();
78 | }
79 |
80 | /**
81 | * Delete user's profile photo.
82 | */
83 | public function deleteProfilePhoto(): void
84 | {
85 | $this->user?->deleteProfilePhoto();
86 | }
87 |
88 | /**
89 | * Sent the email verification.
90 | */
91 | public function sendEmailVerification(): void
92 | {
93 | $this->user?->sendEmailVerificationNotification();
94 |
95 | $this->verificationLinkSent = true;
96 |
97 | Notification::make()
98 | ->title(__('filament-companies::default.notifications.verification_link_sent.title'))
99 | ->success()
100 | ->body(__('filament-companies::default.notifications.verification_link_sent.body'))
101 | ->send();
102 | }
103 |
104 | /**
105 | * Get the current user of the application.
106 | */
107 | public function getUserProperty(): ?Authenticatable
108 | {
109 | return Auth::user();
110 | }
111 |
112 | /**
113 | * Render the component.
114 | */
115 | public function render(): View
116 | {
117 | return view('filament-companies::profile.update-profile-information-form');
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/src/Http/Responses/Auth/FilamentCompaniesRegistrationResponse.php:
--------------------------------------------------------------------------------
1 | user();
16 |
17 | if (
18 | FilamentCompanies::autoAcceptsInvitations() &&
19 | method_exists($user, 'hasAnyCompanies') &&
20 | ! $user->hasAnyCompanies() &&
21 | ($invitation = FilamentCompanies::companyInvitationModel()::where('email', $user->email)->first())
22 | ) {
23 | return redirect(FilamentCompanies::generateAcceptInvitationUrl($invitation));
24 | }
25 |
26 | return parent::toResponse($request);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Listeners/SwitchCurrentCompany.php:
--------------------------------------------------------------------------------
1 | getTenant();
25 |
26 | /** @var HasCompanies $user */
27 | $user = $event->getUser();
28 |
29 | if (FilamentCompanies::switchesCurrentCompany() === false || ! in_array(HasCompanies::class, class_uses_recursive($user), true)) {
30 | return;
31 | }
32 |
33 | if (! $user->switchCompany($tenant) && ($fallbackCompany = $user->primaryCompany())) {
34 | $user->switchCompany($fallbackCompany);
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Mail/CompanyInvitation.php:
--------------------------------------------------------------------------------
1 | invitation = $invitation;
29 | }
30 |
31 | /**
32 | * Build the message.
33 | */
34 | public function build(): static
35 | {
36 | $acceptUrl = FilamentCompanies::generateAcceptInvitationUrl($this->invitation);
37 |
38 | return $this->markdown('filament-companies::mail.company-invitation', compact('acceptUrl'))
39 | ->subject(__('Company Invitation'));
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/OwnerRole.php:
--------------------------------------------------------------------------------
1 | schema([
17 | $this->getEmailFormComponent(),
18 | $this->getPasswordFormComponent(),
19 | $this->getRememberFormComponent(),
20 | ])
21 | ->statePath('data')
22 | ->model(FilamentCompanies::userModel());
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Pages/Auth/PrivacyPolicy.php:
--------------------------------------------------------------------------------
1 | Str::markdown(file_get_contents($policyFile)),
24 | ];
25 | }
26 |
27 | public function getHeading(): string | Htmlable
28 | {
29 | return '';
30 | }
31 |
32 | public function getMaxWidth(): MaxWidth | string | null
33 | {
34 | return MaxWidth::TwoExtraLarge;
35 | }
36 |
37 | public static function getSlug(): string
38 | {
39 | return static::$slug ?? 'privacy-policy';
40 | }
41 |
42 | public static function getRouteName(): string
43 | {
44 | return 'auth.policy';
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Pages/Auth/Register.php:
--------------------------------------------------------------------------------
1 | schema([
21 | $this->getNameFormComponent(),
22 | $this->getEmailFormComponent(),
23 | $this->getPasswordFormComponent(),
24 | $this->getPasswordConfirmationFormComponent(),
25 | ...FilamentCompanies::hasTermsAndPrivacyPolicyFeature() ? [$this->getTermsFormComponent()] : []])
26 | ->statePath('data')
27 | ->model(FilamentCompanies::userModel());
28 | }
29 |
30 | protected function getTermsFormComponent(): Component
31 | {
32 | return Checkbox::make('terms')
33 | ->label(new HtmlString(__('filament-companies::default.subheadings.auth.register', [
34 | 'terms_of_service' => $this->generateFilamentLink(Terms::getRouteName(), __('filament-companies::default.links.terms_of_service')),
35 | 'privacy_policy' => $this->generateFilamentLink(PrivacyPolicy::getRouteName(), __('filament-companies::default.links.privacy_policy')),
36 | ])))
37 | ->validationAttribute(__('filament-companies::default.errors.terms'))
38 | ->accepted();
39 | }
40 |
41 | public function generateFilamentLink(string $routeName, string $label): string
42 | {
43 | return Blade::render('filament::components.link', [
44 | 'href' => FilamentCompanies::route($routeName),
45 | 'target' => '_blank',
46 | 'color' => 'primary',
47 | 'slot' => $label,
48 | ]);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Pages/Auth/Terms.php:
--------------------------------------------------------------------------------
1 | Str::markdown(file_get_contents($termsFile)),
24 | ];
25 | }
26 |
27 | public function getHeading(): string | Htmlable
28 | {
29 | return '';
30 | }
31 |
32 | public function getMaxWidth(): MaxWidth | string | null
33 | {
34 | return MaxWidth::TwoExtraLarge;
35 | }
36 |
37 | public static function getSlug(): string
38 | {
39 | return static::$slug ?? 'terms-of-service';
40 | }
41 |
42 | public static function getRouteName(): string
43 | {
44 | return 'auth.terms';
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Pages/Company/CompanySettings.php:
--------------------------------------------------------------------------------
1 | allowed();
25 | } catch (AuthorizationException $exception) {
26 | return $exception->toResponse()->allowed();
27 | }
28 | }
29 |
30 | protected function getViewData(): array
31 | {
32 | return [
33 | 'company' => Filament::getTenant(),
34 | ];
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Pages/Company/CreateCompany.php:
--------------------------------------------------------------------------------
1 | schema([
29 | TextInput::make('name')
30 | ->label(__('filament-companies::default.labels.company_name'))
31 | ->autofocus()
32 | ->maxLength(255)
33 | ->required(),
34 | ])
35 | ->model(FilamentCompanies::companyModel())
36 | ->statePath('data');
37 | }
38 |
39 | protected function handleRegistration(array $data): Model
40 | {
41 | $user = Auth::user();
42 |
43 | Gate::forUser($user)->authorize('create', FilamentCompanies::newCompanyModel());
44 |
45 | AddingCompany::dispatch($user);
46 |
47 | $personalCompany = $user?->personalCompany() === null;
48 |
49 | $company = $user?->ownedCompanies()->create([
50 | 'name' => $data['name'],
51 | 'personal_company' => $personalCompany,
52 | ]);
53 |
54 | $user?->switchCompany($company);
55 |
56 | $name = $data['name'];
57 |
58 | $this->companyCreated($name);
59 |
60 | return $company;
61 | }
62 |
63 | protected function companyCreated($name): void
64 | {
65 | Notification::make()
66 | ->title(__('filament-companies::default.notifications.company_created.title'))
67 | ->success()
68 | ->body(Str::inlineMarkdown(__('filament-companies::default.notifications.company_created.body', compact('name'))))
69 | ->send();
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/Pages/User/Profile.php:
--------------------------------------------------------------------------------
1 | Auth::user(),
23 | ];
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/RedirectsActions.php:
--------------------------------------------------------------------------------
1 | redirectTo();
18 | } else {
19 | $response = property_exists($action, 'redirectTo')
20 | ? $action->redirectTo
21 | : filament()->getHomeUrl();
22 | }
23 |
24 | return $response instanceof Response ? $response : redirect($response);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Role.php:
--------------------------------------------------------------------------------
1 | key = $key;
37 | $this->name = $name;
38 | $this->permissions = $permissions;
39 | }
40 |
41 | /**
42 | * Describe the role.
43 | *
44 | * @return $this
45 | */
46 | public function description(string $description): static
47 | {
48 | $this->description = $description;
49 |
50 | return $this;
51 | }
52 |
53 | /**
54 | * Get the JSON serializable representation of the object.
55 | */
56 | #[\ReturnTypeWillChange]
57 | public function jsonSerialize(): array
58 | {
59 | return [
60 | 'key' => $this->key,
61 | 'name' => __($this->name),
62 | 'description' => __($this->description),
63 | 'permissions' => $this->permissions,
64 | ];
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/Rules/Role.php:
--------------------------------------------------------------------------------
1 | = 200 and < 300
21 | if ($response->successful()) {
22 | file_put_contents($file = sys_get_temp_dir() . '/' . Str::uuid()->toString(), $response);
23 |
24 | $this->updateProfilePhoto(new UploadedFile($file, $name));
25 | } else {
26 | Notification::make()
27 | ->title('Unable to retrieve image')
28 | ->danger()
29 | ->persistent()
30 | ->send();
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/stubs/app/Actions/FilamentCompanies/AddCompanyEmployee.php:
--------------------------------------------------------------------------------
1 | authorize('addCompanyEmployee', $company);
28 |
29 | $this->validate($company, $email, $role);
30 |
31 | $newCompanyEmployee = FilamentCompanies::findUserByEmailOrFail($email);
32 |
33 | AddingCompanyEmployee::dispatch($company, $newCompanyEmployee);
34 |
35 | $company->users()->attach(
36 | $newCompanyEmployee,
37 | ['role' => $role]
38 | );
39 |
40 | CompanyEmployeeAdded::dispatch($company, $newCompanyEmployee);
41 | }
42 |
43 | /**
44 | * Validate the add employee operation.
45 | */
46 | protected function validate(Company $company, string $email, ?string $role): void
47 | {
48 | Validator::make([
49 | 'email' => $email,
50 | 'role' => $role,
51 | ], $this->rules(), [
52 | 'email.exists' => __('filament-companies::default.errors.email_not_found'),
53 | ])->after(
54 | $this->ensureUserIsNotAlreadyOnCompany($company, $email)
55 | )->validateWithBag('addCompanyEmployee');
56 | }
57 |
58 | /**
59 | * Get the validation rules for adding a company employee.
60 | *
61 | * @return array
62 | */
63 | protected function rules(): array
64 | {
65 | return array_filter([
66 | 'email' => ['required', 'email', 'exists:users'],
67 | 'role' => FilamentCompanies::hasRoles()
68 | ? ['required', 'string', new Role]
69 | : null,
70 | ]);
71 | }
72 |
73 | /**
74 | * Ensure that the user is not already on the company.
75 | */
76 | protected function ensureUserIsNotAlreadyOnCompany(Company $company, string $email): Closure
77 | {
78 | return static function ($validator) use ($company, $email) {
79 | $validator->errors()->addIf(
80 | $company->hasUserWithEmail($email),
81 | 'email',
82 | __('filament-companies::default.errors.user_belongs_to_company')
83 | );
84 | };
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/stubs/app/Actions/FilamentCompanies/CreateCompany.php:
--------------------------------------------------------------------------------
1 | $input
20 | *
21 | * @throws AuthorizationException
22 | */
23 | public function create(User $user, array $input): Company
24 | {
25 | Gate::forUser($user)->authorize('create', FilamentCompanies::newCompanyModel());
26 |
27 | Validator::make($input, [
28 | 'name' => ['required', 'string', 'max:255'],
29 | ])->validateWithBag('createCompany');
30 |
31 | AddingCompany::dispatch($user);
32 |
33 | $user->switchCompany($company = $user->ownedCompanies()->create([
34 | 'name' => $input['name'],
35 | 'personal_company' => false,
36 | ]));
37 |
38 | return $company;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/stubs/app/Actions/FilamentCompanies/CreateConnectedAccount.php:
--------------------------------------------------------------------------------
1 | $user->getAuthIdentifier(),
20 | 'provider' => strtolower($provider),
21 | 'provider_id' => $providerUser->getId(),
22 | 'name' => $providerUser->getName(),
23 | 'nickname' => $providerUser->getNickname(),
24 | 'email' => $providerUser->getEmail(),
25 | 'avatar_path' => $providerUser->getAvatar(),
26 | 'token' => $providerUser->token,
27 | 'secret' => $providerUser->tokenSecret ?? null,
28 | 'refresh_token' => $providerUser->refreshToken ?? null,
29 | 'expires_at' => property_exists($providerUser, 'expiresIn') ? now()->addSeconds($providerUser->expiresIn) : null,
30 | ]);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/stubs/app/Actions/FilamentCompanies/CreateNewUser.php:
--------------------------------------------------------------------------------
1 | $input
19 | */
20 | public function create(array $input): User
21 | {
22 | Validator::make($input, [
23 | 'name' => ['required', 'string', 'max:255'],
24 | 'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
25 | 'password' => ['required', 'string', 'min:8', 'confirmed'],
26 | 'terms' => FilamentCompanies::hasTermsAndPrivacyPolicyFeature() ? ['accepted', 'required'] : '',
27 | ])->validate();
28 |
29 | return DB::transaction(function () use ($input) {
30 | return tap(User::create([
31 | 'name' => $input['name'],
32 | 'email' => $input['email'],
33 | 'password' => Hash::make($input['password']),
34 | ]), function (User $user) {
35 | $this->createCompany($user);
36 | });
37 | });
38 | }
39 |
40 | /**
41 | * Create a personal company for the user.
42 | */
43 | protected function createCompany(User $user): void
44 | {
45 | $user->ownedCompanies()->save(Company::forceCreate([
46 | 'user_id' => $user->id,
47 | 'name' => explode(' ', $user->name, 2)[0] . "'s Company",
48 | 'personal_company' => true,
49 | ]));
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/stubs/app/Actions/FilamentCompanies/CreateUserFromProvider.php:
--------------------------------------------------------------------------------
1 | createsConnectedAccounts = $createsConnectedAccounts;
27 | }
28 |
29 | /**
30 | * Create a new user from a social provider user.
31 | */
32 | public function create(string $provider, ProviderUserContract $providerUser): User
33 | {
34 | return DB::transaction(function () use ($providerUser, $provider) {
35 | return tap(User::create([
36 | 'name' => $providerUser->getName(),
37 | 'email' => $providerUser->getEmail(),
38 | ]), function (User $user) use ($providerUser, $provider) {
39 | $user->markEmailAsVerified();
40 |
41 | if ($this->shouldSetProfilePhoto($providerUser)) {
42 | $user->setProfilePhotoFromUrl($providerUser->getAvatar());
43 | }
44 |
45 | $user->switchConnectedAccount(
46 | $this->createsConnectedAccounts->create($user, $provider, $providerUser)
47 | );
48 |
49 | $this->createCompany($user);
50 | });
51 | });
52 | }
53 |
54 | private function shouldSetProfilePhoto(ProviderUserContract $providerUser): bool
55 | {
56 | return Feature::ProviderAvatars->isEnabled() &&
57 | FilamentCompanies::managesProfilePhotos() &&
58 | $providerUser->getAvatar();
59 | }
60 |
61 | /**
62 | * Create a personal company for the user.
63 | */
64 | protected function createCompany(User $user): void
65 | {
66 | $user->ownedCompanies()->save(Company::forceCreate([
67 | 'user_id' => $user->id,
68 | 'name' => explode(' ', $user->name, 2)[0] . "'s Company",
69 | 'personal_company' => true,
70 | ]));
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/stubs/app/Actions/FilamentCompanies/DeleteCompany.php:
--------------------------------------------------------------------------------
1 | purge();
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/stubs/app/Actions/FilamentCompanies/DeleteUser.php:
--------------------------------------------------------------------------------
1 | deleteCompanies($user);
29 | $user->deleteProfilePhoto();
30 | $user->tokens->each(static fn (PersonalAccessToken $token) => $token->delete());
31 | $user->delete();
32 | });
33 | }
34 |
35 | /**
36 | * Delete the companies and company associations attached to the user.
37 | */
38 | protected function deleteCompanies(User $user): void
39 | {
40 | $user->companies()->detach();
41 |
42 | $user->ownedCompanies->each(function (Company $company) {
43 | $this->deletesCompanies->delete($company);
44 | });
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/stubs/app/Actions/FilamentCompanies/DeleteUserWithSocialite.php:
--------------------------------------------------------------------------------
1 | deletesCompanies = $deletesCompanies;
24 | }
25 |
26 | /**
27 | * Delete the given user.
28 | */
29 | public function delete(User $user): void
30 | {
31 | DB::transaction(function () use ($user) {
32 | $this->deleteCompanies($user);
33 | $user->deleteProfilePhoto();
34 | $user->connectedAccounts->each(static fn ($account) => $account->delete());
35 | $user->tokens->each(static fn ($token) => $token->delete());
36 | $user->delete();
37 | });
38 | }
39 |
40 | /**
41 | * Delete the companies and company associations attached to the user.
42 | */
43 | protected function deleteCompanies(User $user): void
44 | {
45 | $user->companies()->detach();
46 |
47 | $user->ownedCompanies->each(function (Company $company) {
48 | $this->deletesCompanies->delete($company);
49 | });
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/stubs/app/Actions/FilamentCompanies/HandleInvalidState.php:
--------------------------------------------------------------------------------
1 | authorize('addCompanyEmployee', $company);
30 |
31 | $this->validate($company, $email, $role);
32 |
33 | InvitingCompanyEmployee::dispatch($company, $email, $role);
34 |
35 | $invitation = $company->companyInvitations()->create([
36 | 'email' => $email,
37 | 'role' => $role,
38 | ]);
39 |
40 | Mail::to($email)->send(new CompanyInvitation($invitation));
41 | }
42 |
43 | /**
44 | * Validate the invite employee operation.
45 | */
46 | protected function validate(Company $company, string $email, ?string $role): void
47 | {
48 | Validator::make([
49 | 'email' => $email,
50 | 'role' => $role,
51 | ], $this->rules($company), [
52 | 'email.unique' => __('filament-companies::default.errors.employee_already_invited'),
53 | ])->after(
54 | $this->ensureUserIsNotAlreadyOnCompany($company, $email)
55 | )->validateWithBag('addCompanyEmployee');
56 | }
57 |
58 | /**
59 | * Get the validation rules for inviting a company employee.
60 | *
61 | * @return array
62 | */
63 | protected function rules(Company $company): array
64 | {
65 | return array_filter([
66 | 'email' => [
67 | 'required', 'email',
68 | Rule::unique('company_invitations')->where(static function (Builder $query) use ($company) {
69 | $query->where('company_id', $company->id);
70 | }),
71 | ],
72 | 'role' => FilamentCompanies::hasRoles()
73 | ? ['required', 'string', new Role]
74 | : null,
75 | ]);
76 | }
77 |
78 | /**
79 | * Ensure that the employee is not already on the company.
80 | */
81 | protected function ensureUserIsNotAlreadyOnCompany(Company $company, string $email): Closure
82 | {
83 | return static function ($validator) use ($company, $email) {
84 | $validator->errors()->addIf(
85 | $company->hasUserWithEmail($email),
86 | 'email',
87 | __('filament-companies::default.errors.employee_already_belongs_to_company')
88 | );
89 | };
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/stubs/app/Actions/FilamentCompanies/RemoveCompanyEmployee.php:
--------------------------------------------------------------------------------
1 | authorize($user, $company, $companyEmployee);
23 |
24 | $this->ensureUserDoesNotOwnCompany($companyEmployee, $company);
25 |
26 | $company->removeUser($companyEmployee);
27 |
28 | CompanyEmployeeRemoved::dispatch($company, $companyEmployee);
29 | }
30 |
31 | /**
32 | * Authorize that the user can remove the company employee.
33 | *
34 | * @throws AuthorizationException
35 | */
36 | protected function authorize(User $user, Company $company, User $companyEmployee): void
37 | {
38 | if (! Gate::forUser($user)->check('removeCompanyEmployee', $company) &&
39 | $user->id !== $companyEmployee->id) {
40 | throw new AuthorizationException;
41 | }
42 | }
43 |
44 | /**
45 | * Ensure that the currently authenticated user does not own the company.
46 | */
47 | protected function ensureUserDoesNotOwnCompany(User $companyEmployee, Company $company): void
48 | {
49 | if ($companyEmployee->id === $company->owner->id) {
50 | throw ValidationException::withMessages([
51 | 'company' => [__('filament-companies::default.errors.cannot_leave_company')],
52 | ])->errorBag('removeCompanyEmployee');
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/stubs/app/Actions/FilamentCompanies/ResolveSocialiteUser.php:
--------------------------------------------------------------------------------
1 | user();
18 |
19 | if (Feature::GenerateMissingEmails->isEnabled()) {
20 | $user->email = $user->getEmail() ?? ("{$user->id}@{$provider}" . config('app.domain'));
21 | }
22 |
23 | return $user;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/stubs/app/Actions/FilamentCompanies/SetUserPassword.php:
--------------------------------------------------------------------------------
1 | ['required', 'string', 'min:8', 'confirmed'],
19 | ])->validateWithBag('setPassword');
20 |
21 | $user->forceFill([
22 | 'password' => Hash::make($input['password']),
23 | ])->save();
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/stubs/app/Actions/FilamentCompanies/UpdateCompanyName.php:
--------------------------------------------------------------------------------
1 | $input
18 | *
19 | * @throws AuthorizationException
20 | */
21 | public function update(User $user, Company $company, array $input): void
22 | {
23 | Gate::forUser($user)->authorize('update', $company);
24 |
25 | Validator::make($input, [
26 | 'name' => ['required', 'string', 'max:255'],
27 | ])->validateWithBag('updateCompanyName');
28 |
29 | $company->forceFill([
30 | 'name' => $input['name'],
31 | ])->save();
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/stubs/app/Actions/FilamentCompanies/UpdateConnectedAccount.php:
--------------------------------------------------------------------------------
1 | authorize('update', $connectedAccount);
21 |
22 | $connectedAccount->forceFill([
23 | 'provider' => strtolower($provider),
24 | 'provider_id' => $providerUser->getId(),
25 | 'name' => $providerUser->getName(),
26 | 'nickname' => $providerUser->getNickname(),
27 | 'email' => $providerUser->getEmail(),
28 | 'avatar_path' => $providerUser->getAvatar(),
29 | 'token' => $providerUser->token,
30 | 'secret' => $providerUser->tokenSecret ?? null,
31 | 'refresh_token' => $providerUser->refreshToken ?? null,
32 | 'expires_at' => property_exists($providerUser, 'expiresIn') ? now()->addSeconds($providerUser->expiresIn) : null,
33 | ])->save();
34 |
35 | return $connectedAccount;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/stubs/app/Actions/FilamentCompanies/UpdateUserPassword.php:
--------------------------------------------------------------------------------
1 | $input
16 | */
17 | public function update(User $user, array $input): void
18 | {
19 | Validator::make($input, [
20 | 'current_password' => ['required', 'string', 'current_password:web'],
21 | 'password' => ['required', 'string', 'min:8', 'confirmed'],
22 | ], [
23 | 'current_password.current_password' => __('filament-companies::default.errors.password_does_not_match'),
24 | ])->validateWithBag('updatePassword');
25 |
26 | $user->forceFill([
27 | 'password' => Hash::make($input['password']),
28 | ])->save();
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/stubs/app/Actions/FilamentCompanies/UpdateUserProfileInformation.php:
--------------------------------------------------------------------------------
1 | $input
17 | */
18 | public function update(User $user, array $input): void
19 | {
20 | Validator::make($input, [
21 | 'name' => ['required', 'string', 'max:255'],
22 | 'email' => ['required', 'email', 'max:255', Rule::unique('users')->ignore($user->id)],
23 | 'photo' => ['nullable', 'mimes:jpg,jpeg,png', 'max:1024'],
24 | ])->validateWithBag('updateProfileInformation');
25 |
26 | if (isset($input['photo'])) {
27 | $user->updateProfilePhoto($input['photo']);
28 | }
29 |
30 | if ($input['email'] !== $user->email &&
31 | $this->userMustVerifyEmail()) {
32 | $this->updateVerifiedUser($user, $input);
33 | } else {
34 | $user->forceFill([
35 | 'name' => $input['name'],
36 | 'email' => $input['email'],
37 | ])->save();
38 | }
39 | }
40 |
41 | /**
42 | * Determine if the user must verify their email address.
43 | */
44 | protected function userMustVerifyEmail(): bool
45 | {
46 | return in_array(MustVerifyEmail::class, class_implements(User::class));
47 | }
48 |
49 | /**
50 | * Update the given verified user's profile information.
51 | *
52 | * @param array $input
53 | */
54 | protected function updateVerifiedUser(User $user, array $input): void
55 | {
56 | $user->forceFill([
57 | 'name' => $input['name'],
58 | 'email' => $input['email'],
59 | 'email_verified_at' => null,
60 | ])->save();
61 |
62 | $user->sendEmailVerificationNotification();
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/stubs/app/Models/Company.php:
--------------------------------------------------------------------------------
1 |
20 | */
21 | protected $fillable = [
22 | 'name',
23 | 'personal_company',
24 | ];
25 |
26 | /**
27 | * The event map for the model.
28 | *
29 | * @var array
30 | */
31 | protected $dispatchesEvents = [
32 | 'created' => CompanyCreated::class,
33 | 'updated' => CompanyUpdated::class,
34 | 'deleted' => CompanyDeleted::class,
35 | ];
36 |
37 | /**
38 | * Get the attributes that should be cast.
39 | *
40 | * @return array
41 | */
42 | protected function casts(): array
43 | {
44 | return [
45 | 'personal_company' => 'boolean',
46 | ];
47 | }
48 |
49 | public function getFilamentAvatarUrl(): string
50 | {
51 | return $this->owner->profile_photo_url;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/stubs/app/Models/CompanyInvitation.php:
--------------------------------------------------------------------------------
1 |
15 | */
16 | protected $fillable = [
17 | 'email',
18 | 'role',
19 | ];
20 |
21 | /**
22 | * Get the company that the invitation belongs to.
23 | */
24 | public function company(): BelongsTo
25 | {
26 | return $this->belongsTo(FilamentCompanies::companyModel());
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/stubs/app/Models/ConnectedAccount.php:
--------------------------------------------------------------------------------
1 |
19 | */
20 | protected $fillable = [
21 | 'provider',
22 | 'provider_id',
23 | 'name',
24 | 'nickname',
25 | 'email',
26 | 'avatar_path',
27 | 'token',
28 | 'refresh_token',
29 | 'expires_at',
30 | ];
31 |
32 | /**
33 | * The event map for the model.
34 | *
35 | * @var array
36 | */
37 | protected $dispatchesEvents = [
38 | 'created' => ConnectedAccountCreated::class,
39 | 'updated' => ConnectedAccountUpdated::class,
40 | 'deleted' => ConnectedAccountDeleted::class,
41 | ];
42 | }
43 |
--------------------------------------------------------------------------------
/stubs/app/Models/Employeeship.php:
--------------------------------------------------------------------------------
1 |
31 | */
32 | protected $fillable = [
33 | 'name',
34 | 'email',
35 | 'password',
36 | ];
37 |
38 | /**
39 | * The attributes that should be hidden for serialization.
40 | *
41 | * @var array
42 | */
43 | protected $hidden = [
44 | 'password',
45 | 'remember_token',
46 | ];
47 |
48 | /**
49 | * The accessors to append to the model's array form.
50 | *
51 | * @var array
52 | */
53 | protected $appends = [
54 | 'profile_photo_url',
55 | ];
56 |
57 | /**
58 | * Get the attributes that should be cast.
59 | *
60 | * @return array
61 | */
62 | protected function casts(): array
63 | {
64 | return [
65 | 'email_verified_at' => 'datetime',
66 | 'password' => 'hashed',
67 | ];
68 | }
69 |
70 | public function canAccessPanel(Panel $panel): bool
71 | {
72 | return true;
73 | }
74 |
75 | public function canAccessTenant(Model $tenant): bool
76 | {
77 | return $this->belongsToCompany($tenant);
78 | }
79 |
80 | public function getTenants(Panel $panel): array | Collection
81 | {
82 | return $this->allCompanies();
83 | }
84 |
85 | public function getDefaultTenant(Panel $panel): ?Model
86 | {
87 | return $this->currentCompany;
88 | }
89 |
90 | public function getFilamentAvatarUrl(): string
91 | {
92 | return $this->profile_photo_url;
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/stubs/app/Models/UserWithSocialite.php:
--------------------------------------------------------------------------------
1 |
35 | */
36 | protected $fillable = [
37 | 'name',
38 | 'email',
39 | 'password',
40 | ];
41 |
42 | /**
43 | * The attributes that should be hidden for serialization.
44 | *
45 | * @var array
46 | */
47 | protected $hidden = [
48 | 'password',
49 | 'remember_token',
50 | ];
51 |
52 | /**
53 | * The accessors to append to the model's array form.
54 | *
55 | * @var array
56 | */
57 | protected $appends = [
58 | 'profile_photo_url',
59 | ];
60 |
61 | /**
62 | * Get the attributes that should be cast.
63 | *
64 | * @return array
65 | */
66 | protected function casts(): array
67 | {
68 | return [
69 | 'email_verified_at' => 'datetime',
70 | 'password' => 'hashed',
71 | ];
72 | }
73 |
74 | public function canAccessPanel(Panel $panel): bool
75 | {
76 | return true;
77 | }
78 |
79 | public function canAccessTenant(Model $tenant): bool
80 | {
81 | return $this->belongsToCompany($tenant);
82 | }
83 |
84 | public function getTenants(Panel $panel): array | Collection
85 | {
86 | return $this->allCompanies();
87 | }
88 |
89 | public function getDefaultTenant(Panel $panel): ?Model
90 | {
91 | return $this->currentCompany;
92 | }
93 |
94 | public function getFilamentAvatarUrl(): string
95 | {
96 | return $this->profile_photo_url;
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/stubs/app/Policies/CompanyPolicy.php:
--------------------------------------------------------------------------------
1 | belongsToCompany($company);
27 | }
28 |
29 | /**
30 | * Determine whether the user can create models.
31 | */
32 | public function create(User $user): bool
33 | {
34 | return true;
35 | }
36 |
37 | /**
38 | * Determine whether the user can update the model.
39 | */
40 | public function update(User $user, Company $company): bool
41 | {
42 | return $user->ownsCompany($company);
43 | }
44 |
45 | /**
46 | * Determine whether the user can add company employees.
47 | */
48 | public function addCompanyEmployee(User $user, Company $company): bool
49 | {
50 | return $user->ownsCompany($company);
51 | }
52 |
53 | /**
54 | * Determine whether the user can update company employee permissions.
55 | */
56 | public function updateCompanyEmployee(User $user, Company $company): bool
57 | {
58 | return $user->ownsCompany($company);
59 | }
60 |
61 | /**
62 | * Determine whether the user can remove company employees.
63 | */
64 | public function removeCompanyEmployee(User $user, Company $company): bool
65 | {
66 | return $user->ownsCompany($company);
67 | }
68 |
69 | /**
70 | * Determine whether the user can delete the model.
71 | */
72 | public function delete(User $user, Company $company): bool
73 | {
74 | return $user->ownsCompany($company);
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/stubs/app/Policies/ConnectedAccountPolicy.php:
--------------------------------------------------------------------------------
1 | ownsConnectedAccount($connectedAccount);
27 | }
28 |
29 | /**
30 | * Determine whether the user can create models.
31 | */
32 | public function create(User $user): bool
33 | {
34 | return true;
35 | }
36 |
37 | /**
38 | * Determine whether the user can update the model.
39 | */
40 | public function update(User $user, ConnectedAccount $connectedAccount): bool
41 | {
42 | return $user->ownsConnectedAccount($connectedAccount);
43 | }
44 |
45 | /**
46 | * Determine whether the user can delete the model.
47 | */
48 | public function delete(User $user, ConnectedAccount $connectedAccount): bool
49 | {
50 | return $user->ownsConnectedAccount($connectedAccount);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/stubs/resources/markdown/policy.md:
--------------------------------------------------------------------------------
1 | # Privacy Policy
2 |
3 | Edit this file to define the privacy policy for your application.
4 |
5 | ## Introduction
6 |
7 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce ullamcorper non libero nec volutpat. Nullam et nibh ac nisl dictum blandit. Sed id sagittis urna. Suspendisse potenti. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Phasellus sit amet quam vitae odio sollicitudin volutpat in vel orci. Donec nec dolor vitae dolor feugiat condimentum.
8 |
9 | ## Information Collection and Use
10 |
11 | Mauris non tempus ante, a mollis tellus. Sed vel nulla lorem. Nulla facilisi. Praesent pellentesque, orci nec mollis fermentum, dolor felis venenatis magna, vel scelerisque nisl tellus ac ante. Aliquam erat volutpat. Nulla facilisi. Sed libero purus, gravida in eleifend at, iaculis et est. Mauris pharetra nibh eu libero vehicula, vitae consequat velit blandit.
12 |
13 | ## Log Data
14 |
15 | Cras placerat, velit et lacinia molestie, mi libero laoreet risus, eu cursus ante eros id nisl. Nunc vitae libero lacus. Pellentesque vel sapien sed lorem suscipit commodo. Phasellus eget nisi quis lectus faucibus vehicula. Mauris mollis nunc quis nisi vehicula, sit amet hendrerit velit dapibus. Donec sit amet nulla sed arcu lobortis elementum.
16 |
17 | ## Cookies
18 |
19 | Quisque egestas, arcu et convallis placerat, velit orci dapibus leo, eget vulputate eros nisl vitae risus. Vivamus vel tincidunt libero, sit amet sodales enim. Nulla sagittis, sapien at volutpat fermentum, ipsum arcu pretium quam, eget placerat enim sapien in nibh. Sed ac finibus sapien, at sollicitudin dolor.
20 |
21 | ## Service Providers
22 |
23 | Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.
24 |
--------------------------------------------------------------------------------
/stubs/resources/markdown/terms.md:
--------------------------------------------------------------------------------
1 | # Terms of Service
2 |
3 | Edit this file to define the terms of service for your application.
4 |
5 | ## Introduction
6 |
7 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce ullamcorper non libero nec volutpat. Nullam et nibh ac nisl dictum blandit. Sed id sagittis urna. Suspendisse potenti. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Phasellus sit amet quam vitae odio sollicitudin volutpat in vel orci. Donec nec dolor vitae dolor feugiat condimentum.
8 |
9 | ## Usage Terms
10 |
11 | Mauris non tempus ante, a mollis tellus. Sed vel nulla lorem. Nulla facilisi. Praesent pellentesque, orci nec mollis fermentum, dolor felis venenatis magna, vel scelerisque nisl tellus ac ante. Aliquam erat volutpat. Nulla facilisi. Sed libero purus, gravida in eleifend at, iaculis et est. Mauris pharetra nibh eu libero vehicula, vitae consequat velit blandit.
12 |
13 | ## User Conduct
14 |
15 | Cras placerat, velit et lacinia molestie, mi libero laoreet risus, eu cursus ante eros id nisl. Nunc vitae libero lacus. Pellentesque vel sapien sed lorem suscipit commodo. Phasellus eget nisi quis lectus faucibus vehicula. Mauris mollis nunc quis nisi vehicula, sit amet hendrerit velit dapibus. Donec sit amet nulla sed arcu lobortis elementum.
16 |
17 | ## Content Ownership
18 |
19 | Quisque egestas, arcu et convallis placerat, velit orci dapibus leo, eget vulputate eros nisl vitae risus. Vivamus vel tincidunt libero, sit amet sodales enim. Nulla sagittis, sapien at volutpat fermentum, ipsum arcu pretium quam, eget placerat enim sapien in nibh. Sed ac finibus sapien, at sollicitudin dolor.
20 |
21 | ## Limitations of Liability
22 |
23 | Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.
24 |
--------------------------------------------------------------------------------