Merge pull request 'DX audit and fix (PHP)' (#50) from agent/dx-audit-and-fix--laravel-php-package into dev
Reviewed-on: #50
This commit is contained in:
commit
c51e4310b1
52 changed files with 365 additions and 197 deletions
70
Boot.php
70
Boot.php
|
|
@ -4,10 +4,20 @@ declare(strict_types=1);
|
|||
|
||||
namespace Core\Tenant;
|
||||
|
||||
use App\Services\UserStatsService;
|
||||
use App\Services\WorkspaceCacheManager;
|
||||
use App\Services\WorkspaceManager;
|
||||
use App\Services\WorkspaceService;
|
||||
use Core\Events\AdminPanelBooting;
|
||||
use Core\Events\ApiRoutesRegistering;
|
||||
use Core\Events\ConsoleBooting;
|
||||
use Core\Events\WebRoutesRegistering;
|
||||
use Core\Tenant\Contracts\TwoFactorAuthenticationProvider;
|
||||
use Core\Tenant\Services\EntitlementService;
|
||||
use Core\Tenant\Services\EntitlementWebhookService;
|
||||
use Core\Tenant\Services\TotpService;
|
||||
use Core\Tenant\Services\UsageAlertService;
|
||||
use Core\Tenant\Services\WorkspaceTeamService;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
|
|
@ -40,48 +50,48 @@ class Boot extends ServiceProvider
|
|||
public function register(): void
|
||||
{
|
||||
$this->app->singleton(
|
||||
\Core\Tenant\Contracts\TwoFactorAuthenticationProvider::class,
|
||||
\Core\Tenant\Services\TotpService::class
|
||||
TwoFactorAuthenticationProvider::class,
|
||||
TotpService::class
|
||||
);
|
||||
|
||||
$this->app->singleton(
|
||||
\Core\Tenant\Services\EntitlementService::class,
|
||||
\Core\Tenant\Services\EntitlementService::class
|
||||
EntitlementService::class,
|
||||
EntitlementService::class
|
||||
);
|
||||
|
||||
$this->app->singleton(
|
||||
\Core\Tenant\Services\WorkspaceManager::class,
|
||||
\Core\Tenant\Services\WorkspaceManager::class
|
||||
Services\WorkspaceManager::class,
|
||||
Services\WorkspaceManager::class
|
||||
);
|
||||
|
||||
$this->app->singleton(
|
||||
\Core\Tenant\Services\UserStatsService::class,
|
||||
\Core\Tenant\Services\UserStatsService::class
|
||||
Services\UserStatsService::class,
|
||||
Services\UserStatsService::class
|
||||
);
|
||||
|
||||
$this->app->singleton(
|
||||
\Core\Tenant\Services\WorkspaceService::class,
|
||||
\Core\Tenant\Services\WorkspaceService::class
|
||||
Services\WorkspaceService::class,
|
||||
Services\WorkspaceService::class
|
||||
);
|
||||
|
||||
$this->app->singleton(
|
||||
\Core\Tenant\Services\WorkspaceCacheManager::class,
|
||||
\Core\Tenant\Services\WorkspaceCacheManager::class
|
||||
Services\WorkspaceCacheManager::class,
|
||||
Services\WorkspaceCacheManager::class
|
||||
);
|
||||
|
||||
$this->app->singleton(
|
||||
\Core\Tenant\Services\UsageAlertService::class,
|
||||
\Core\Tenant\Services\UsageAlertService::class
|
||||
UsageAlertService::class,
|
||||
UsageAlertService::class
|
||||
);
|
||||
|
||||
$this->app->singleton(
|
||||
\Core\Tenant\Services\EntitlementWebhookService::class,
|
||||
\Core\Tenant\Services\EntitlementWebhookService::class
|
||||
EntitlementWebhookService::class,
|
||||
EntitlementWebhookService::class
|
||||
);
|
||||
|
||||
$this->app->singleton(
|
||||
\Core\Tenant\Services\WorkspaceTeamService::class,
|
||||
\Core\Tenant\Services\WorkspaceTeamService::class
|
||||
WorkspaceTeamService::class,
|
||||
WorkspaceTeamService::class
|
||||
);
|
||||
|
||||
$this->registerBackwardCompatAliases();
|
||||
|
|
@ -89,31 +99,31 @@ class Boot extends ServiceProvider
|
|||
|
||||
protected function registerBackwardCompatAliases(): void
|
||||
{
|
||||
if (! class_exists(\App\Services\WorkspaceManager::class)) {
|
||||
if (! class_exists(WorkspaceManager::class)) {
|
||||
class_alias(
|
||||
\Core\Tenant\Services\WorkspaceManager::class,
|
||||
\App\Services\WorkspaceManager::class
|
||||
Services\WorkspaceManager::class,
|
||||
WorkspaceManager::class
|
||||
);
|
||||
}
|
||||
|
||||
if (! class_exists(\App\Services\UserStatsService::class)) {
|
||||
if (! class_exists(UserStatsService::class)) {
|
||||
class_alias(
|
||||
\Core\Tenant\Services\UserStatsService::class,
|
||||
\App\Services\UserStatsService::class
|
||||
Services\UserStatsService::class,
|
||||
UserStatsService::class
|
||||
);
|
||||
}
|
||||
|
||||
if (! class_exists(\App\Services\WorkspaceService::class)) {
|
||||
if (! class_exists(WorkspaceService::class)) {
|
||||
class_alias(
|
||||
\Core\Tenant\Services\WorkspaceService::class,
|
||||
\App\Services\WorkspaceService::class
|
||||
Services\WorkspaceService::class,
|
||||
WorkspaceService::class
|
||||
);
|
||||
}
|
||||
|
||||
if (! class_exists(\App\Services\WorkspaceCacheManager::class)) {
|
||||
if (! class_exists(WorkspaceCacheManager::class)) {
|
||||
class_alias(
|
||||
\Core\Tenant\Services\WorkspaceCacheManager::class,
|
||||
\App\Services\WorkspaceCacheManager::class
|
||||
Services\WorkspaceCacheManager::class,
|
||||
WorkspaceCacheManager::class
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace Core\Tenant\Controllers;
|
||||
|
||||
use Core\Front\Controller;
|
||||
use Core\Helpers\PrivacyHelper;
|
||||
use Core\Mod\Trees\Models\TreePlanting;
|
||||
use Core\Mod\Trees\Models\TreePlantingStats;
|
||||
|
|
@ -21,7 +22,7 @@ use Illuminate\Support\Facades\Cookie;
|
|||
*
|
||||
* On signup, PlantTreeForAgentReferral listener plants a tree for the referrer.
|
||||
*/
|
||||
class ReferralController extends \Core\Front\Controller
|
||||
class ReferralController extends Controller
|
||||
{
|
||||
/**
|
||||
* Cookie name for agent referral tracking.
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ use Core\Tenant\Models\User;
|
|||
use Core\Tenant\Models\Workspace;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
use Mod\Api\Controllers\Concerns\HasApiResponses;
|
||||
use Mod\Api\Controllers\Concerns\ResolvesWorkspace;
|
||||
use Mod\Api\Resources\PaginatedCollection;
|
||||
|
|
@ -133,7 +135,7 @@ class WorkspaceController extends Controller
|
|||
|
||||
// Generate slug if not provided
|
||||
if (empty($validated['slug'])) {
|
||||
$validated['slug'] = \Illuminate\Support\Str::slug($validated['name']).'-'.\Illuminate\Support\Str::random(6);
|
||||
$validated['slug'] = Str::slug($validated['name']).'-'.Str::random(6);
|
||||
}
|
||||
|
||||
// Set default domain
|
||||
|
|
@ -252,9 +254,9 @@ class WorkspaceController extends Controller
|
|||
|
||||
// Use a single transaction with optimised query:
|
||||
// Clear all defaults and set the new one in one operation using raw update
|
||||
\Illuminate\Support\Facades\DB::transaction(function () use ($user, $workspace) {
|
||||
DB::transaction(function () use ($user, $workspace) {
|
||||
// Clear all existing defaults for this user's hub workspaces
|
||||
\Illuminate\Support\Facades\DB::table('user_workspace')
|
||||
DB::table('user_workspace')
|
||||
->where('user_id', $user->id)
|
||||
->whereIn('workspace_id', function ($query) {
|
||||
$query->select('id')
|
||||
|
|
@ -264,7 +266,7 @@ class WorkspaceController extends Controller
|
|||
->update(['is_default' => false]);
|
||||
|
||||
// Set the new default
|
||||
\Illuminate\Support\Facades\DB::table('user_workspace')
|
||||
DB::table('user_workspace')
|
||||
->where('user_id', $user->id)
|
||||
->where('workspace_id', $workspace->id)
|
||||
->update(['is_default' => true]);
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core\Tenant\Database\Factories;
|
||||
|
||||
use Core\Tenant\Models\User;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\Core\Tenant\Models\User>
|
||||
* @extends Factory<User>
|
||||
*/
|
||||
class UserFactory extends Factory
|
||||
{
|
||||
|
|
@ -17,7 +20,7 @@ class UserFactory extends Factory
|
|||
* Uses the backward-compatible alias class to ensure type compatibility
|
||||
* with existing code that expects Mod\Tenant\Models\User.
|
||||
*/
|
||||
protected $model = \Core\Tenant\Models\User::class;
|
||||
protected $model = User::class;
|
||||
|
||||
/**
|
||||
* The current password being used by the factory.
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use Core\Tenant\Models\WaitlistEntry;
|
|||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\Core\Tenant\Models\WaitlistEntry>
|
||||
* @extends Factory<WaitlistEntry>
|
||||
*/
|
||||
class WaitlistEntryFactory extends Factory
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core\Tenant\Database\Factories;
|
||||
|
||||
use Core\Tenant\Models\Workspace;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\Core\Tenant\Models\Workspace>
|
||||
* @extends Factory<Workspace>
|
||||
*/
|
||||
class WorkspaceFactory extends Factory
|
||||
{
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use Illuminate\Database\Eloquent\Factories\Factory;
|
|||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\Core\Tenant\Models\WorkspaceInvitation>
|
||||
* @extends Factory<WorkspaceInvitation>
|
||||
*/
|
||||
class WorkspaceInvitationFactory extends Factory
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core\Tenant\Database\Seeders;
|
||||
|
||||
use Core\Mod\Web\Models\Page;
|
||||
use Core\Tenant\Models\Package;
|
||||
use Core\Tenant\Models\User;
|
||||
use Core\Tenant\Models\Workspace;
|
||||
|
|
@ -94,11 +97,11 @@ class DemoTestUserSeeder extends Seeder
|
|||
protected function createTestBioPage(Workspace $workspace, User $user): void
|
||||
{
|
||||
// Only create if Web Page model exists and no test page exists
|
||||
if (! class_exists(\Core\Mod\Web\Models\Page::class)) {
|
||||
if (! class_exists(Page::class)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$existingPage = \Core\Mod\Web\Models\Page::where('workspace_id', $workspace->id)
|
||||
$existingPage = Page::where('workspace_id', $workspace->id)
|
||||
->where('url', 'nyx-test')
|
||||
->first();
|
||||
|
||||
|
|
@ -106,7 +109,7 @@ class DemoTestUserSeeder extends Seeder
|
|||
return;
|
||||
}
|
||||
|
||||
\Core\Mod\Web\Models\Page::create([
|
||||
Page::create([
|
||||
'workspace_id' => $workspace->id,
|
||||
'user_id' => $user->id,
|
||||
'url' => 'nyx-test',
|
||||
|
|
@ -146,11 +149,11 @@ class DemoTestUserSeeder extends Seeder
|
|||
protected function createTestShortLink(Workspace $workspace, User $user): void
|
||||
{
|
||||
// Only create if Web Page model exists
|
||||
if (! class_exists(\Core\Mod\Web\Models\Page::class)) {
|
||||
if (! class_exists(Page::class)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$existingLink = \Core\Mod\Web\Models\Page::where('workspace_id', $workspace->id)
|
||||
$existingLink = Page::where('workspace_id', $workspace->id)
|
||||
->where('url', 'nyx-short')
|
||||
->first();
|
||||
|
||||
|
|
@ -158,7 +161,7 @@ class DemoTestUserSeeder extends Seeder
|
|||
return;
|
||||
}
|
||||
|
||||
\Core\Mod\Web\Models\Page::create([
|
||||
Page::create([
|
||||
'workspace_id' => $workspace->id,
|
||||
'user_id' => $user->id,
|
||||
'url' => 'nyx-short',
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core\Tenant\Database\Seeders;
|
||||
|
||||
use Core\Tenant\Models\Feature;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core\Tenant\Database\Seeders;
|
||||
|
||||
use Core\Tenant\Models\Feature;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core\Tenant\Database\Seeders;
|
||||
|
||||
use Core\Tenant\Models\Package;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core\Tenant\Database\Seeders;
|
||||
|
||||
use Core\Tenant\Enums\UserTier;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core\Tenant\Enums;
|
||||
|
||||
enum UserTier: string
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use Core\Tenant\Models\AccountDeletionRequest;
|
|||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Attachment;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
|
@ -53,7 +54,7 @@ class AccountDeletionRequested extends Mailable implements ShouldQueue
|
|||
/**
|
||||
* Get the attachments for the message.
|
||||
*
|
||||
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
|
||||
* @return array<int, Attachment>
|
||||
*/
|
||||
public function attachments(): array
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core\Tenant\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core\Tenant\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core\Tenant\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core\Tenant\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core\Tenant\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core\Tenant\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
|
|
|||
|
|
@ -4,8 +4,11 @@ declare(strict_types=1);
|
|||
|
||||
namespace Core\Tenant\Models;
|
||||
|
||||
use Core\Tenant\Database\Factories\UserFactory;
|
||||
use Core\Tenant\Enums\UserTier;
|
||||
use Illuminate\Auth\Notifications\VerifyEmail;
|
||||
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
|
|
@ -22,9 +25,9 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||
/**
|
||||
* Create a new factory instance for the model.
|
||||
*/
|
||||
protected static function newFactory(): \Core\Tenant\Database\Factories\UserFactory
|
||||
protected static function newFactory(): UserFactory
|
||||
{
|
||||
return \Core\Tenant\Database\Factories\UserFactory::new();
|
||||
return UserFactory::new();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -235,7 +238,7 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||
/**
|
||||
* Get all namespaces accessible by this user (owned + via workspaces).
|
||||
*/
|
||||
public function accessibleNamespaces(): \Illuminate\Database\Eloquent\Builder
|
||||
public function accessibleNamespaces(): Builder
|
||||
{
|
||||
return Namespace_::accessibleBy($this);
|
||||
}
|
||||
|
|
@ -269,7 +272,7 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||
*/
|
||||
public function sendEmailVerificationNotification(): void
|
||||
{
|
||||
$this->notify(new \Illuminate\Auth\Notifications\VerifyEmail);
|
||||
$this->notify(new VerifyEmail);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace Core\Tenant\Models;
|
||||
|
||||
use Core\Tenant\Database\Factories\UserTokenFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
|
@ -18,9 +19,9 @@ class UserToken extends Model
|
|||
{
|
||||
use HasFactory;
|
||||
|
||||
protected static function newFactory(): \Core\Tenant\Database\Factories\UserTokenFactory
|
||||
protected static function newFactory(): UserTokenFactory
|
||||
{
|
||||
return \Core\Tenant\Database\Factories\UserTokenFactory::new();
|
||||
return UserTokenFactory::new();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core\Tenant\Models;
|
||||
|
||||
use Core\Tenant\Database\Factories\WaitlistEntryFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
|
@ -13,9 +16,9 @@ class WaitlistEntry extends Model
|
|||
use HasFactory;
|
||||
use Notifiable;
|
||||
|
||||
protected static function newFactory(): \Core\Tenant\Database\Factories\WaitlistEntryFactory
|
||||
protected static function newFactory(): WaitlistEntryFactory
|
||||
{
|
||||
return \Core\Tenant\Database\Factories\WaitlistEntryFactory::new();
|
||||
return WaitlistEntryFactory::new();
|
||||
}
|
||||
|
||||
protected $fillable = [
|
||||
|
|
|
|||
|
|
@ -4,6 +4,39 @@ declare(strict_types=1);
|
|||
|
||||
namespace Core\Tenant\Models;
|
||||
|
||||
use App\Services\WorkspaceService;
|
||||
use Core\Mod\Analytics\Models\AnalyticsGoal;
|
||||
use Core\Mod\Analytics\Models\AnalyticsWebsite;
|
||||
use Core\Mod\Analytics\Models\Goal;
|
||||
use Core\Mod\Analytics\Models\Website;
|
||||
use Core\Mod\Api\Models\ApiKey;
|
||||
use Core\Mod\Api\Models\WebhookEndpoint;
|
||||
use Core\Mod\Content\Models\ContentAuthor;
|
||||
use Core\Mod\Content\Models\ContentItem;
|
||||
use Core\Mod\Notify\Models\PushCampaign;
|
||||
use Core\Mod\Notify\Models\PushFlow;
|
||||
use Core\Mod\Notify\Models\PushSegment;
|
||||
use Core\Mod\Notify\Models\PushWebsite;
|
||||
use Core\Mod\Social\Models\Account;
|
||||
use Core\Mod\Social\Models\Analytics;
|
||||
use Core\Mod\Social\Models\Audience;
|
||||
use Core\Mod\Social\Models\FacebookInsight;
|
||||
use Core\Mod\Social\Models\HashtagGroup;
|
||||
use Core\Mod\Social\Models\ImportedPost;
|
||||
use Core\Mod\Social\Models\InstagramInsight;
|
||||
use Core\Mod\Social\Models\Media;
|
||||
use Core\Mod\Social\Models\Metric;
|
||||
use Core\Mod\Social\Models\PinterestAnalytic;
|
||||
use Core\Mod\Social\Models\Post;
|
||||
use Core\Mod\Social\Models\PostingSchedule;
|
||||
use Core\Mod\Social\Models\Template;
|
||||
use Core\Mod\Social\Models\Variable;
|
||||
use Core\Mod\Social\Models\Webhook;
|
||||
use Core\Mod\Trees\Models\TreePlanting;
|
||||
use Core\Mod\Trust\Models\Campaign;
|
||||
use Core\Mod\Trust\Models\Notification;
|
||||
use Core\Tenant\Database\Factories\WorkspaceFactory;
|
||||
use Core\Tenant\Notifications\WorkspaceInvitationNotification;
|
||||
use Core\Tenant\Services\EntitlementResult;
|
||||
use Core\Tenant\Services\EntitlementService;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
|
@ -11,14 +44,19 @@ use Illuminate\Database\Eloquent\Model;
|
|||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||
use Illuminate\Support\Collection;
|
||||
use Mod\Commerce\Models\Invoice;
|
||||
use Mod\Commerce\Models\Order;
|
||||
use Mod\Commerce\Models\PaymentMethod;
|
||||
use Mod\Commerce\Models\Subscription;
|
||||
|
||||
class Workspace extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected static function newFactory(): \Core\Tenant\Database\Factories\WorkspaceFactory
|
||||
protected static function newFactory(): WorkspaceFactory
|
||||
{
|
||||
return \Core\Tenant\Database\Factories\WorkspaceFactory::new();
|
||||
return WorkspaceFactory::new();
|
||||
}
|
||||
|
||||
protected $fillable = [
|
||||
|
|
@ -235,7 +273,7 @@ class Workspace extends Model
|
|||
*/
|
||||
public function socialAccounts(): HasMany
|
||||
{
|
||||
return $this->hasMany(\Core\Mod\Social\Models\Account::class);
|
||||
return $this->hasMany(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -243,7 +281,7 @@ class Workspace extends Model
|
|||
*/
|
||||
public function socialPosts(): HasMany
|
||||
{
|
||||
return $this->hasMany(\Core\Mod\Social\Models\Post::class);
|
||||
return $this->hasMany(Post::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -251,7 +289,7 @@ class Workspace extends Model
|
|||
*/
|
||||
public function socialTemplates(): HasMany
|
||||
{
|
||||
return $this->hasMany(\Core\Mod\Social\Models\Template::class);
|
||||
return $this->hasMany(Template::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -259,7 +297,7 @@ class Workspace extends Model
|
|||
*/
|
||||
public function socialMedia(): HasMany
|
||||
{
|
||||
return $this->hasMany(\Core\Mod\Social\Models\Media::class);
|
||||
return $this->hasMany(Media::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -267,7 +305,7 @@ class Workspace extends Model
|
|||
*/
|
||||
public function socialHashtagGroups(): HasMany
|
||||
{
|
||||
return $this->hasMany(\Core\Mod\Social\Models\HashtagGroup::class);
|
||||
return $this->hasMany(HashtagGroup::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -275,7 +313,7 @@ class Workspace extends Model
|
|||
*/
|
||||
public function socialWebhooks(): HasMany
|
||||
{
|
||||
return $this->hasMany(\Core\Mod\Social\Models\Webhook::class);
|
||||
return $this->hasMany(Webhook::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -283,7 +321,7 @@ class Workspace extends Model
|
|||
*/
|
||||
public function socialAnalytics(): HasMany
|
||||
{
|
||||
return $this->hasMany(\Core\Mod\Social\Models\Analytics::class);
|
||||
return $this->hasMany(Analytics::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -291,7 +329,7 @@ class Workspace extends Model
|
|||
*/
|
||||
public function socialVariables(): HasMany
|
||||
{
|
||||
return $this->hasMany(\Core\Mod\Social\Models\Variable::class);
|
||||
return $this->hasMany(Variable::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -299,7 +337,7 @@ class Workspace extends Model
|
|||
*/
|
||||
public function socialPostingSchedule(): HasMany
|
||||
{
|
||||
return $this->hasMany(\Core\Mod\Social\Models\PostingSchedule::class);
|
||||
return $this->hasMany(PostingSchedule::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -307,7 +345,7 @@ class Workspace extends Model
|
|||
*/
|
||||
public function socialImportedPosts(): HasMany
|
||||
{
|
||||
return $this->hasMany(\Core\Mod\Social\Models\ImportedPost::class);
|
||||
return $this->hasMany(ImportedPost::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -315,7 +353,7 @@ class Workspace extends Model
|
|||
*/
|
||||
public function socialMetrics(): HasMany
|
||||
{
|
||||
return $this->hasMany(\Core\Mod\Social\Models\Metric::class);
|
||||
return $this->hasMany(Metric::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -323,7 +361,7 @@ class Workspace extends Model
|
|||
*/
|
||||
public function socialAudience(): HasMany
|
||||
{
|
||||
return $this->hasMany(\Core\Mod\Social\Models\Audience::class);
|
||||
return $this->hasMany(Audience::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -331,7 +369,7 @@ class Workspace extends Model
|
|||
*/
|
||||
public function socialFacebookInsights(): HasMany
|
||||
{
|
||||
return $this->hasMany(\Core\Mod\Social\Models\FacebookInsight::class);
|
||||
return $this->hasMany(FacebookInsight::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -339,7 +377,7 @@ class Workspace extends Model
|
|||
*/
|
||||
public function socialInstagramInsights(): HasMany
|
||||
{
|
||||
return $this->hasMany(\Core\Mod\Social\Models\InstagramInsight::class);
|
||||
return $this->hasMany(InstagramInsight::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -347,7 +385,7 @@ class Workspace extends Model
|
|||
*/
|
||||
public function socialPinterestAnalytics(): HasMany
|
||||
{
|
||||
return $this->hasMany(\Core\Mod\Social\Models\PinterestAnalytic::class);
|
||||
return $this->hasMany(PinterestAnalytic::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -376,7 +414,7 @@ class Workspace extends Model
|
|||
*/
|
||||
public function analyticsSites(): HasMany
|
||||
{
|
||||
return $this->hasMany(\Core\Mod\Analytics\Models\Website::class);
|
||||
return $this->hasMany(Website::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -384,7 +422,7 @@ class Workspace extends Model
|
|||
*/
|
||||
public function socialAnalyticsWebsites(): HasMany
|
||||
{
|
||||
return $this->hasMany(\Core\Mod\Analytics\Models\AnalyticsWebsite::class);
|
||||
return $this->hasMany(AnalyticsWebsite::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -392,7 +430,7 @@ class Workspace extends Model
|
|||
*/
|
||||
public function analyticsGoals(): HasMany
|
||||
{
|
||||
return $this->hasMany(\Core\Mod\Analytics\Models\Goal::class);
|
||||
return $this->hasMany(Goal::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -400,7 +438,7 @@ class Workspace extends Model
|
|||
*/
|
||||
public function socialAnalyticsGoals(): HasMany
|
||||
{
|
||||
return $this->hasMany(\Core\Mod\Analytics\Models\AnalyticsGoal::class);
|
||||
return $this->hasMany(AnalyticsGoal::class);
|
||||
}
|
||||
|
||||
// TrustHost Relationships
|
||||
|
|
@ -410,7 +448,7 @@ class Workspace extends Model
|
|||
*/
|
||||
public function trustWidgets(): HasMany
|
||||
{
|
||||
return $this->hasMany(\Core\Mod\Trust\Models\Campaign::class);
|
||||
return $this->hasMany(Campaign::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -418,7 +456,7 @@ class Workspace extends Model
|
|||
*/
|
||||
public function trustNotifications(): HasMany
|
||||
{
|
||||
return $this->hasMany(\Core\Mod\Trust\Models\Notification::class);
|
||||
return $this->hasMany(Notification::class);
|
||||
}
|
||||
|
||||
// NotifyHost Relationships
|
||||
|
|
@ -428,7 +466,7 @@ class Workspace extends Model
|
|||
*/
|
||||
public function notificationSites(): HasMany
|
||||
{
|
||||
return $this->hasMany(\Core\Mod\Notify\Models\PushWebsite::class);
|
||||
return $this->hasMany(PushWebsite::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -436,7 +474,7 @@ class Workspace extends Model
|
|||
*/
|
||||
public function pushCampaigns(): HasMany
|
||||
{
|
||||
return $this->hasMany(\Core\Mod\Notify\Models\PushCampaign::class);
|
||||
return $this->hasMany(PushCampaign::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -444,7 +482,7 @@ class Workspace extends Model
|
|||
*/
|
||||
public function pushFlows(): HasMany
|
||||
{
|
||||
return $this->hasMany(\Core\Mod\Notify\Models\PushFlow::class);
|
||||
return $this->hasMany(PushFlow::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -452,7 +490,7 @@ class Workspace extends Model
|
|||
*/
|
||||
public function pushSegments(): HasMany
|
||||
{
|
||||
return $this->hasMany(\Core\Mod\Notify\Models\PushSegment::class);
|
||||
return $this->hasMany(PushSegment::class);
|
||||
}
|
||||
|
||||
// API & Webhooks Relationships
|
||||
|
|
@ -462,7 +500,7 @@ class Workspace extends Model
|
|||
*/
|
||||
public function apiKeys(): HasMany
|
||||
{
|
||||
return $this->hasMany(\Core\Mod\Api\Models\ApiKey::class);
|
||||
return $this->hasMany(ApiKey::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -470,7 +508,7 @@ class Workspace extends Model
|
|||
*/
|
||||
public function webhookEndpoints(): HasMany
|
||||
{
|
||||
return $this->hasMany(\Core\Mod\Api\Models\WebhookEndpoint::class);
|
||||
return $this->hasMany(WebhookEndpoint::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -488,7 +526,7 @@ class Workspace extends Model
|
|||
*/
|
||||
public function treePlantings(): HasMany
|
||||
{
|
||||
return $this->hasMany(\Core\Mod\Trees\Models\TreePlanting::class);
|
||||
return $this->hasMany(TreePlanting::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -519,7 +557,7 @@ class Workspace extends Model
|
|||
*/
|
||||
public function contentItems(): HasMany
|
||||
{
|
||||
return $this->hasMany(\Core\Mod\Content\Models\ContentItem::class);
|
||||
return $this->hasMany(ContentItem::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -527,7 +565,7 @@ class Workspace extends Model
|
|||
*/
|
||||
public function contentAuthors(): HasMany
|
||||
{
|
||||
return $this->hasMany(\Core\Mod\Content\Models\ContentAuthor::class);
|
||||
return $this->hasMany(ContentAuthor::class);
|
||||
}
|
||||
|
||||
// Commerce Relationships (defined in app Mod\Commerce)
|
||||
|
|
@ -537,7 +575,7 @@ class Workspace extends Model
|
|||
*/
|
||||
public function subscriptions(): HasMany
|
||||
{
|
||||
return $this->hasMany(\Mod\Commerce\Models\Subscription::class);
|
||||
return $this->hasMany(Subscription::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -545,7 +583,7 @@ class Workspace extends Model
|
|||
*/
|
||||
public function invoices(): HasMany
|
||||
{
|
||||
return $this->hasMany(\Mod\Commerce\Models\Invoice::class);
|
||||
return $this->hasMany(Invoice::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -553,7 +591,7 @@ class Workspace extends Model
|
|||
*/
|
||||
public function paymentMethods(): HasMany
|
||||
{
|
||||
return $this->hasMany(\Mod\Commerce\Models\PaymentMethod::class);
|
||||
return $this->hasMany(PaymentMethod::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -561,7 +599,7 @@ class Workspace extends Model
|
|||
*/
|
||||
public function orders(): MorphMany
|
||||
{
|
||||
return $this->morphMany(\Mod\Commerce\Models\Order::class, 'orderable');
|
||||
return $this->morphMany(Order::class, 'orderable');
|
||||
}
|
||||
|
||||
// Helper Methods
|
||||
|
|
@ -579,12 +617,12 @@ class Workspace extends Model
|
|||
}
|
||||
|
||||
// Try to get from authenticated user's default workspace
|
||||
if (auth()->check() && auth()->user() instanceof \Core\Tenant\Models\User) {
|
||||
if (auth()->check() && auth()->user() instanceof User) {
|
||||
return auth()->user()->defaultHostWorkspace();
|
||||
}
|
||||
|
||||
// Try to resolve from subdomain via WorkspaceService
|
||||
$workspaceService = app(\App\Services\WorkspaceService::class);
|
||||
$workspaceService = app(WorkspaceService::class);
|
||||
$slug = $workspaceService->currentSlug();
|
||||
|
||||
return static::where('slug', $slug)->first();
|
||||
|
|
@ -609,7 +647,7 @@ class Workspace extends Model
|
|||
/**
|
||||
* Get usage summary for all features.
|
||||
*/
|
||||
public function getUsageSummary(): \Illuminate\Support\Collection
|
||||
public function getUsageSummary(): Collection
|
||||
{
|
||||
return app(EntitlementService::class)->getUsageSummary($this);
|
||||
}
|
||||
|
|
@ -675,7 +713,7 @@ class Workspace extends Model
|
|||
$existing->save();
|
||||
|
||||
// Send notification with the new plaintext token
|
||||
$existing->notify(new \Core\Tenant\Notifications\WorkspaceInvitationNotification($existing, $plaintextToken));
|
||||
$existing->notify(new WorkspaceInvitationNotification($existing, $plaintextToken));
|
||||
|
||||
return $existing;
|
||||
}
|
||||
|
|
@ -693,7 +731,7 @@ class Workspace extends Model
|
|||
]);
|
||||
|
||||
// Send notification with the plaintext token (not the hashed one)
|
||||
$invitation->notify(new \Core\Tenant\Notifications\WorkspaceInvitationNotification($invitation, $plaintextToken));
|
||||
$invitation->notify(new WorkspaceInvitationNotification($invitation, $plaintextToken));
|
||||
|
||||
return $invitation;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace Core\Tenant\Models;
|
||||
|
||||
use Core\Tenant\Database\Factories\WorkspaceInvitationFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
|
@ -16,9 +17,9 @@ class WorkspaceInvitation extends Model
|
|||
use HasFactory;
|
||||
use Notifiable;
|
||||
|
||||
protected static function newFactory(): \Core\Tenant\Database\Factories\WorkspaceInvitationFactory
|
||||
protected static function newFactory(): WorkspaceInvitationFactory
|
||||
{
|
||||
return \Core\Tenant\Database\Factories\WorkspaceInvitationFactory::new();
|
||||
return WorkspaceInvitationFactory::new();
|
||||
}
|
||||
|
||||
protected $fillable = [
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace Core\Tenant\Models;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
|
|
@ -20,10 +21,10 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|||
* @property int|null $team_id
|
||||
* @property array|null $custom_permissions
|
||||
* @property bool $is_default
|
||||
* @property \Carbon\Carbon|null $joined_at
|
||||
* @property Carbon|null $joined_at
|
||||
* @property int|null $invited_by
|
||||
* @property \Carbon\Carbon $created_at
|
||||
* @property \Carbon\Carbon $updated_at
|
||||
* @property Carbon $created_at
|
||||
* @property Carbon $updated_at
|
||||
*/
|
||||
class WorkspaceMember extends Model
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core\Tenant\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace Core\Tenant\Models;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Core\Tenant\Concerns\BelongsToWorkspace;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
|
@ -26,8 +27,8 @@ use Illuminate\Support\Str;
|
|||
* @property bool $is_system
|
||||
* @property string $colour
|
||||
* @property int $sort_order
|
||||
* @property \Carbon\Carbon $created_at
|
||||
* @property \Carbon\Carbon $updated_at
|
||||
* @property Carbon $created_at
|
||||
* @property Carbon $updated_at
|
||||
*/
|
||||
class WorkspaceTeam extends Model
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Core\Tenant\View\Modal\Admin\MemberManager;
|
||||
use Core\Tenant\View\Modal\Admin\TeamManager;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
/*
|
||||
|
|
@ -15,10 +17,10 @@ use Illuminate\Support\Facades\Route;
|
|||
|
||||
Route::middleware(['web', 'auth', 'admin.domain'])->prefix('admin/tenant')->name('hub.admin.tenant.')->group(function () {
|
||||
// Team Manager
|
||||
Route::get('/teams', \Core\Tenant\View\Modal\Admin\TeamManager::class)
|
||||
Route::get('/teams', TeamManager::class)
|
||||
->name('teams');
|
||||
|
||||
// Member Manager
|
||||
Route::get('/members', \Core\Tenant\View\Modal\Admin\MemberManager::class)
|
||||
Route::get('/members', MemberManager::class)
|
||||
->name('members');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ declare(strict_types=1);
|
|||
* Account management and workspace routes.
|
||||
*/
|
||||
|
||||
use Core\Tenant\Controllers\WorkspaceInvitationController;
|
||||
use Core\Tenant\View\Modal\Web\CancelDeletion;
|
||||
use Core\Tenant\View\Modal\Web\ConfirmDeletion;
|
||||
use Core\Tenant\View\Modal\Web\WorkspaceHome;
|
||||
|
|
@ -41,7 +42,7 @@ Route::prefix('account')->name('account.')->group(function () {
|
|||
|
|
||||
*/
|
||||
|
||||
Route::get('/workspace/invitation/{token}', \Core\Tenant\Controllers\WorkspaceInvitationController::class)
|
||||
Route::get('/workspace/invitation/{token}', WorkspaceInvitationController::class)
|
||||
->name('workspace.invitation.accept');
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core\Tenant\Services;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ use Core\Tenant\Jobs\DispatchEntitlementWebhook;
|
|||
use Core\Tenant\Models\EntitlementWebhook;
|
||||
use Core\Tenant\Models\EntitlementWebhookDelivery;
|
||||
use Core\Tenant\Models\Workspace;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Str;
|
||||
|
|
@ -425,7 +426,7 @@ class EntitlementWebhookService
|
|||
/**
|
||||
* Get webhooks for a workspace.
|
||||
*/
|
||||
public function getWebhooksForWorkspace(Workspace $workspace): \Illuminate\Database\Eloquent\Collection
|
||||
public function getWebhooksForWorkspace(Workspace $workspace): Collection
|
||||
{
|
||||
return EntitlementWebhook::query()
|
||||
->forWorkspace($workspace)
|
||||
|
|
@ -437,7 +438,7 @@ class EntitlementWebhookService
|
|||
/**
|
||||
* Get delivery history for a webhook.
|
||||
*/
|
||||
public function getDeliveryHistory(EntitlementWebhook $webhook, int $limit = 50): \Illuminate\Database\Eloquent\Collection
|
||||
public function getDeliveryHistory(EntitlementWebhook $webhook, int $limit = 50): Collection
|
||||
{
|
||||
return $webhook->deliveries()
|
||||
->latest('created_at')
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||
namespace Core\Tenant\Services;
|
||||
|
||||
use Core\Tenant\Enums\UserTier;
|
||||
use Core\Tenant\Jobs\ComputeUserStats;
|
||||
use Core\Tenant\Models\User;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
|
|
@ -44,7 +45,7 @@ class UserStatsService
|
|||
// For page loads, return cached data immediately and queue refresh
|
||||
if ($user->cached_stats) {
|
||||
// Queue background refresh
|
||||
dispatch(new \Core\Tenant\Jobs\ComputeUserStats($user->id))->onQueue('stats');
|
||||
dispatch(new ComputeUserStats($user->id))->onQueue('stats');
|
||||
|
||||
return $user->cached_stats;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,9 @@ namespace Core\Tenant\View\Modal\Admin;
|
|||
use Core\Tenant\Models\Workspace;
|
||||
use Core\Tenant\Models\WorkspaceMember;
|
||||
use Core\Tenant\Models\WorkspaceTeam;
|
||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Livewire\Attributes\Computed;
|
||||
use Livewire\Component;
|
||||
use Livewire\WithPagination;
|
||||
|
|
@ -287,7 +289,7 @@ class MemberManager extends Component
|
|||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
#[Computed]
|
||||
public function members(): \Illuminate\Contracts\Pagination\LengthAwarePaginator
|
||||
public function members(): LengthAwarePaginator
|
||||
{
|
||||
return WorkspaceMember::query()
|
||||
->with(['user', 'workspace', 'team', 'inviter'])
|
||||
|
|
@ -308,13 +310,13 @@ class MemberManager extends Component
|
|||
}
|
||||
|
||||
#[Computed]
|
||||
public function workspaces(): \Illuminate\Database\Eloquent\Collection
|
||||
public function workspaces(): Collection
|
||||
{
|
||||
return Workspace::orderBy('name')->get();
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
public function teamsForFilter(): \Illuminate\Database\Eloquent\Collection
|
||||
public function teamsForFilter(): Collection
|
||||
{
|
||||
$query = WorkspaceTeam::query();
|
||||
|
||||
|
|
@ -326,7 +328,7 @@ class MemberManager extends Component
|
|||
}
|
||||
|
||||
#[Computed]
|
||||
public function teamsForAssignment(): \Illuminate\Database\Eloquent\Collection
|
||||
public function teamsForAssignment(): Collection
|
||||
{
|
||||
if ($this->assignMemberId) {
|
||||
$member = WorkspaceMember::find($this->assignMemberId);
|
||||
|
|
@ -337,11 +339,11 @@ class MemberManager extends Component
|
|||
}
|
||||
}
|
||||
|
||||
return new \Illuminate\Database\Eloquent\Collection;
|
||||
return new Collection;
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
public function teamsForBulkAssignment(): \Illuminate\Database\Eloquent\Collection
|
||||
public function teamsForBulkAssignment(): Collection
|
||||
{
|
||||
// Only show teams from the current workspace filter
|
||||
if ($this->workspaceFilter) {
|
||||
|
|
@ -350,7 +352,7 @@ class MemberManager extends Component
|
|||
->get();
|
||||
}
|
||||
|
||||
return new \Illuminate\Database\Eloquent\Collection;
|
||||
return new Collection;
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@ use Core\Tenant\Models\Workspace;
|
|||
use Core\Tenant\Models\WorkspaceMember;
|
||||
use Core\Tenant\Models\WorkspaceTeam;
|
||||
use Core\Tenant\Services\WorkspaceTeamService;
|
||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Livewire\Attributes\Computed;
|
||||
use Livewire\Component;
|
||||
use Livewire\WithPagination;
|
||||
|
|
@ -219,7 +221,7 @@ class TeamManager extends Component
|
|||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
#[Computed]
|
||||
public function teams(): \Illuminate\Contracts\Pagination\LengthAwarePaginator
|
||||
public function teams(): LengthAwarePaginator
|
||||
{
|
||||
return WorkspaceTeam::query()
|
||||
->with(['workspace'])
|
||||
|
|
@ -239,7 +241,7 @@ class TeamManager extends Component
|
|||
}
|
||||
|
||||
#[Computed]
|
||||
public function workspaces(): \Illuminate\Database\Eloquent\Collection
|
||||
public function workspaces(): Collection
|
||||
{
|
||||
return Workspace::orderBy('name')->get();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,29 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core\Tenant\View\Modal\Admin;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Core\Mod\Analytics\Models\Website;
|
||||
use Core\Mod\Api\Models\ApiKey;
|
||||
use Core\Mod\Content\Models\ContentItem;
|
||||
use Core\Mod\Notify\Models\PushWebsite;
|
||||
use Core\Mod\Social\Models\Account;
|
||||
use Core\Mod\Social\Models\Post;
|
||||
use Core\Mod\Trust\Models\Campaign;
|
||||
use Core\Mod\Web\Models\Page;
|
||||
use Core\Mod\Web\Models\Project;
|
||||
use Core\Tenant\Models\Boost;
|
||||
use Core\Tenant\Models\EntitlementLog;
|
||||
use Core\Tenant\Models\Feature;
|
||||
use Core\Tenant\Models\Package;
|
||||
use Core\Tenant\Models\UsageRecord;
|
||||
use Core\Tenant\Models\User;
|
||||
use Core\Tenant\Models\Workspace;
|
||||
use Core\Tenant\Models\WorkspacePackage;
|
||||
use Core\Tenant\Services\EntitlementService;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Livewire\Attributes\Computed;
|
||||
use Livewire\Component;
|
||||
|
||||
|
|
@ -87,18 +107,18 @@ class WorkspaceDetails extends Component
|
|||
public function resourceCounts(): array
|
||||
{
|
||||
$counts = [];
|
||||
$schema = \Illuminate\Support\Facades\Schema::getFacadeRoot();
|
||||
$schema = Schema::getFacadeRoot();
|
||||
|
||||
$resources = [
|
||||
['relation' => 'bioPages', 'label' => 'Bio Pages', 'icon' => 'link', 'color' => 'blue', 'model' => \Core\Mod\Web\Models\Page::class],
|
||||
['relation' => 'bioProjects', 'label' => 'Bio Projects', 'icon' => 'folder', 'color' => 'indigo', 'model' => \Core\Mod\Web\Models\Project::class],
|
||||
['relation' => 'socialAccounts', 'label' => 'Social Accounts', 'icon' => 'share-nodes', 'color' => 'purple', 'model' => \Core\Mod\Social\Models\Account::class],
|
||||
['relation' => 'socialPosts', 'label' => 'Social Posts', 'icon' => 'paper-plane', 'color' => 'pink', 'model' => \Core\Mod\Social\Models\Post::class],
|
||||
['relation' => 'analyticsSites', 'label' => 'Analytics Sites', 'icon' => 'chart-line', 'color' => 'cyan', 'model' => \Core\Mod\Analytics\Models\Website::class],
|
||||
['relation' => 'trustWidgets', 'label' => 'Trust Campaigns', 'icon' => 'shield-check', 'color' => 'emerald', 'model' => \Core\Mod\Trust\Models\Campaign::class],
|
||||
['relation' => 'notificationSites', 'label' => 'Notification Sites', 'icon' => 'bell', 'color' => 'amber', 'model' => \Core\Mod\Notify\Models\PushWebsite::class],
|
||||
['relation' => 'contentItems', 'label' => 'Content Items', 'icon' => 'file-lines', 'color' => 'slate', 'model' => \Core\Mod\Content\Models\ContentItem::class],
|
||||
['relation' => 'apiKeys', 'label' => 'API Keys', 'icon' => 'key', 'color' => 'rose', 'model' => \Core\Mod\Api\Models\ApiKey::class],
|
||||
['relation' => 'bioPages', 'label' => 'Bio Pages', 'icon' => 'link', 'color' => 'blue', 'model' => Page::class],
|
||||
['relation' => 'bioProjects', 'label' => 'Bio Projects', 'icon' => 'folder', 'color' => 'indigo', 'model' => Project::class],
|
||||
['relation' => 'socialAccounts', 'label' => 'Social Accounts', 'icon' => 'share-nodes', 'color' => 'purple', 'model' => Account::class],
|
||||
['relation' => 'socialPosts', 'label' => 'Social Posts', 'icon' => 'paper-plane', 'color' => 'pink', 'model' => Post::class],
|
||||
['relation' => 'analyticsSites', 'label' => 'Analytics Sites', 'icon' => 'chart-line', 'color' => 'cyan', 'model' => Website::class],
|
||||
['relation' => 'trustWidgets', 'label' => 'Trust Campaigns', 'icon' => 'shield-check', 'color' => 'emerald', 'model' => Campaign::class],
|
||||
['relation' => 'notificationSites', 'label' => 'Notification Sites', 'icon' => 'bell', 'color' => 'amber', 'model' => PushWebsite::class],
|
||||
['relation' => 'contentItems', 'label' => 'Content Items', 'icon' => 'file-lines', 'color' => 'slate', 'model' => ContentItem::class],
|
||||
['relation' => 'apiKeys', 'label' => 'API Keys', 'icon' => 'key', 'color' => 'rose', 'model' => ApiKey::class],
|
||||
];
|
||||
|
||||
foreach ($resources as $resource) {
|
||||
|
|
@ -125,7 +145,7 @@ class WorkspaceDetails extends Component
|
|||
$activities = collect();
|
||||
|
||||
// Entitlement logs
|
||||
if (class_exists(\Core\Tenant\Models\EntitlementLog::class)) {
|
||||
if (class_exists(EntitlementLog::class)) {
|
||||
try {
|
||||
$logs = $this->workspace->entitlementLogs()
|
||||
->with('user', 'feature')
|
||||
|
|
@ -147,7 +167,7 @@ class WorkspaceDetails extends Component
|
|||
}
|
||||
|
||||
// Usage records
|
||||
if (class_exists(\Core\Tenant\Models\UsageRecord::class)) {
|
||||
if (class_exists(UsageRecord::class)) {
|
||||
try {
|
||||
$usage = $this->workspace->usageRecords()
|
||||
->with('user', 'feature')
|
||||
|
|
@ -325,7 +345,7 @@ class WorkspaceDetails extends Component
|
|||
#[Computed]
|
||||
public function allPackages()
|
||||
{
|
||||
return \Core\Tenant\Models\Package::active()
|
||||
return Package::active()
|
||||
->ordered()
|
||||
->get();
|
||||
}
|
||||
|
|
@ -333,7 +353,7 @@ class WorkspaceDetails extends Component
|
|||
#[Computed]
|
||||
public function allFeatures()
|
||||
{
|
||||
return \Core\Tenant\Models\Feature::active()
|
||||
return Feature::active()
|
||||
->orderBy('category')
|
||||
->orderBy('sort_order')
|
||||
->get();
|
||||
|
|
@ -403,7 +423,7 @@ class WorkspaceDetails extends Component
|
|||
public function resolvedEntitlements()
|
||||
{
|
||||
try {
|
||||
return app(\Core\Tenant\Services\EntitlementService::class)
|
||||
return app(EntitlementService::class)
|
||||
->getUsageSummary($this->workspace);
|
||||
} catch (\Exception $e) {
|
||||
return collect();
|
||||
|
|
@ -431,7 +451,7 @@ class WorkspaceDetails extends Component
|
|||
return;
|
||||
}
|
||||
|
||||
$package = \Core\Tenant\Models\Package::findOrFail($this->selectedPackageId);
|
||||
$package = Package::findOrFail($this->selectedPackageId);
|
||||
|
||||
// Check if already assigned
|
||||
$existing = $this->workspace->workspacePackages()
|
||||
|
|
@ -446,7 +466,7 @@ class WorkspaceDetails extends Component
|
|||
return;
|
||||
}
|
||||
|
||||
\Core\Tenant\Models\WorkspacePackage::create([
|
||||
WorkspacePackage::create([
|
||||
'workspace_id' => $this->workspace->id,
|
||||
'package_id' => $package->id,
|
||||
'status' => 'active',
|
||||
|
|
@ -461,7 +481,7 @@ class WorkspaceDetails extends Component
|
|||
|
||||
public function removePackage(int $workspacePackageId): void
|
||||
{
|
||||
$wp = \Core\Tenant\Models\WorkspacePackage::where('workspace_id', $this->workspace->id)
|
||||
$wp = WorkspacePackage::where('workspace_id', $this->workspace->id)
|
||||
->findOrFail($workspacePackageId);
|
||||
|
||||
$packageName = $wp->package?->name ?? 'Package';
|
||||
|
|
@ -474,7 +494,7 @@ class WorkspaceDetails extends Component
|
|||
|
||||
public function suspendPackage(int $workspacePackageId): void
|
||||
{
|
||||
$wp = \Core\Tenant\Models\WorkspacePackage::where('workspace_id', $this->workspace->id)
|
||||
$wp = WorkspacePackage::where('workspace_id', $this->workspace->id)
|
||||
->findOrFail($workspacePackageId);
|
||||
|
||||
$wp->suspend();
|
||||
|
|
@ -486,7 +506,7 @@ class WorkspaceDetails extends Component
|
|||
|
||||
public function reactivatePackage(int $workspacePackageId): void
|
||||
{
|
||||
$wp = \Core\Tenant\Models\WorkspacePackage::where('workspace_id', $this->workspace->id)
|
||||
$wp = WorkspacePackage::where('workspace_id', $this->workspace->id)
|
||||
->findOrFail($workspacePackageId);
|
||||
|
||||
$wp->reactivate();
|
||||
|
|
@ -523,7 +543,7 @@ class WorkspaceDetails extends Component
|
|||
return;
|
||||
}
|
||||
|
||||
$feature = \Core\Tenant\Models\Feature::where('code', $this->selectedFeatureCode)->first();
|
||||
$feature = Feature::where('code', $this->selectedFeatureCode)->first();
|
||||
|
||||
if (! $feature) {
|
||||
$this->actionMessage = 'Feature not found.';
|
||||
|
|
@ -534,26 +554,26 @@ class WorkspaceDetails extends Component
|
|||
|
||||
// Map type to boost type constant
|
||||
$boostType = match ($this->entitlementType) {
|
||||
'enable' => \Core\Tenant\Models\Boost::BOOST_TYPE_ENABLE,
|
||||
'add_limit' => \Core\Tenant\Models\Boost::BOOST_TYPE_ADD_LIMIT,
|
||||
'unlimited' => \Core\Tenant\Models\Boost::BOOST_TYPE_UNLIMITED,
|
||||
default => \Core\Tenant\Models\Boost::BOOST_TYPE_ENABLE,
|
||||
'enable' => Boost::BOOST_TYPE_ENABLE,
|
||||
'add_limit' => Boost::BOOST_TYPE_ADD_LIMIT,
|
||||
'unlimited' => Boost::BOOST_TYPE_UNLIMITED,
|
||||
default => Boost::BOOST_TYPE_ENABLE,
|
||||
};
|
||||
|
||||
$durationType = $this->entitlementDuration === 'permanent'
|
||||
? \Core\Tenant\Models\Boost::DURATION_PERMANENT
|
||||
: \Core\Tenant\Models\Boost::DURATION_DURATION;
|
||||
? Boost::DURATION_PERMANENT
|
||||
: Boost::DURATION_DURATION;
|
||||
|
||||
\Core\Tenant\Models\Boost::create([
|
||||
Boost::create([
|
||||
'workspace_id' => $this->workspace->id,
|
||||
'feature_code' => $this->selectedFeatureCode,
|
||||
'boost_type' => $boostType,
|
||||
'duration_type' => $durationType,
|
||||
'limit_value' => $this->entitlementType === 'add_limit' ? $this->entitlementLimit : null,
|
||||
'consumed_quantity' => 0,
|
||||
'status' => \Core\Tenant\Models\Boost::STATUS_ACTIVE,
|
||||
'status' => Boost::STATUS_ACTIVE,
|
||||
'starts_at' => now(),
|
||||
'expires_at' => $this->entitlementExpiresAt ? \Carbon\Carbon::parse($this->entitlementExpiresAt) : null,
|
||||
'expires_at' => $this->entitlementExpiresAt ? Carbon::parse($this->entitlementExpiresAt) : null,
|
||||
'metadata' => ['granted_by' => auth()->id(), 'granted_at' => now()->toDateTimeString()],
|
||||
]);
|
||||
|
||||
|
|
@ -565,7 +585,7 @@ class WorkspaceDetails extends Component
|
|||
|
||||
public function removeBoost(int $boostId): void
|
||||
{
|
||||
$boost = \Core\Tenant\Models\Boost::where('workspace_id', $this->workspace->id)
|
||||
$boost = Boost::where('workspace_id', $this->workspace->id)
|
||||
->findOrFail($boostId);
|
||||
|
||||
$featureCode = $boost->feature_code;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,20 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core\Tenant\View\Modal\Admin;
|
||||
|
||||
use Core\Mod\Analytics\Models\Website;
|
||||
use Core\Mod\Notify\Models\PushWebsite;
|
||||
use Core\Mod\Social\Models\Account;
|
||||
use Core\Mod\Trust\Models\Campaign;
|
||||
use Core\Mod\Web\Models\Page;
|
||||
use Core\Mod\Web\Models\Project;
|
||||
use Core\Tenant\Models\User;
|
||||
use Core\Tenant\Models\Workspace;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Str;
|
||||
use Livewire\Attributes\Computed;
|
||||
use Livewire\Component;
|
||||
use Livewire\WithPagination;
|
||||
|
|
@ -117,15 +127,15 @@ class WorkspaceManager extends Component
|
|||
|
||||
// Check each relation's model exists and has workspace_id column
|
||||
$checks = [
|
||||
'bioPages' => ['model' => \Core\Mod\Web\Models\Page::class, 'table' => 'pages'],
|
||||
'bioProjects' => ['model' => \Core\Mod\Web\Models\Project::class, 'table' => 'page_projects'],
|
||||
'socialAccounts' => ['model' => \Core\Mod\Social\Models\Account::class, 'table' => 'social_accounts'],
|
||||
'analyticsSites' => ['model' => \Core\Mod\Analytics\Models\Website::class, 'table' => 'analytics_websites'],
|
||||
'trustWidgets' => ['model' => \Core\Mod\Trust\Models\Campaign::class, 'table' => 'trust_campaigns'],
|
||||
'notificationSites' => ['model' => \Core\Mod\Notify\Models\PushWebsite::class, 'table' => 'push_websites'],
|
||||
'bioPages' => ['model' => Page::class, 'table' => 'pages'],
|
||||
'bioProjects' => ['model' => Project::class, 'table' => 'page_projects'],
|
||||
'socialAccounts' => ['model' => Account::class, 'table' => 'social_accounts'],
|
||||
'analyticsSites' => ['model' => Website::class, 'table' => 'analytics_websites'],
|
||||
'trustWidgets' => ['model' => Campaign::class, 'table' => 'trust_campaigns'],
|
||||
'notificationSites' => ['model' => PushWebsite::class, 'table' => 'push_websites'],
|
||||
];
|
||||
|
||||
$schema = \Illuminate\Support\Facades\Schema::getFacadeRoot();
|
||||
$schema = Schema::getFacadeRoot();
|
||||
|
||||
foreach ($checks as $relation => $info) {
|
||||
if (class_exists($info['model'])) {
|
||||
|
|
@ -153,16 +163,16 @@ class WorkspaceManager extends Component
|
|||
public function resourceTypes(): array
|
||||
{
|
||||
$types = [];
|
||||
$schema = \Illuminate\Support\Facades\Schema::getFacadeRoot();
|
||||
$schema = Schema::getFacadeRoot();
|
||||
|
||||
// Only include resource types for models that exist and have valid relations
|
||||
$checks = [
|
||||
'bio_pages' => ['model' => \Core\Mod\Web\Models\Page::class, 'table' => 'pages', 'label' => 'Bio Pages', 'relation' => 'bioPages', 'icon' => 'link'],
|
||||
'bio_projects' => ['model' => \Core\Mod\Web\Models\Project::class, 'table' => 'page_projects', 'label' => 'Bio Projects', 'relation' => 'bioProjects', 'icon' => 'folder'],
|
||||
'social_accounts' => ['model' => \Core\Mod\Social\Models\Account::class, 'table' => 'social_accounts', 'label' => 'Social Accounts', 'relation' => 'socialAccounts', 'icon' => 'share-nodes'],
|
||||
'analytics_sites' => ['model' => \Core\Mod\Analytics\Models\Website::class, 'table' => 'analytics_websites', 'label' => 'Analytics Sites', 'relation' => 'analyticsSites', 'icon' => 'chart-line'],
|
||||
'trust_widgets' => ['model' => \Core\Mod\Trust\Models\Campaign::class, 'table' => 'trust_campaigns', 'label' => 'Trust Campaigns', 'relation' => 'trustWidgets', 'icon' => 'shield-check'],
|
||||
'notification_sites' => ['model' => \Core\Mod\Notify\Models\PushWebsite::class, 'table' => 'push_websites', 'label' => 'Notification Sites', 'relation' => 'notificationSites', 'icon' => 'bell'],
|
||||
'bio_pages' => ['model' => Page::class, 'table' => 'pages', 'label' => 'Bio Pages', 'relation' => 'bioPages', 'icon' => 'link'],
|
||||
'bio_projects' => ['model' => Project::class, 'table' => 'page_projects', 'label' => 'Bio Projects', 'relation' => 'bioProjects', 'icon' => 'folder'],
|
||||
'social_accounts' => ['model' => Account::class, 'table' => 'social_accounts', 'label' => 'Social Accounts', 'relation' => 'socialAccounts', 'icon' => 'share-nodes'],
|
||||
'analytics_sites' => ['model' => Website::class, 'table' => 'analytics_websites', 'label' => 'Analytics Sites', 'relation' => 'analyticsSites', 'icon' => 'chart-line'],
|
||||
'trust_widgets' => ['model' => Campaign::class, 'table' => 'trust_campaigns', 'label' => 'Trust Campaigns', 'relation' => 'trustWidgets', 'icon' => 'shield-check'],
|
||||
'notification_sites' => ['model' => PushWebsite::class, 'table' => 'push_websites', 'label' => 'Notification Sites', 'relation' => 'notificationSites', 'icon' => 'bell'],
|
||||
];
|
||||
|
||||
foreach ($checks as $key => $info) {
|
||||
|
|
@ -530,7 +540,7 @@ class WorkspaceManager extends Component
|
|||
'icon' => 'link',
|
||||
'color' => 'blue',
|
||||
'fields' => ['name', 'slug'],
|
||||
'model' => \Core\Mod\Web\Models\Page::class,
|
||||
'model' => Page::class,
|
||||
'defaults' => ['type' => 'page', 'is_enabled' => true],
|
||||
],
|
||||
'social_accounts' => [
|
||||
|
|
@ -538,7 +548,7 @@ class WorkspaceManager extends Component
|
|||
'icon' => 'share-nodes',
|
||||
'color' => 'purple',
|
||||
'fields' => ['name'],
|
||||
'model' => \Core\Mod\Social\Models\Account::class,
|
||||
'model' => Account::class,
|
||||
'defaults' => ['provider' => 'manual', 'status' => 'active'],
|
||||
],
|
||||
'analytics_sites' => [
|
||||
|
|
@ -546,7 +556,7 @@ class WorkspaceManager extends Component
|
|||
'icon' => 'chart-line',
|
||||
'color' => 'cyan',
|
||||
'fields' => ['name', 'url'],
|
||||
'model' => \Core\Mod\Analytics\Models\Website::class,
|
||||
'model' => Website::class,
|
||||
'defaults' => ['tracking_enabled' => true, 'is_enabled' => true],
|
||||
],
|
||||
'trust_widgets' => [
|
||||
|
|
@ -554,7 +564,7 @@ class WorkspaceManager extends Component
|
|||
'icon' => 'shield-check',
|
||||
'color' => 'emerald',
|
||||
'fields' => ['name'],
|
||||
'model' => \Core\Mod\Trust\Models\Campaign::class,
|
||||
'model' => Campaign::class,
|
||||
'defaults' => ['status' => 'draft'],
|
||||
],
|
||||
'notification_sites' => [
|
||||
|
|
@ -562,7 +572,7 @@ class WorkspaceManager extends Component
|
|||
'icon' => 'bell',
|
||||
'color' => 'amber',
|
||||
'fields' => ['name', 'url'],
|
||||
'model' => \Core\Mod\Notify\Models\PushWebsite::class,
|
||||
'model' => PushWebsite::class,
|
||||
'defaults' => ['status' => 'active'],
|
||||
],
|
||||
];
|
||||
|
|
@ -615,7 +625,7 @@ class WorkspaceManager extends Component
|
|||
|
||||
// Add slug for bio pages
|
||||
if (in_array('slug', $config['fields']) && $this->provisionSlug) {
|
||||
$data['url'] = \Illuminate\Support\Str::slug($this->provisionSlug);
|
||||
$data['url'] = Str::slug($this->provisionSlug);
|
||||
}
|
||||
|
||||
// Add URL-related fields if applicable
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core\Tenant\View\Modal\Web;
|
||||
|
||||
use Core\Tenant\Models\AccountDeletionRequest;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core\Tenant\View\Modal\Web;
|
||||
|
||||
use Core\Tenant\Models\AccountDeletionRequest;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core\Tenant\View\Modal\Web;
|
||||
|
||||
use Core\Mod\Content\Services\ContentRender;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core\Tenant\Tests\Feature;
|
||||
|
||||
use Core\Tenant\Models\User;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Core\Api\RateLimit\RateLimit;
|
||||
use Core\Tenant\Controllers\EntitlementApiController;
|
||||
use Core\Tenant\Models\EntitlementLog;
|
||||
use Core\Tenant\Models\Feature;
|
||||
use Core\Tenant\Models\Package;
|
||||
|
|
@ -10,10 +12,11 @@ use Core\Tenant\Models\Workspace;
|
|||
use Core\Tenant\Models\WorkspacePackage;
|
||||
use Core\Tenant\Services\EntitlementService;
|
||||
use Illuminate\Auth\Events\Registered;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
|
||||
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
beforeEach(function () {
|
||||
Cache::flush();
|
||||
|
|
@ -1076,8 +1079,8 @@ describe('Error Response Format', function () {
|
|||
|
||||
describe('Rate Limiting', function () {
|
||||
it('controller has rate limit attribute', function () {
|
||||
$reflection = new \ReflectionClass(\Core\Tenant\Controllers\EntitlementApiController::class);
|
||||
$attributes = $reflection->getAttributes(\Core\Api\RateLimit\RateLimit::class);
|
||||
$reflection = new ReflectionClass(EntitlementApiController::class);
|
||||
$attributes = $reflection->getAttributes(RateLimit::class);
|
||||
|
||||
expect($attributes)->toHaveCount(1);
|
||||
|
||||
|
|
|
|||
|
|
@ -15,9 +15,11 @@ use Core\Tenant\Models\Workspace;
|
|||
use Core\Tenant\Models\WorkspacePackage;
|
||||
use Core\Tenant\Services\EntitlementResult;
|
||||
use Core\Tenant\Services\EntitlementService;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
beforeEach(function () {
|
||||
// Clear cache before each test
|
||||
|
|
@ -426,7 +428,7 @@ describe('EntitlementService', function () {
|
|||
|
||||
$summary = $this->service->getUsageSummary($this->workspace);
|
||||
|
||||
expect($summary)->toBeInstanceOf(\Illuminate\Support\Collection::class)
|
||||
expect($summary)->toBeInstanceOf(Collection::class)
|
||||
->and($summary->has('ai'))->toBeTrue()
|
||||
->and($summary->has('tier'))->toBeTrue()
|
||||
->and($summary->has('social'))->toBeTrue();
|
||||
|
|
@ -1101,7 +1103,7 @@ describe('Namespace Entitlements', function () {
|
|||
|
||||
$summary = $this->service->getNamespaceUsageSummary($this->userNamespace);
|
||||
|
||||
expect($summary)->toBeInstanceOf(\Illuminate\Support\Collection::class)
|
||||
expect($summary)->toBeInstanceOf(Collection::class)
|
||||
->and($summary->has('content'))->toBeTrue()
|
||||
->and($summary->has('features'))->toBeTrue();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,16 +2,18 @@
|
|||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Core\Mod\Api\Guards\AccessTokenGuard;
|
||||
use Core\Tenant\Models\User;
|
||||
use Core\Tenant\Models\UserToken;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
test('can authenticate with valid bearer token', function () {
|
||||
$user = User::factory()->create();
|
||||
$result = $user->createToken('Test Token');
|
||||
|
||||
// Test the guard directly by invoking it with a mock request
|
||||
$guard = new \Core\Mod\Api\Guards\AccessTokenGuard(app('auth'));
|
||||
$request = \Illuminate\Http\Request::create('/test', 'GET');
|
||||
$guard = new AccessTokenGuard(app('auth'));
|
||||
$request = Request::create('/test', 'GET');
|
||||
$request->headers->set('Authorization', "Bearer {$result['token']}");
|
||||
|
||||
$authenticatedUser = $guard($request);
|
||||
|
|
@ -57,8 +59,8 @@ test('token last_used_at is updated on successful authentication', function () {
|
|||
expect($tokenModel->last_used_at)->toBeNull();
|
||||
|
||||
// Test the guard directly by invoking it with a mock request
|
||||
$guard = new \Core\Mod\Api\Guards\AccessTokenGuard(app('auth'));
|
||||
$request = \Illuminate\Http\Request::create('/test', 'GET');
|
||||
$guard = new AccessTokenGuard(app('auth'));
|
||||
$request = Request::create('/test', 'GET');
|
||||
$request->headers->set('Authorization', "Bearer {$result['token']}");
|
||||
|
||||
$guard($request);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core\Tenant\Tests\Feature;
|
||||
|
||||
use Core\Mod\Hub\View\Modal\Admin\Profile;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Core\Tenant\Models\Boost;
|
||||
use Core\Tenant\Models\EntitlementLog;
|
||||
use Core\Tenant\Models\Feature;
|
||||
|
|
@ -9,10 +11,11 @@ use Core\Tenant\Models\User;
|
|||
use Core\Tenant\Models\Workspace;
|
||||
use Core\Tenant\Notifications\BoostExpiredNotification;
|
||||
use Core\Tenant\Services\EntitlementService;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
beforeEach(function () {
|
||||
Cache::flush();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core\Tenant\Tests\Feature;
|
||||
|
||||
use Core\Mod\Hub\View\Modal\Admin\Settings;
|
||||
|
|
|
|||
|
|
@ -2,9 +2,13 @@
|
|||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Core\Tenant\Models\User;
|
||||
use Core\Tenant\Models\UserTwoFactorAuth;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
beforeEach(function () {
|
||||
Cache::flush();
|
||||
|
|
@ -15,7 +19,7 @@ describe('TwoFactorAuthenticatable Trait', function () {
|
|||
describe('twoFactorAuth() relationship', function () {
|
||||
it('returns HasOne relationship', function () {
|
||||
expect($this->user->twoFactorAuth())->toBeInstanceOf(
|
||||
\Illuminate\Database\Eloquent\Relations\HasOne::class
|
||||
HasOne::class
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -315,7 +319,7 @@ describe('UserTwoFactorAuth Model', function () {
|
|||
'recovery_codes' => $codes,
|
||||
]);
|
||||
|
||||
expect($twoFactorAuth->recovery_codes)->toBeInstanceOf(\Illuminate\Support\Collection::class)
|
||||
expect($twoFactorAuth->recovery_codes)->toBeInstanceOf(Collection::class)
|
||||
->and($twoFactorAuth->recovery_codes->toArray())->toBe($codes);
|
||||
});
|
||||
|
||||
|
|
@ -329,7 +333,7 @@ describe('UserTwoFactorAuth Model', function () {
|
|||
'confirmed_at' => $confirmedAt,
|
||||
]);
|
||||
|
||||
expect($twoFactorAuth->confirmed_at)->toBeInstanceOf(\Carbon\Carbon::class);
|
||||
expect($twoFactorAuth->confirmed_at)->toBeInstanceOf(Carbon::class);
|
||||
});
|
||||
|
||||
it('encrypts secret at rest', function () {
|
||||
|
|
@ -348,7 +352,7 @@ describe('UserTwoFactorAuth Model', function () {
|
|||
|
||||
// But the raw database value should be encrypted (base64 JSON with iv, value, mac)
|
||||
// Note: DB column is 'secret', not 'secret_key'
|
||||
$rawValue = \Illuminate\Support\Facades\DB::table('user_two_factor_auth')
|
||||
$rawValue = DB::table('user_two_factor_auth')
|
||||
->where('id', $twoFactorAuth->id)
|
||||
->value('secret');
|
||||
|
||||
|
|
@ -372,7 +376,7 @@ describe('UserTwoFactorAuth Model', function () {
|
|||
expect($twoFactorAuth->recovery_codes->toArray())->toBe($codes);
|
||||
|
||||
// But the raw database value should be encrypted
|
||||
$rawValue = \Illuminate\Support\Facades\DB::table('user_two_factor_auth')
|
||||
$rawValue = DB::table('user_two_factor_auth')
|
||||
->where('id', $twoFactorAuth->id)
|
||||
->value('recovery_codes');
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use Core\Tenant\Notifications\WaitlistInviteNotification;
|
|||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
use Illuminate\Support\Str;
|
||||
use Livewire\Livewire;
|
||||
use Website\Host\View\Modal\Waitlist;
|
||||
|
||||
|
|
@ -130,7 +131,7 @@ describe('Waitlist Entry Model', function () {
|
|||
expect($entry->invite_code)->toBeNull();
|
||||
|
||||
$entry->update([
|
||||
'invite_code' => \Illuminate\Support\Str::random(16),
|
||||
'invite_code' => Str::random(16),
|
||||
'invited_at' => now(),
|
||||
]);
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ use Core\Tenant\Scopes\WorkspaceScope;
|
|||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Str;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
|
|
@ -208,7 +209,7 @@ class WorkspaceSecurityTest extends TestCase
|
|||
$this->expectExceptionMessage('create');
|
||||
|
||||
Account::create([
|
||||
'uuid' => \Illuminate\Support\Str::uuid(),
|
||||
'uuid' => Str::uuid(),
|
||||
'provider' => 'twitter',
|
||||
'provider_id' => '12345',
|
||||
'name' => 'Test Account',
|
||||
|
|
@ -223,7 +224,7 @@ class WorkspaceSecurityTest extends TestCase
|
|||
|
||||
// Should succeed because workspace_id is explicitly provided
|
||||
$account = Account::create([
|
||||
'uuid' => \Illuminate\Support\Str::uuid(),
|
||||
'uuid' => Str::uuid(),
|
||||
'workspace_id' => $this->workspace->id,
|
||||
'provider' => 'twitter',
|
||||
'provider_id' => '12345',
|
||||
|
|
@ -240,7 +241,7 @@ class WorkspaceSecurityTest extends TestCase
|
|||
request()->attributes->set('workspace_model', $this->workspace);
|
||||
|
||||
$account = Account::create([
|
||||
'uuid' => \Illuminate\Support\Str::uuid(),
|
||||
'uuid' => Str::uuid(),
|
||||
'provider' => 'twitter',
|
||||
'provider_id' => '12345',
|
||||
'name' => 'Test Account',
|
||||
|
|
|
|||
|
|
@ -1,12 +1,16 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core\Tenant\Tests\Feature;
|
||||
|
||||
use Core\Mod\Analytics\Models\Website;
|
||||
use Core\Mod\Social\Models\Account;
|
||||
use Core\Tenant\Models\User;
|
||||
use Core\Tenant\Models\Workspace;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Str;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
|
|
@ -45,12 +49,12 @@ class WorkspaceTenancyTest extends TestCase
|
|||
$workspace = Workspace::factory()->create();
|
||||
|
||||
// Test that all relationship methods exist and return correct type
|
||||
$this->assertInstanceOf(\Illuminate\Database\Eloquent\Relations\HasMany::class, $workspace->socialAccounts());
|
||||
$this->assertInstanceOf(\Illuminate\Database\Eloquent\Relations\HasMany::class, $workspace->socialPosts());
|
||||
$this->assertInstanceOf(\Illuminate\Database\Eloquent\Relations\HasMany::class, $workspace->analyticsSites());
|
||||
$this->assertInstanceOf(\Illuminate\Database\Eloquent\Relations\HasMany::class, $workspace->trustWidgets());
|
||||
$this->assertInstanceOf(\Illuminate\Database\Eloquent\Relations\HasMany::class, $workspace->notificationSites());
|
||||
$this->assertInstanceOf(\Illuminate\Database\Eloquent\Relations\HasMany::class, $workspace->pushCampaigns());
|
||||
$this->assertInstanceOf(HasMany::class, $workspace->socialAccounts());
|
||||
$this->assertInstanceOf(HasMany::class, $workspace->socialPosts());
|
||||
$this->assertInstanceOf(HasMany::class, $workspace->analyticsSites());
|
||||
$this->assertInstanceOf(HasMany::class, $workspace->trustWidgets());
|
||||
$this->assertInstanceOf(HasMany::class, $workspace->notificationSites());
|
||||
$this->assertInstanceOf(HasMany::class, $workspace->pushCampaigns());
|
||||
// NOTE: bioPages relationship has been moved to Host UK app's Mod\Bio module
|
||||
}
|
||||
|
||||
|
|
@ -114,7 +118,7 @@ class WorkspaceTenancyTest extends TestCase
|
|||
// When creating a model with BelongsToWorkspace trait,
|
||||
// it should auto-assign the current user's workspace
|
||||
$account = Account::create([
|
||||
'uuid' => \Illuminate\Support\Str::uuid(),
|
||||
'uuid' => Str::uuid(),
|
||||
'provider' => 'twitter',
|
||||
'provider_id' => '12345',
|
||||
'name' => 'Test Account',
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace Tests;
|
||||
|
||||
use Core\Tenant\Boot;
|
||||
use Orchestra\Testbench\TestCase as BaseTestCase;
|
||||
|
||||
abstract class TestCase extends BaseTestCase
|
||||
|
|
@ -11,7 +12,7 @@ abstract class TestCase extends BaseTestCase
|
|||
protected function getPackageProviders($app): array
|
||||
{
|
||||
return [
|
||||
\Core\Tenant\Boot::class,
|
||||
Boot::class,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue