diff --git a/composer.json b/composer.json index 36f779f73b..8b087953f5 100644 --- a/composer.json +++ b/composer.json @@ -64,7 +64,7 @@ "autoload-dev": { "psr-4": { "Tests\\": "tests", - "Foo\\Bar\\": "tests/fixtures/Addon" + "Foo\\Bar\\": "tests/Fixtures/Addon" } } } diff --git a/src/Extend/Addon.php b/src/Extend/Addon.php index 858e82b42b..bc5d3e8cda 100644 --- a/src/Extend/Addon.php +++ b/src/Extend/Addon.php @@ -3,6 +3,7 @@ namespace Statamic\Extend; use Facades\Statamic\Licensing\LicenseManager; +use ReflectionClass; use Statamic\Facades\File; use Statamic\Facades\Path; use Statamic\Facades\URL; @@ -63,6 +64,13 @@ final class Addon */ protected $autoload; + /** + * The service provider class. + * + * @var string + */ + protected $provider; + /** * The name of the addon. eg. "Bloodhound Search". * @@ -162,8 +170,8 @@ public static function makeFromPackage(array $package) $instance = self::make($package['id']); $keys = [ - 'id', 'slug', 'editions', 'marketplaceId', 'marketplaceSlug', 'marketplaceSellerSlug', 'name', 'namespace', 'directory', - 'autoload', 'description', 'package', 'version', 'latestVersion', 'url', 'developer', 'developerUrl', 'isCommercial', + 'id', 'slug', 'editions', 'marketplaceId', 'marketplaceSlug', 'marketplaceSellerSlug', 'name', 'namespace', + 'autoload', 'provider', 'description', 'package', 'version', 'latestVersion', 'url', 'developer', 'developerUrl', 'isCommercial', ]; foreach (Arr::only($package, $keys) as $key => $value) { @@ -297,7 +305,7 @@ public function name($name = null) public function hasFile($path) { if (! $this->directory()) { - throw new \Exception('Cannot check files without a directory specified.'); + throw new \Exception('Cannot check files without a provider specified.'); } return File::exists(Path::assemble($this->directory(), $path)); @@ -312,7 +320,7 @@ public function hasFile($path) public function getFile($path) { if (! $this->directory()) { - throw new \Exception('Cannot get files without a directory specified.'); + throw new \Exception('Cannot get files without a provider specified.'); } return File::get(Path::assemble($this->directory(), $path)); @@ -327,7 +335,7 @@ public function getFile($path) public function putFile($path, $contents) { if (! $this->directory()) { - throw new \Exception('Cannot write files without a directory specified.'); + throw new \Exception('Cannot write files without a provider specified.'); } File::put( @@ -393,6 +401,28 @@ public function __call($method, $args) return $this; } + /** + * The directory the package is located within. eg. "/path/to/vendor/statamic/bloodhound". + * + * @return string + */ + public function directory() + { + if (! $this->provider) { + return null; + } + + if ($this->directory) { + return $this->directory; + } + + $reflector = new ReflectionClass($this->provider); + + $dir = Str::removeRight(dirname($reflector->getFileName()), rtrim($this->autoload, '/')); + + return $this->directory = Str::removeRight($dir, '/'); + } + public function existsOnMarketplace() { return $this->marketplaceSlug() !== null; diff --git a/src/Extend/Manifest.php b/src/Extend/Manifest.php index 8bbee6dbf0..50555f55f1 100644 --- a/src/Extend/Manifest.php +++ b/src/Extend/Manifest.php @@ -60,8 +60,8 @@ protected function formatPackage($package) 'latestVersion' => data_get($marketplaceData, 'latest_version', null), 'version' => Str::removeLeft($package['version'], 'v'), 'namespace' => $namespace, - 'directory' => $directory, 'autoload' => $autoload, + 'provider' => $provider, // Local data for marketplace GUI? 'name' => $statamic['name'] ?? Arr::last($providerParts), diff --git a/tests/Extend/AddonTest.php b/tests/Extend/AddonTest.php index c78b9d539d..9fa4cb3dde 100644 --- a/tests/Extend/AddonTest.php +++ b/tests/Extend/AddonTest.php @@ -3,6 +3,7 @@ namespace Statamic\Testing\Extend; use Facades\Statamic\Licensing\LicenseManager; +use Foo\Bar\TestAddonServiceProvider; use Illuminate\Support\Collection; use Statamic\Extend\Addon; use Statamic\Facades\File; @@ -10,6 +11,14 @@ class AddonTest extends TestCase { + protected $addonFixtureDir; + + public function setUp(): void + { + parent::setUp(); + $this->addonFixtureDir = realpath(__DIR__.'/../Fixtures/Addon'); + } + /** @test */ public function it_creates_an_instance_with_a_name() { @@ -100,7 +109,8 @@ public function it_creates_an_instance_from_a_package() $this->assertEquals('Test Addon', $addon->name()); $this->assertEquals('Test description', $addon->description()); $this->assertEquals('Vendor\\TestAddon', $addon->namespace()); - $this->assertEquals('/path/to/addon', $addon->directory()); + $this->assertEquals($this->addonFixtureDir, $addon->directory()); + $this->assertEquals('', $addon->autoload()); $this->assertEquals('http://test-url.com', $addon->url()); $this->assertEquals('Test Developer LLC', $addon->developer()); $this->assertEquals('http://test-developer.com', $addon->developerUrl()); @@ -111,10 +121,10 @@ public function it_creates_an_instance_from_a_package() /** @test */ public function it_checks_if_a_file_exists() { - $addon = Addon::make('Test Addon')->directory('/path/to/addon'); + $addon = $this->makeFromPackage(); - File::shouldReceive('exists')->with('/path/to/addon/test.txt')->andReturnTrue(); - File::shouldReceive('exists')->with('/path/to/addon/notfound.txt')->andReturnFalse(); + File::shouldReceive('exists')->with($this->addonFixtureDir.'/test.txt')->andReturnTrue(); + File::shouldReceive('exists')->with($this->addonFixtureDir.'/notfound.txt')->andReturnFalse(); $this->assertTrue($addon->hasFile('test.txt')); $this->assertFalse($addon->hasFile('notfound.txt')); @@ -123,9 +133,9 @@ public function it_checks_if_a_file_exists() /** @test */ public function it_gets_file_contents() { - $addon = Addon::make('Test Addon')->directory('/path/to/addon'); + $addon = $this->makeFromPackage(); - File::shouldReceive('get')->with('/path/to/addon/test.txt')->andReturn('the file contents'); + File::shouldReceive('get')->with($this->addonFixtureDir.'/test.txt')->andReturn('the file contents'); $this->assertEquals('the file contents', $addon->getFile('test.txt')); } @@ -133,23 +143,23 @@ public function it_gets_file_contents() /** @test */ public function it_writes_file_contents() { - $addon = Addon::make('Test Addon')->directory('/path/to/addon'); + $addon = $this->makeFromPackage(); - File::shouldReceive('put')->with('/path/to/addon/test.txt', 'the file contents'); + File::shouldReceive('put')->with($this->addonFixtureDir.'/test.txt', 'the file contents'); $addon->putFile('test.txt', 'the file contents'); } /** @test */ - public function it_doesnt_allow_getting_files_if_no_directory_is_set() + public function it_doesnt_allow_getting_files_if_no_provider_is_set() { File::spy(); - $addon = $this->makeFromPackage(['directory' => null]); + $addon = $this->makeFromPackage(['provider' => null]); try { $addon->getFile('foo.txt', 'foo'); } catch (\Exception $e) { - $this->assertEquals('Cannot get files without a directory specified.', $e->getMessage()); + $this->assertEquals('Cannot get files without a provider specified.', $e->getMessage()); File::shouldNotHaveReceived('get'); return; @@ -159,15 +169,15 @@ public function it_doesnt_allow_getting_files_if_no_directory_is_set() } /** @test */ - public function it_doesnt_allow_checking_for_files_if_no_directory_is_set() + public function it_doesnt_allow_checking_for_files_if_no_provider_is_set() { File::spy(); - $addon = $this->makeFromPackage(['directory' => null]); + $addon = $this->makeFromPackage(['provider' => null]); try { $addon->hasFile('foo.txt', 'foo'); } catch (\Exception $e) { - $this->assertEquals('Cannot check files without a directory specified.', $e->getMessage()); + $this->assertEquals('Cannot check files without a provider specified.', $e->getMessage()); File::shouldNotHaveReceived('get'); return; @@ -177,15 +187,15 @@ public function it_doesnt_allow_checking_for_files_if_no_directory_is_set() } /** @test */ - public function it_doesnt_allow_writing_files_if_no_directory_is_set() + public function it_doesnt_allow_writing_files_if_no_provider_is_set() { File::spy(); - $addon = $this->makeFromPackage(['directory' => null]); + $addon = $this->makeFromPackage(['provider' => null]); try { $addon->putFile('foo.txt', 'foo'); } catch (\Exception $e) { - $this->assertEquals('Cannot write files without a directory specified.', $e->getMessage()); + $this->assertEquals('Cannot write files without a provider specified.', $e->getMessage()); File::shouldNotHaveReceived('put'); return; @@ -230,15 +240,15 @@ public function it_gets_the_license() $this->assertEquals('the license', Addon::make('foo/bar')->license()); } - private function makeFromPackage($attributes) + private function makeFromPackage($attributes = []) { return Addon::makeFromPackage(array_merge([ 'id' => 'vendor/test-addon', 'name' => 'Test Addon', 'description' => 'Test description', 'namespace' => 'Vendor\\TestAddon', - 'directory' => '/path/to/addon', - 'autoload' => 'src', + 'provider' => TestAddonServiceProvider::class, + 'autoload' => '', 'url' => 'http://test-url.com', 'developer' => 'Test Developer LLC', 'developerUrl' => 'http://test-developer.com', diff --git a/tests/Fixtures/Addon/TestAddonServiceProvider.php b/tests/Fixtures/Addon/TestAddonServiceProvider.php new file mode 100644 index 0000000000..91ffeb4201 --- /dev/null +++ b/tests/Fixtures/Addon/TestAddonServiceProvider.php @@ -0,0 +1,10 @@ +