Page MenuHomePhorge

D3307.1775218832.diff
No OneTemporary

Authored By
Unknown
Size
15 KB
Referenced Files
None
Subscribers
None

D3307.1775218832.diff

diff --git a/src/app/Console/Command.php b/src/app/Console/Command.php
--- a/src/app/Console/Command.php
+++ b/src/app/Console/Command.php
@@ -126,17 +126,6 @@
return $model;
}
- $modelsWithTenant = [
- \App\Discount::class,
- \App\Domain::class,
- \App\Group::class,
- \App\Package::class,
- \App\Plan::class,
- \App\Resource::class,
- \App\Sku::class,
- \App\User::class,
- ];
-
$modelsWithOwner = [
\App\Wallet::class,
];
@@ -144,7 +133,7 @@
$tenantId = \config('app.tenant_id');
// Add tenant filter
- if (in_array($objectClass, $modelsWithTenant)) {
+ if (in_array(\App\Traits\BelongsToTenantTrait::class, class_uses($objectClass))) {
$model = $model->withEnvTenantContext();
} elseif (in_array($objectClass, $modelsWithOwner)) {
$model = $model->whereExists(function ($query) use ($tenantId) {
@@ -171,6 +160,19 @@
return $this->getObject(\App\Resource::class, $resource, 'email', $withDeleted);
}
+ /**
+ * Find a shared folder.
+ *
+ * @param string $folder Folder ID or email
+ * @param bool $withDeleted Include deleted
+ *
+ * @return \App\SharedFolder|null
+ */
+ public function getSharedFolder($folder, $withDeleted = false)
+ {
+ return $this->getObject(\App\SharedFolder::class, $folder, 'email', $withDeleted);
+ }
+
/**
* Find the user.
*
diff --git a/src/app/Console/Commands/Scalpel/SharedFolder/CreateCommand.php b/src/app/Console/Commands/Scalpel/SharedFolder/CreateCommand.php
new file mode 100644
--- /dev/null
+++ b/src/app/Console/Commands/Scalpel/SharedFolder/CreateCommand.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace App\Console\Commands\Scalpel\SharedFolder;
+
+use App\Console\ObjectCreateCommand;
+
+class CreateCommand extends ObjectCreateCommand
+{
+ protected $hidden = true;
+
+ protected $commandPrefix = 'scalpel';
+ protected $objectClass = \App\SharedFolder::class;
+ protected $objectName = 'folder';
+ protected $objectTitle = 'email';
+}
diff --git a/src/app/Console/Commands/Scalpel/SharedFolder/ReadCommand.php b/src/app/Console/Commands/Scalpel/SharedFolder/ReadCommand.php
new file mode 100644
--- /dev/null
+++ b/src/app/Console/Commands/Scalpel/SharedFolder/ReadCommand.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace App\Console\Commands\Scalpel\SharedFolder;
+
+use App\Console\ObjectReadCommand;
+
+class ReadCommand extends ObjectReadCommand
+{
+ protected $hidden = true;
+
+ protected $commandPrefix = 'scalpel';
+ protected $objectClass = \App\SharedFolder::class;
+ protected $objectName = 'folder';
+ protected $objectTitle = 'email';
+}
diff --git a/src/app/Console/Commands/Scalpel/SharedFolder/UpdateCommand.php b/src/app/Console/Commands/Scalpel/SharedFolder/UpdateCommand.php
new file mode 100644
--- /dev/null
+++ b/src/app/Console/Commands/Scalpel/SharedFolder/UpdateCommand.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace App\Console\Commands\Scalpel\SharedFolder;
+
+use App\Console\ObjectUpdateCommand;
+
+class UpdateCommand extends ObjectUpdateCommand
+{
+ protected $hidden = true;
+
+ protected $commandPrefix = 'scalpel';
+ protected $objectClass = \App\SharedFolder::class;
+ protected $objectName = 'folder';
+ protected $objectTitle = 'email';
+}
diff --git a/src/app/Console/Commands/SharedFolder/CreateCommand.php b/src/app/Console/Commands/SharedFolder/CreateCommand.php
new file mode 100644
--- /dev/null
+++ b/src/app/Console/Commands/SharedFolder/CreateCommand.php
@@ -0,0 +1,101 @@
+<?php
+
+namespace App\Console\Commands\SharedFolder;
+
+use App\Console\Command;
+use App\SharedFolder;
+use App\Rules\SharedFolderName;
+use App\Rules\SharedFolderType;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Validator;
+
+/**
+ * Create a shared folder.
+ *
+ * @see \App\Console\Commands\Scalpel\SharedFolder\CreateCommand
+ */
+class CreateCommand extends Command
+{
+ /**
+ * The name and signature of the console command.
+ *
+ * @var string
+ */
+ protected $signature = 'sharedfolder:create {domain} {name} {--type=} {--acl=*}';
+
+ /**
+ * The console command description.
+ *
+ * @var string
+ */
+ protected $description = "Create a shared folder.";
+
+ /**
+ * Execute the console command.
+ *
+ * @return mixed
+ */
+ public function handle()
+ {
+ $domainName = $this->argument('domain');
+ $name = $this->argument('name');
+ $type = $this->option('type');
+ $acl = $this->option('acl');
+
+ if (empty($type)) {
+ $type = 'mail';
+ }
+
+ $domain = $this->getDomain($domainName);
+
+ if (!$domain) {
+ $this->error("No such domain {$domainName}.");
+ return 1;
+ }
+
+ if ($domain->isPublic()) {
+ $this->error("Domain {$domainName} is public.");
+ return 1;
+ }
+
+ $owner = $domain->wallet()->owner;
+
+ // Validate folder name and type
+ $rules = [
+ 'name' => ['required', 'string', new SharedFolderName($owner, $domain->namespace)],
+ 'type' => ['required', 'string', new SharedFolderType()]
+ ];
+
+ $v = Validator::make(['name' => $name, 'type' => $type], $rules);
+
+ if ($v->fails()) {
+ $this->error($v->errors()->all()[0]);
+ return 1;
+ }
+
+ DB::beginTransaction();
+
+ // Create the shared folder
+ $folder = new SharedFolder();
+ $folder->name = $name;
+ $folder->type = $type;
+ $folder->domain = $domainName;
+ $folder->save();
+
+ $folder->assignToWallet($owner->wallets->first());
+
+ if (!empty($acl)) {
+ $errors = $folder->setConfig(['acl' => $acl]);
+
+ if (!empty($errors)) {
+ $this->error("Invalid --acl entry.");
+ DB::rollBack();
+ return 1;
+ }
+ }
+
+ DB::commit();
+
+ $this->info($folder->id);
+ }
+}
diff --git a/src/app/Console/Commands/SharedFolder/DeleteCommand.php b/src/app/Console/Commands/SharedFolder/DeleteCommand.php
new file mode 100644
--- /dev/null
+++ b/src/app/Console/Commands/SharedFolder/DeleteCommand.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace App\Console\Commands\SharedFolder;
+
+use App\Console\Command;
+
+class DeleteCommand extends Command
+{
+ /**
+ * The name and signature of the console command.
+ *
+ * @var string
+ */
+ protected $signature = 'sharedfolder:delete {folder}';
+
+ /**
+ * The console command description.
+ *
+ * @var string
+ */
+ protected $description = "Delete a shared folder.";
+
+ /**
+ * Execute the console command.
+ *
+ * @return mixed
+ */
+ public function handle()
+ {
+ $input = $this->argument('folder');
+ $folder = $this->getSharedFolder($input);
+
+ if (empty($folder)) {
+ $this->error("Shared folder {$input} does not exist.");
+ return 1;
+ }
+
+ $folder->delete();
+ }
+}
diff --git a/src/app/Console/Commands/SharedFolder/ForceDeleteCommand.php b/src/app/Console/Commands/SharedFolder/ForceDeleteCommand.php
new file mode 100644
--- /dev/null
+++ b/src/app/Console/Commands/SharedFolder/ForceDeleteCommand.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace App\Console\Commands\SharedFolder;
+
+use App\Console\Command;
+use Illuminate\Support\Facades\DB;
+
+class ForceDeleteCommand extends Command
+{
+ /**
+ * The name and signature of the console command.
+ *
+ * @var string
+ */
+ protected $signature = 'sharedfolder:force-delete {folder}';
+
+ /**
+ * The console command description.
+ *
+ * @var string
+ */
+ protected $description = 'Delete a shared folder for realz';
+
+ /**
+ * Execute the console command.
+ *
+ * @return mixed
+ */
+ public function handle()
+ {
+ $folder = $this->getSharedFolder($this->argument('folder'), true);
+
+ if (!$folder) {
+ $this->error("Shared folder not found.");
+ return 1;
+ }
+
+ if (!$folder->trashed()) {
+ $this->error("The shared folder is not yet deleted.");
+ return 1;
+ }
+
+ DB::beginTransaction();
+ $folder->forceDelete();
+ DB::commit();
+ }
+}
diff --git a/src/tests/Feature/Console/SharedFolder/CreateTest.php b/src/tests/Feature/Console/SharedFolder/CreateTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Feature/Console/SharedFolder/CreateTest.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace Tests\Feature\Console\SharedFolder;
+
+use App\SharedFolder;
+use Illuminate\Support\Facades\Queue;
+use Tests\TestCase;
+
+class CreateTest extends TestCase
+{
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ if ($folder = SharedFolder::withTrashed()->where('name', 'Tasks')->first()) {
+ $folder->forceDelete();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function tearDown(): void
+ {
+ if ($folder = SharedFolder::withTrashed()->where('name', 'Tasks')->first()) {
+ $folder->forceDelete();
+ }
+
+ parent::tearDown();
+ }
+
+ /**
+ * Test command runs
+ */
+ public function testHandle(): void
+ {
+ Queue::fake();
+
+ // Warning: We're not using artisan() here, as this will not
+ // allow us to test "empty output" cases
+
+ $user = $this->getTestUser('john@kolab.org');
+
+ // Domain not existing
+ $code = \Artisan::call("sharedfolder:create unknown.org test");
+ $output = trim(\Artisan::output());
+ $this->assertSame(1, $code);
+ $this->assertSame("No such domain unknown.org.", $output);
+
+ // Public domain not allowed
+ $code = \Artisan::call("sharedfolder:create kolabnow.com Test --type=event");
+ $output = trim(\Artisan::output());
+ $this->assertSame(1, $code);
+ $this->assertSame("Domain kolabnow.com is public.", $output);
+
+ // Existing folder
+ $code = \Artisan::call("sharedfolder:create kolab.org Calendar");
+ $output = trim(\Artisan::output());
+ $this->assertSame(1, $code);
+ $this->assertSame("The specified name is not available.", $output);
+
+ // Invalid type
+ $code = \Artisan::call("sharedfolder:create kolab.org Test --type=unknown");
+ $output = trim(\Artisan::output());
+ $this->assertSame(1, $code);
+ $this->assertSame("The specified type is invalid.", $output);
+
+ // Invalid acl
+ $code = \Artisan::call("sharedfolder:create kolab.org Test --type=task --acl=\"anyone,unknown\"");
+ $output = trim(\Artisan::output());
+
+ $this->assertSame(1, $code);
+ $this->assertSame("Invalid --acl entry.", $output);
+ $this->assertSame(0, SharedFolder::where('name', 'Test')->count());
+
+ // Create a folder
+ $acl = '--acl="anyone, read-only" --acl="jack@kolab.org, full"';
+ $code = \Artisan::call("sharedfolder:create kolab.org Tasks --type=task $acl");
+ $output = trim(\Artisan::output());
+
+ $folder = SharedFolder::find($output);
+
+ $this->assertSame(0, $code);
+ $this->assertSame('Tasks', $folder->name);
+ $this->assertSame('task', $folder->type);
+ $this->assertSame($user->wallets->first()->id, $folder->wallet()->id);
+ $this->assertSame(['anyone, read-only', 'jack@kolab.org, full'], $folder->getConfig()['acl']);
+ }
+}
diff --git a/src/tests/Feature/Console/SharedFolder/DeleteTest.php b/src/tests/Feature/Console/SharedFolder/DeleteTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Feature/Console/SharedFolder/DeleteTest.php
@@ -0,0 +1,61 @@
+<?php
+
+namespace Tests\Feature\Console\SharedFolder;
+
+use App\SharedFolder;
+use Illuminate\Support\Facades\Queue;
+use Tests\TestCase;
+
+class DeleteTest extends TestCase
+{
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ $this->deleteTestSharedFolder('folder-test@kolabnow.com');
+ $this->deleteTestUser('folder-owner@kolabnow.com');
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function tearDown(): void
+ {
+ $this->deleteTestSharedFolder('folder-test@kolabnow.com');
+ $this->deleteTestUser('folder-owner@kolabnow.com');
+
+ parent::tearDown();
+ }
+
+ /**
+ * Test command runs
+ */
+ public function testHandle(): void
+ {
+ Queue::fake();
+
+ // Warning: We're not using artisan() here, as this will not
+ // allow us to test "empty output" cases
+
+ // Non-existing folder
+ $code = \Artisan::call("sharedfolder:delete test@folder.com");
+ $output = trim(\Artisan::output());
+
+ $this->assertSame(1, $code);
+ $this->assertSame("Shared folder test@folder.com does not exist.", $output);
+
+ $user = $this->getTestUser('folder-owner@kolabnow.com');
+ $folder = $this->getTestSharedFolder('folder-test@kolabnow.com');
+
+ // Existing folder
+ $code = \Artisan::call("sharedfolder:delete {$folder->email}");
+ $output = trim(\Artisan::output());
+
+ $this->assertSame(0, $code);
+ $this->assertSame('', $output);
+ $this->assertTrue($folder->refresh()->trashed());
+ }
+}
diff --git a/src/tests/Feature/Console/SharedFolder/ForceDeleteTest.php b/src/tests/Feature/Console/SharedFolder/ForceDeleteTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Feature/Console/SharedFolder/ForceDeleteTest.php
@@ -0,0 +1,71 @@
+<?php
+
+namespace Tests\Feature\Console\SharedFolder;
+
+use App\SharedFolder;
+use Illuminate\Support\Facades\Queue;
+use Tests\TestCase;
+
+class ForceDeleteTest extends TestCase
+{
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ $this->deleteTestSharedFolder('folder-test@kolabnow.com');
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function tearDown(): void
+ {
+ $this->deleteTestSharedFolder('folder-test@kolabnow.com');
+
+ parent::tearDown();
+ }
+
+ /**
+ * Test command runs
+ */
+ public function testHandle(): void
+ {
+ Queue::fake();
+
+ // Warning: We're not using artisan() here, as this will not
+ // allow us to test "empty output" cases
+
+ $folder = $this->getTestSharedFolder('folder-test@kolabnow.com');
+
+ // Non-existing folder
+ $code = \Artisan::call("sharedfolder:force-delete test@folder.com");
+ $output = trim(\Artisan::output());
+
+ $this->assertSame(1, $code);
+ $this->assertSame("Shared folder not found.", $output);
+
+ // Non-deleted folder
+ $code = \Artisan::call("sharedfolder:force-delete {$folder->email}");
+ $output = trim(\Artisan::output());
+
+ $this->assertSame(1, $code);
+ $this->assertSame("The shared folder is not yet deleted.", $output);
+
+ $folder->delete();
+ $this->assertTrue($folder->trashed());
+
+ // Existing and deleted folder
+ $code = \Artisan::call("sharedfolder:force-delete {$folder->email}");
+ $output = trim(\Artisan::output());
+
+ $this->assertSame(0, $code);
+ $this->assertSame('', $output);
+ $this->assertCount(
+ 0,
+ SharedFolder::withTrashed()->where('email', $folder->email)->get()
+ );
+ }
+}

File Metadata

Mime Type
text/plain
Expires
Fri, Apr 3, 12:20 PM (1 d, 8 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18824032
Default Alt Text
D3307.1775218832.diff (15 KB)

Event Timeline