diff --git a/bin/developer b/bin/developer
index 4ba8a54cc3..83213f9c72 100755
--- a/bin/developer
+++ b/bin/developer
@@ -34,7 +34,7 @@ use Doctrine\ORM\Tools\Console\Command\ConvertMappingCommand;
use Doctrine\ORM\Tools\Console\Command\RunDqlCommand;
use Doctrine\ORM\Tools\Console\Command\ValidateSchemaCommand;
-require_once __DIR__ . '/../vendor/autoload.php';
+require_once __DIR__ . '/../plugins/autoload.php';
try {
$cli = new CLI("
diff --git a/lib/Alchemy/Phrasea/Command/Plugin/AbstractPluginCommand.php b/lib/Alchemy/Phrasea/Command/Plugin/AbstractPluginCommand.php
new file mode 100644
index 0000000000..e241c92d98
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Command/Plugin/AbstractPluginCommand.php
@@ -0,0 +1,41 @@
+write("Validating plugins...");
+ foreach ($this->container['plugins.explorer'] as $directory) {
+ $manifests[] = $manifest = $this->container['plugins.plugins-validator']->validatePlugin($directory);
+ }
+ $output->writeln(" OK");
+
+ return $manifests;
+ }
+
+ protected function updateConfigFiles(InputInterface $input, OutputInterface $output)
+ {
+ $manifests = $this->validatePlugins($input, $output);
+
+ $output->write("Updating config files...");
+ $this->container['plugins.autoloader-generator']->write($manifests);
+ $output->writeln(" OK");
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Command/Plugin/AddPlugin.php b/lib/Alchemy/Phrasea/Command/Plugin/AddPlugin.php
new file mode 100644
index 0000000000..1fd0c2c0cd
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Command/Plugin/AddPlugin.php
@@ -0,0 +1,61 @@
+setDescription('Install a plugin to Phraseanet')
+ ->addArgument('source', InputArgument::REQUIRED, 'The source is a folder');
+ }
+
+ protected function doExecute(InputInterface $input, OutputInterface $output)
+ {
+ $source = $input->getArgument('source');
+
+ $temporaryDir = $this->container['temporary-filesystem']->createTemporaryDirectory();
+
+ $output->write("Importing $source...");
+ $this->container['plugins.importer']->import($source, $temporaryDir);
+ $output->writeln(" OK");
+
+ $output->write("Validating plugin...");
+ $manifest = $this->container['plugins.plugins-validator']->validatePlugin($temporaryDir);
+ $output->writeln(" OK found ".$manifest->getName()."");
+
+ $targetDir = $this->container['plugins.directory'] . DIRECTORY_SEPARATOR . $manifest->getName();
+
+ $output->write("Setting up composer...");
+ $this->container['plugins.composer-installer']->install($temporaryDir);
+ $output->writeln(" OK");
+
+ $output->write("Installing plugin ".$manifest->getName()."...");
+ $this->container['filesystem']->mirror($temporaryDir, $targetDir);
+ $output->writeln(" OK");
+
+ $output->write("Removing temporary directory...");
+ $this->container['filesystem']->remove($temporaryDir);
+ $output->writeln(" OK");
+
+ $this->updateConfigFiles($input, $output);
+
+ return 0;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Command/Plugin/RemovePlugin.php b/lib/Alchemy/Phrasea/Command/Plugin/RemovePlugin.php
new file mode 100644
index 0000000000..8d26f5e22d
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Command/Plugin/RemovePlugin.php
@@ -0,0 +1,43 @@
+setDescription('Removes a plugin given its name')
+ ->addArgument('name', InputArgument::REQUIRED, 'The name of the plugin');
+ }
+
+ protected function doExecute(InputInterface $input, OutputInterface $output)
+ {
+ $name = $input->getArgument('name');
+
+ $path = $this->container['plugins.directory'] . DIRECTORY_SEPARATOR . $name;
+
+ $output->write("Removing $name...");
+ $this->container['filesystem']->remove($path);
+ $output->writeln(" OK");
+
+ $this->updateConfigFiles($input, $output);
+
+ return 0;
+ }
+}