-
- {extension.extra['flarum-extension'].icon ? icon(extension.extra['flarum-extension'].icon.name) : ''}
+
+ {extension.icon ? icon(extension.icon.name) : ''}
{controls.length ? (
setVariable('settings', $settings);
$view->setVariable('permissions', Permission::map());
- $view->setVariable('extensions', $this->extensions->getInfo());
+ $view->setVariable('extensions', $this->extensions->getExtensions()->toArray());
return $view;
}
diff --git a/framework/core/src/Database/DatabaseMigrationRepository.php b/framework/core/src/Database/DatabaseMigrationRepository.php
index d7230ad01..49a1978e4 100755
--- a/framework/core/src/Database/DatabaseMigrationRepository.php
+++ b/framework/core/src/Database/DatabaseMigrationRepository.php
@@ -39,9 +39,8 @@ class DatabaseMigrationRepository implements MigrationRepositoryInterface
/**
* Create a new database migration repository instance.
*
- * @param \Illuminate\Database\ConnectionResolverInterface $resolver
- * @param string $table
- * @return void
+ * @param \Illuminate\Database\ConnectionResolverInterface $resolver
+ * @param string $table
*/
public function __construct(Resolver $resolver, $table)
{
diff --git a/framework/core/src/Database/Migrator.php b/framework/core/src/Database/Migrator.php
index 07d6c7b67..979ef2191 100755
--- a/framework/core/src/Database/Migrator.php
+++ b/framework/core/src/Database/Migrator.php
@@ -11,6 +11,7 @@
namespace Flarum\Database;
+use Flarum\Extension\Extension;
use Illuminate\Database\ConnectionResolverInterface as Resolver;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Str;
@@ -55,35 +56,34 @@ class Migrator
/**
* Create a new migrator instance.
*
- * @param \Flarum\Database\MigrationRepositoryInterface $repository
- * @param \Illuminate\Database\ConnectionResolverInterface $resolver
- * @param \Illuminate\Filesystem\Filesystem $files
- * @return void
+ * @param \Flarum\Database\MigrationRepositoryInterface $repository
+ * @param \Illuminate\Database\ConnectionResolverInterface $resolver
+ * @param \Illuminate\Filesystem\Filesystem $files
*/
public function __construct(
MigrationRepositoryInterface $repository,
Resolver $resolver,
Filesystem $files
) {
- $this->files = $files;
- $this->resolver = $resolver;
+ $this->files = $files;
+ $this->resolver = $resolver;
$this->repository = $repository;
}
/**
* Run the outstanding migrations at a given path.
*
- * @param string $path
- * @param string $extension
+ * @param string $path
+ * @param Extension $extension
* @return void
*/
- public function run($path, $extension = null)
+ public function run($path, Extension $extension = null)
{
$this->notes = [];
$files = $this->getMigrationFiles($path);
- $ran = $this->repository->getRan($extension);
+ $ran = $this->repository->getRan($extension ? $extension->getId() : null);
$migrations = array_diff($files, $ran);
@@ -95,11 +95,10 @@ class Migrator
/**
* Run an array of migrations.
*
- * @param array $migrations
- * @param bool $pretend
- * @return void
+ * @param array $migrations
+ * @param Extension $extension
*/
- public function runMigrationList($migrations, $extension)
+ public function runMigrationList($migrations, Extension $extension = null)
{
// First we will just make sure that there are any migrations to run. If there
// aren't, we will just make a note of it to the developer so they're aware
@@ -121,11 +120,11 @@ class Migrator
/**
* Run "up" a migration instance.
*
- * @param string $file
- * @param string $extension
+ * @param string $file
+ * @param Extension $extension
* @return void
*/
- protected function runUp($file, $extension)
+ protected function runUp($file, Extension $extension = null)
{
// First we will resolve a "real" instance of the migration class from this
// migration file name. Once we have the instances we can run the actual
@@ -137,7 +136,7 @@ class Migrator
// Once we have run a migrations class, we will log that it was run in this
// repository so that we don't try to run it next time we do a migration
// in the application. A migration repository keeps the migrate order.
- $this->repository->log($file, $extension);
+ $this->repository->log($file, $extension ? $extension->getId() : null);
$this->note("Migrated: $file");
}
@@ -145,14 +144,15 @@ class Migrator
/**
* Rolls all of the currently applied migrations back.
*
- * @param bool $pretend
+ * @param string $path
+ * @param Extension $extension
* @return int
*/
- public function reset($path, $extension = null)
+ public function reset($path, Extension $extension = null)
{
$this->notes = [];
- $migrations = array_reverse($this->repository->getRan($extension));
+ $migrations = array_reverse($this->repository->getRan($extension->getId()));
$this->requireFiles($path, $migrations);
@@ -172,11 +172,11 @@ class Migrator
/**
* Run "down" a migration instance.
*
- * @param string $file
- * @param string $extension
+ * @param string $file
+ * @param Extension $extension
* @return void
*/
- protected function runDown($file, $extension = null)
+ protected function runDown($file, Extension $extension = null)
{
// First we will get the file name of the migration so we can resolve out an
// instance of the migration. Once we get an instance we can either run a
@@ -188,7 +188,7 @@ class Migrator
// Once we have successfully run the migration "down" we will remove it from
// the migration repository so it will be considered to have not been run
// by the application then will be able to fire by any later operation.
- $this->repository->delete($file, $extension);
+ $this->repository->delete($file, $extension ? $extension->getId() : null);
$this->note("Rolled back: $file");
}
@@ -196,12 +196,12 @@ class Migrator
/**
* Get all of the migration files in a given path.
*
- * @param string $path
+ * @param string $path
* @return array
*/
public function getMigrationFiles($path)
{
- $files = $this->files->glob($path.'/*_*.php');
+ $files = $this->files->glob($path . '/*_*.php');
// Once we have the array of files in the directory we will just remove the
// extension and take the basename of the file which is all we need when
@@ -225,28 +225,36 @@ class Migrator
/**
* Require in all the migration files in a given path.
*
- * @param string $path
- * @param array $files
+ * @param string $path
+ * @param array $files
* @return void
*/
public function requireFiles($path, array $files)
{
foreach ($files as $file) {
- $this->files->requireOnce($path.'/'.$file.'.php');
+ $this->files->requireOnce($path . '/' . $file . '.php');
}
}
/**
* Resolve a migration instance from a file.
*
- * @param string $file
+ * @param string $file
+ * @param Extension $extension
* @return object
*/
- public function resolve($file, $extension = null)
+ public function resolve($file, Extension $extension = null)
{
$file = implode('_', array_slice(explode('_', $file), 4));
- $class = ($extension ? str_replace('-', '\\', $extension) : 'Flarum\\Core') . '\\Migration\\';
+ // flagrow/image-upload
+ if ($extension) {
+ $class = str_replace('/', '\\', $extension->name);
+ } else {
+ $class = 'Flarum\\Core';
+ }
+
+ $class .= '\\Migration\\';
$class .= Str::studly($file);
@@ -256,7 +264,7 @@ class Migrator
/**
* Raise a note event for the migrator.
*
- * @param string $message
+ * @param string $message
* @return void
*/
protected function note($message)
@@ -277,7 +285,7 @@ class Migrator
/**
* Resolve the database connection instance.
*
- * @param string $connection
+ * @param string $connection
* @return \Illuminate\Database\Connection
*/
public function resolveConnection($connection)
@@ -288,7 +296,7 @@ class Migrator
/**
* Set the default connection name.
*
- * @param string $name
+ * @param string $name
* @return void
*/
public function setConnection($name)
diff --git a/framework/core/src/Event/ExtensionWasDisabled.php b/framework/core/src/Event/ExtensionWasDisabled.php
index f9f8ba7d0..9382452c4 100644
--- a/framework/core/src/Event/ExtensionWasDisabled.php
+++ b/framework/core/src/Event/ExtensionWasDisabled.php
@@ -10,6 +10,8 @@
namespace Flarum\Event;
+use Flarum\Extension\Extension;
+
class ExtensionWasDisabled
{
/**
@@ -18,9 +20,9 @@ class ExtensionWasDisabled
protected $extension;
/**
- * @param string $extension
+ * @param Extension $extension
*/
- public function __construct($extension)
+ public function __construct(Extension $extension)
{
$this->extension = $extension;
}
diff --git a/framework/core/src/Event/ExtensionWasEnabled.php b/framework/core/src/Event/ExtensionWasEnabled.php
index 19d1080be..48f0ea4fa 100644
--- a/framework/core/src/Event/ExtensionWasEnabled.php
+++ b/framework/core/src/Event/ExtensionWasEnabled.php
@@ -10,6 +10,8 @@
namespace Flarum\Event;
+use Flarum\Extension\Extension;
+
class ExtensionWasEnabled
{
/**
@@ -18,9 +20,9 @@ class ExtensionWasEnabled
protected $extension;
/**
- * @param string $extension
+ * @param Extension $extension
*/
- public function __construct($extension)
+ public function __construct(Extension $extension)
{
$this->extension = $extension;
}
diff --git a/framework/core/src/Extension/Extension.php b/framework/core/src/Extension/Extension.php
new file mode 100644
index 000000000..33ad9ff9b
--- /dev/null
+++ b/framework/core/src/Extension/Extension.php
@@ -0,0 +1,252 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Flarum\Extension;
+
+use Illuminate\Contracts\Support\Arrayable;
+use Illuminate\Support\Arr;
+use Illuminate\Support\Str;
+
+/**
+ * @property string $name
+ * @property string $description
+ * @property string $type
+ * @property array $keywords
+ * @property string $homepage
+ * @property string $time
+ * @property string $license
+ * @property array $authors
+ * @property array $support
+ * @property array $require
+ * @property array $requireDev
+ * @property array $autoload
+ * @property array $autoloadDev
+ * @property array $conflict
+ * @property array $replace
+ * @property array $provide
+ * @property array $suggest
+ * @property array $extra
+ */
+class Extension implements Arrayable
+{
+ /**
+ * Unique Id of the extension.
+ *
+ * @info Identical to the directory in the extensions directory.
+ * @example flarum_suspend
+ *
+ * @var string
+ */
+ protected $id;
+ /**
+ * The directory of this extension.
+ *
+ * @var string
+ */
+ protected $path;
+
+ /**
+ * Composer json of the package.
+ *
+ * @var array
+ */
+ protected $composerJson;
+
+ /**
+ * Whether the extension is installed.
+ *
+ * @var bool
+ */
+ protected $installed = true;
+
+ /**
+ * The installed version of the extension.
+ *
+ * @var string
+ */
+ protected $version;
+
+ /**
+ * Whether the extension is enabled.
+ *
+ * @var bool
+ */
+ protected $enabled = false;
+
+ /**
+ * @param $path
+ * @param array $composerJson
+ */
+ public function __construct($path, $composerJson)
+ {
+ $this->id = end(explode('/', $path));
+ $this->path = $path;
+ $this->composerJson = $composerJson;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function __get($name)
+ {
+ return $this->composerJsonAttribute(Str::snake($name, '-'));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function __isset($name)
+ {
+ return isset($this->{$name}) || $this->composerJsonAttribute(Str::snake($name, '-'));
+ }
+
+
+ /**
+ * Dot notation getter for composer.json attributes.
+ *
+ * @see https://laravel.com/docs/5.1/helpers#arrays
+ *
+ * @param $name
+ * @return mixed
+ */
+ public function composerJsonAttribute($name)
+ {
+ return Arr::get($this->composerJson, $name);
+ }
+
+ /**
+ * @param boolean $installed
+ * @return Extension
+ */
+ public function setInstalled($installed)
+ {
+ $this->installed = $installed;
+
+ return $this;
+ }
+
+ /**
+ * @return boolean
+ */
+ public function isInstalled()
+ {
+ return $this->installed;
+ }
+
+ /**
+ * @param string $version
+ * @return Extension
+ */
+ public function setVersion($version)
+ {
+ $this->version = $version;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getVersion()
+ {
+ return $this->version;
+ }
+
+ /**
+ * Loads the icon information from the composer.json.
+ *
+ * @return array|null
+ */
+ public function getIcon()
+ {
+ if (($icon = $this->composerJsonAttribute('extra.flarum-extension.icon'))) {
+ if ($file = Arr::get($icon, 'image')) {
+ $file = $this->path . '/' . $file;
+
+ if (file_exists($file)) {
+ $mimetype = pathinfo($file, PATHINFO_EXTENSION) === 'svg'
+ ? 'image/svg+xml'
+ : finfo_file(finfo_open(FILEINFO_MIME_TYPE), $file);
+ $data = file_get_contents($file);
+
+ $icon['backgroundImage'] = 'url(\'data:' . $mimetype . ';base64,' . base64_encode($data) . '\')';
+ }
+ }
+
+ return $icon;
+ }
+ }
+
+ /**
+ * @param boolean $enabled
+ * @return Extension
+ */
+ public function setEnabled($enabled)
+ {
+ $this->enabled = $enabled;
+
+ return $this;
+ }
+
+ /**
+ * @return boolean
+ */
+ public function isEnabled()
+ {
+ return $this->enabled;
+ }
+
+ /**
+ * The raw path of the directory under extensions.
+ *
+ * @return string
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * Tests whether the extension has assets.
+ *
+ * @return bool
+ */
+ public function hasAssets()
+ {
+ return realpath($this->path . '/assets/') !== false;
+ }
+
+ /**
+ * Tests whether the extension has migrations.
+ *
+ * @return bool
+ */
+ public function hasMigrations()
+ {
+ return realpath($this->path . '/migrations/') !== false;
+ }
+
+ /**
+ * Generates an array result for the object.
+ *
+ * @return array
+ */
+ public function toArray()
+ {
+ return (array) array_merge([
+ 'id' => $this->getId(),
+ 'version' => $this->getVersion(),
+ 'path' => $this->path,
+ 'icon' => $this->getIcon(),
+ 'hasAssets' => $this->hasAssets(),
+ 'hasMigrations' => $this->hasMigrations(),
+ ], $this->composerJson);
+ }
+}
diff --git a/framework/core/src/Extension/ExtensionManager.php b/framework/core/src/Extension/ExtensionManager.php
index 288400ac9..cdcc975de 100644
--- a/framework/core/src/Extension/ExtensionManager.php
+++ b/framework/core/src/Extension/ExtensionManager.php
@@ -19,6 +19,8 @@ use Flarum\Foundation\Application;
use Flarum\Settings\SettingsRepositoryInterface;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Filesystem\Filesystem;
+use Illuminate\Support\Arr;
+use Illuminate\Support\Collection;
class ExtensionManager
{
@@ -38,70 +40,84 @@ class ExtensionManager
*/
protected $filesystem;
- public function __construct(SettingsRepositoryInterface $config, Application $app, Migrator $migrator, Dispatcher $dispatcher, Filesystem $filesystem)
- {
- $this->config = $config;
- $this->app = $app;
- $this->migrator = $migrator;
+ public function __construct(
+ SettingsRepositoryInterface $config,
+ Application $app,
+ Migrator $migrator,
+ Dispatcher $dispatcher,
+ Filesystem $filesystem
+ ) {
+ $this->config = $config;
+ $this->app = $app;
+ $this->migrator = $migrator;
$this->dispatcher = $dispatcher;
$this->filesystem = $filesystem;
}
- public function getInfo()
+ /**
+ * @return Collection
+ */
+ public function getExtensions()
{
$extensionsDir = $this->getExtensionsDir();
- $dirs = array_diff(scandir($extensionsDir), ['.', '..']);
- $extensions = [];
+ $dirs = array_diff(scandir($extensionsDir), ['.', '..']);
+ $extensions = new Collection();
$installed = json_decode(file_get_contents(public_path('vendor/composer/installed.json')), true);
foreach ($dirs as $dir) {
if (file_exists($manifest = $extensionsDir . '/' . $dir . '/composer.json')) {
- $extension = json_decode(file_get_contents($manifest), true);
+ $extension = new Extension(
+ $extensionsDir . '/' . $dir,
+ json_decode(file_get_contents($manifest), true)
+ );
- if (empty($extension['name'])) {
+ if (empty($extension->name)) {
continue;
}
- if (isset($extension['extra']['flarum-extension']['icon'])) {
- $icon = &$extension['extra']['flarum-extension']['icon'];
-
- if ($file = array_get($icon, 'image')) {
- $file = $extensionsDir . '/' . $dir . '/' . $file;
-
- if (file_exists($file)) {
- $mimetype = pathinfo($file, PATHINFO_EXTENSION) === 'svg'
- ? 'image/svg+xml'
- : finfo_file(finfo_open(FILEINFO_MIME_TYPE), $file);
- $data = file_get_contents($file);
-
- $icon['backgroundImage'] = 'url(\'data:' . $mimetype . ';base64,' . base64_encode($data) . '\')';
- }
- }
- }
-
foreach ($installed as $package) {
- if ($package['name'] === $extension['name']) {
- $extension['version'] = $package['version'];
+ if ($package['name'] === $extension->name) {
+ $extension->setInstalled(true);
+ $extension->setVersion($package['version']);
+ $extension->setEnabled($this->isEnabled($dir));
}
}
- $extension['id'] = $dir;
-
- $extensions[$dir] = $extension;
+ $extensions->put($dir, $extension);
}
}
- return $extensions;
+ return $extensions->sortBy(function ($extension, $name) {
+ return $extension->composerJsonAttribute('extra.flarum-extension.title');
+ });
}
- public function enable($extension)
+ /**
+ * Loads an Extension with all information.
+ *
+ * @param string $name
+ * @return Extension|null
+ */
+ public function getExtension($name)
{
- if (! $this->isEnabled($extension)) {
+ return $this->getExtensions()->get($name);
+ }
+
+ /**
+ * Enables the extension.
+ *
+ * @param string $name
+ */
+ public function enable($name)
+ {
+ if (!$this->isEnabled($name)) {
+ $extension = $this->getExtension($name);
+
$enabled = $this->getEnabled();
- $enabled[] = $extension;
+ $enabled[] = $name;
$this->migrate($extension);
@@ -109,87 +125,122 @@ class ExtensionManager
$this->setEnabled($enabled);
+ $extension->setEnabled(true);
+
$this->dispatcher->fire(new ExtensionWasEnabled($extension));
}
}
- public function disable($extension)
+ /**
+ * Disables an extension.
+ *
+ * @param string $name
+ */
+ public function disable($name)
{
$enabled = $this->getEnabled();
- if (($k = array_search($extension, $enabled)) !== false) {
+ if (($k = array_search($name, $enabled)) !== false) {
unset($enabled[$k]);
+ $extension = $this->getExtension($name);
+
$this->setEnabled($enabled);
+ $extension->setEnabled(false);
+
$this->dispatcher->fire(new ExtensionWasDisabled($extension));
}
}
- public function uninstall($extension)
+ /**
+ * Uninstalls an extension.
+ *
+ * @param string $name
+ */
+ public function uninstall($name)
{
- $this->disable($extension);
+ $extension = $this->getExtension($name);
+
+ $this->disable($name);
$this->migrateDown($extension);
$this->unpublishAssets($extension);
+ $extension->setInstalled(false);
+
$this->dispatcher->fire(new ExtensionWasUninstalled($extension));
}
/**
* Copy the assets from an extension's assets directory into public view.
*
- * @param string $extension
+ * @param Extension $extension
*/
- protected function publishAssets($extension)
+ protected function publishAssets(Extension $extension)
{
- $this->filesystem->copyDirectory(
- $this->app->basePath().'/extensions/'.$extension.'/assets',
- $this->app->basePath().'/assets/extensions/'.$extension
- );
+ if ($extension->hasAssets()) {
+ $this->filesystem->copyDirectory(
+ $this->app->basePath() . '/extensions/' . $extension->getId() . '/assets',
+ $this->app->basePath() . '/assets/extensions/' . $extension->getId()
+ );
+ }
}
/**
* Delete an extension's assets from public view.
*
- * @param string $extension
+ * @param Extension $extension
*/
- protected function unpublishAssets($extension)
+ protected function unpublishAssets(Extension $extension)
{
- $this->filesystem->deleteDirectory($this->app->basePath().'/assets/extensions/'.$extension);
+ $this->filesystem->deleteDirectory($this->app->basePath() . '/assets/extensions/' . $extension);
}
/**
* Get the path to an extension's published asset.
*
- * @param string $extension
- * @param string $path
+ * @param Extension $extension
+ * @param string $path
* @return string
*/
- public function getAsset($extension, $path)
+ public function getAsset(Extension $extension, $path)
{
- return $this->app->basePath().'/assets/extensions/'.$extension.$path;
+ return $this->app->basePath() . '/assets/extensions/' . $extension->getId() . $path;
}
- public function migrate($extension, $up = true)
+ /**
+ * Runs the database migrations for the extension.
+ *
+ * @param Extension $extension
+ * @param bool|true $up
+ */
+ public function migrate(Extension $extension, $up = true)
{
- $migrationDir = public_path('extensions/' . $extension . '/migrations');
+ if ($extension->hasMigrations()) {
+ $migrationDir = public_path('extensions/' . $extension->getId() . '/migrations');
- $this->app->bind('Illuminate\Database\Schema\Builder', function ($container) {
- return $container->make('Illuminate\Database\ConnectionInterface')->getSchemaBuilder();
- });
+ $this->app->bind('Illuminate\Database\Schema\Builder', function ($container) {
+ return $container->make('Illuminate\Database\ConnectionInterface')->getSchemaBuilder();
+ });
- if ($up) {
- $this->migrator->run($migrationDir, $extension);
- } else {
- $this->migrator->reset($migrationDir, $extension);
+ if ($up) {
+ $this->migrator->run($migrationDir, $extension);
+ } else {
+ $this->migrator->reset($migrationDir, $extension);
+ }
}
}
- public function migrateDown($extension)
+ /**
+ * Runs the database migrations to reset the database to its old state.
+ *
+ * @param Extension $extension
+ */
+ public function migrateDown(Extension $extension)
{
- $this->migrate($extension, false);
+ $this->migrate($extension->getId(), false);
}
public function getMigrator()
diff --git a/framework/core/src/Install/Console/InstallCommand.php b/framework/core/src/Install/Console/InstallCommand.php
index 68802d22c..e67d192c8 100644
--- a/framework/core/src/Install/Console/InstallCommand.php
+++ b/framework/core/src/Install/Console/InstallCommand.php
@@ -343,7 +343,7 @@ class InstallCommand extends AbstractCommand
'flarum-pusher',
];
- foreach ($extensions->getInfo() as $name => $extension) {
+ foreach ($extensions->getExtensions() as $name => $extension) {
if (in_array($name, $disabled)) {
continue;
}
diff --git a/framework/core/src/Update/Console/MigrateCommand.php b/framework/core/src/Update/Console/MigrateCommand.php
index 9638fe157..6356d2c6b 100644
--- a/framework/core/src/Update/Console/MigrateCommand.php
+++ b/framework/core/src/Update/Console/MigrateCommand.php
@@ -74,8 +74,8 @@ class MigrateCommand extends AbstractCommand
$migrator = $extensions->getMigrator();
- foreach ($extensions->getInfo() as $name => $extension) {
- if (! $extensions->isEnabled($name)) {
+ foreach ($extensions->getExtensions() as $name => $extension) {
+ if (! $extension->isEnabled()) {
continue;
}