aboutsummaryrefslogtreecommitdiff
path: root/cli
diff options
context:
space:
mode:
authorJan-Hendrik Willms <tleilax+studip@gmail.com>2024-09-05 12:00:36 +0000
committerJan-Hendrik Willms <tleilax+studip@gmail.com>2024-09-05 12:00:36 +0000
commit1cb328ab5a58034d9545d0353843e3aaae210ef3 (patch)
treed855d5fb1e8da013c824642994f2ccd84aeee282 /cli
parentac71bb7372e550d5de63746d5b1fc0d263b6ea28 (diff)
use PsrPrinter instead of own printer, make wizard optional, add more...
Closes #4563 Merge request studip/studip!3378
Diffstat (limited to 'cli')
-rw-r--r--cli/Commands/Make/Migration.php3
-rw-r--r--cli/Commands/Make/Model.php3
-rw-r--r--cli/Commands/Make/Plugin.php299
-rw-r--r--cli/Commands/Make/StudipClassPrinter.php18
-rwxr-xr-xcli/studip1
5 files changed, 304 insertions, 20 deletions
diff --git a/cli/Commands/Make/Migration.php b/cli/Commands/Make/Migration.php
index d9ae1d1..992b02e 100644
--- a/cli/Commands/Make/Migration.php
+++ b/cli/Commands/Make/Migration.php
@@ -3,6 +3,7 @@
namespace Studip\Cli\Commands\Make;
use Nette\PhpGenerator\PhpFile;
+use Nette\PhpGenerator\PsrPrinter;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
@@ -96,7 +97,7 @@ final class Migration extends Command
$class->addMethod('up')->addBody('// Add content');
$class->addMethod('down')->addBody('// Add content');
- $printer = new StudipClassPrinter();
+ $printer = new PsrPrinter();
$result = $printer->printFile($file);
$migrationName = $version . '_' . str_replace(' ', '_', lcfirst($name));
$filename = $path . '/' . $migrationName . '.php';
diff --git a/cli/Commands/Make/Model.php b/cli/Commands/Make/Model.php
index 57b15af..82e683b 100644
--- a/cli/Commands/Make/Model.php
+++ b/cli/Commands/Make/Model.php
@@ -3,6 +3,7 @@
namespace Studip\Cli\Commands\Make;
use Nette\PhpGenerator\PhpFile;
+use Nette\PhpGenerator\PsrPrinter;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\ArrayInput;
@@ -89,7 +90,7 @@ final class Model extends Command
$method->addParameter('config', []);
- $printer = new StudipClassPrinter();
+ $printer = new PsrPrinter();
$result = $printer->printFile($file);
$modelName = str_replace(' ', '_', ucfirst($name));
diff --git a/cli/Commands/Make/Plugin.php b/cli/Commands/Make/Plugin.php
new file mode 100644
index 0000000..4f1f9df
--- /dev/null
+++ b/cli/Commands/Make/Plugin.php
@@ -0,0 +1,299 @@
+<?php
+
+namespace Studip\Cli\Commands\Make;
+
+use Nette\PhpGenerator\PhpFile;
+use Nette\PhpGenerator\PsrPrinter;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Question\ChoiceQuestion;
+use Symfony\Component\Console\Question\ConfirmationQuestion;
+use Symfony\Component\Console\Question\Question;
+
+final class Plugin extends Command
+{
+ private const VALID_PLUGIN_INTERFACES = [
+ \SystemPlugin::class,
+ \StandardPlugin::class,
+ \AdminCourseAction::class,
+ \AdminCourseContents::class,
+ \AdminCourseWidgetPlugin::class,
+ \AdministrationPlugin::class,
+ \DetailspagePlugin::class,
+ \ExternPagePlugin::class,
+ \FileSystemPlugin::class,
+ \FileUploadHook::class,
+ \ForumModule::class,
+ \HomepagePlugin::class,
+ \LibraryPlugin::class,
+ \MetricsPlugin::class,
+ \PortalPlugin::class,
+ \PrivacyPlugin::class,
+ \ScorePlugin::class,
+ \QuestionnaireAssignmentPlugin::class,
+ \WebServicePlugin::class,
+ ];
+
+ protected static $defaultName = 'make:plugin';
+
+ protected function configure(): void
+ {
+ $this->addArgument('name', InputArgument::OPTIONAL, 'Name of the plugin');
+ $this->addOption('origin', 'o', InputOption::VALUE_OPTIONAL, 'Origin of the plugin');
+ $this->addOption('description', 'd', InputOption::VALUE_OPTIONAL, 'Description of the plugin');
+ $this->addOption('plugin-version', 'pv', InputOption::VALUE_OPTIONAL, 'Version of the plugin');
+ $this->addOption('min-version', 'min', InputOption::VALUE_OPTIONAL, 'Minimum version of Stud.IP the plugin supports');
+ $this->addOption('max-version', 'max', InputOption::VALUE_OPTIONAL, 'Maximum version of Stud.IP the plugin supports');
+ $this->addOption('plugin-interfaces', 'I', InputOption::VALUE_OPTIONAL, 'Comma separated list of plugin interfaces');
+ $this->addOption('with-controller', 'c', InputOption::VALUE_OPTIONAL, 'Create default controller');
+ $this->addOption('force', 'F', InputOption::VALUE_NEGATABLE, 'Force creation of the plugin (even if a plugin with that name and origin already exists)', false);
+ $this->setDescription('Create a new plain plugin frame');
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output): int
+ {
+ $helper = $this->getHelper('question');
+
+ // Get name of the plugin (if not already passed via command line)
+ $name = $input->getArgument('name');
+ if ($name === null) {
+ $question = new Question('Please enter the name of the plugin: ');
+ $question->setMaxAttempts(3);
+ $question->setValidator(function ($name): string {
+ if (!$name) {
+ throw new \RuntimeException('The name of the plugin is required');
+ }
+
+ return $name;
+ });
+ $question->setTrimmable(true);
+ $name = $helper->ask($input, $output, $question);
+ }
+
+ // Get origin of the plugin (if not already passed via command line)
+ $origin = $input->getOption('origin');
+ if ($origin === null) {
+ $question = new Question('Please enter the origin of the plugin: ');
+ $question->setAutocompleterValues($this->getKnownOrigins());
+ $question->setMaxAttempts(3);
+ $question->setValidator(function ($origin): string {
+ if (!$origin) {
+ throw new \RuntimeException('The origin of the plugin is required');
+ }
+
+ return $origin;
+ });
+ $question->setTrimmable(true);
+ $origin = $helper->ask($input, $output, $question);
+ }
+
+ $interfaces = null;
+ if ($input->hasOption('plugin-interfaces')) {
+ $interfaces = explode(',', $input->getOption('plugin-interfaces'));
+ $interfaces = array_filter($interfaces);
+ $interfaces = array_intersect($interfaces, self::VALID_PLUGIN_INTERFACES);
+ $interfaces = $interfaces ?: null;
+ }
+ $withDefaultController = $input->getOption('with-controller');
+
+ if (!$input->getOption('no-interaction')) {
+ $version = $input->getOption('plugin-version');
+ if ($version === null) {
+ $question = new Question('Please enter the version of the plugin: ', '1.0');
+ $version = $helper->ask($input, $output, $question);
+ }
+
+ $minVersion = $input->getOption('min-version');
+ if ($minVersion === null) {
+ $question = new Question('Please enter the studipMinVersion of the plugin: ', '');
+ $minVersion = $helper->ask($input, $output, $question);
+ }
+
+ $maxVersion = $input->getOption('max-version');
+ if ($maxVersion === null) {
+ $question = new Question('Please enter the studipMaxVersion of the plugin: ', '');
+ $maxVersion = $helper->ask($input, $output, $question);
+ }
+
+ $description = $input->getOption('description');
+ if ($description === null) {
+ $question = new Question('Please enter the description of the plugin: ');
+ $description = $helper->ask($input, $output, $question);
+ }
+
+ if ($interfaces === null) {
+ $question = new ChoiceQuestion(
+ 'Please enter the interfaces of the plugin: ',
+ self::VALID_PLUGIN_INTERFACES,
+ 0
+ );
+ $question->setMultiselect(true);
+ $interfaces = $helper->ask($input, $output, $question);
+ }
+
+ if ($withDefaultController === null) {
+ $question = new ConfirmationQuestion(
+ 'Should controller classes be created? (y/n) ',
+ false,
+ '/^(y|j)/i'
+ );
+ $withDefaultController = $helper->ask($input, $output, $question);
+ }
+ }
+
+ // Cleanup
+ $className = strtopascalcase($name);
+ $interfaces = $interfaces ?? [\SystemPlugin::class];
+
+ $pluginPath = $GLOBALS['STUDIP_BASE_PATH'] . "/public/plugins_packages/$origin/$className";
+
+ if (
+ file_exists($pluginPath)
+ && !$input->getOption('force')
+ ) {
+ $question = new ConfirmationQuestion(
+ 'There is already a plugin with that origin and name. Overwrite? (y/n) ',
+ false,
+ '/^(y|j)/i'
+ );
+ if (!$helper->ask($input, $output, $question)) {
+ $output->writeln('<error>Aborted');
+ exit;
+ }
+ }
+
+ mkdir($pluginPath, 0755, true);
+ mkdir("$pluginPath/controllers", 0755, true);
+ mkdir("$pluginPath/views", 0755, true);
+ mkdir("$pluginPath/lib/classes/", 0755, true);
+ mkdir("$pluginPath/lib/models", 0755, true);
+ mkdir("$pluginPath/migrations", 0755, true);
+
+ file_put_contents(
+ "$pluginPath/plugin.manifest",
+ $this->generatePluginManifest(
+ $name,
+ $className,
+ $origin,
+ $version ?? '',
+ $minVersion ?? '',
+ $maxVersion ?? '',
+ $description ?? ''
+ )
+ );
+
+ // Generate Plugin-Class
+ $file = new PhpFile();
+ $class = $file->addClass($className);
+ $class->setExtends(\StudIPPlugin::class);
+ foreach ($interfaces as $interface) {
+ $class->addImplement($interface);
+ }
+
+ $method = $class->addMethod('__construct');
+ $method->addBody('parent::__construct();');
+ $method = $class->addMethod('perform');
+ $method->addParameter('unconsumed_path');
+ $method->addBody("//Import here styles or scripts for example");
+ $method->addBody('parent::perform($unconsumed_path);');
+
+ foreach ($interfaces as $interface) {
+ foreach (get_class_methods($interface) as $method) {
+ $class->inheritMethod($method);
+ };
+ }
+
+ $printer = new PsrPrinter();
+ $result = $printer->printFile($file);
+
+ // Include requiring of bootstrap
+ $result = str_replace(
+ '<?php',
+ '<?php' . PHP_EOL . 'require __DIR__ . \'/bootstrap.php\';' . PHP_EOL,
+ $result
+ );
+ $filename = "$pluginPath/$className.php";
+ file_put_contents($filename, $result);
+
+ // Create bootstrap
+ $bootstrap = implode(PHP_EOL, [
+ '<?php',
+ 'StudipAutoloader::addAutoloadPath(__DIR__ . \'/lib/classes\');',
+ 'StudipAutoloader::addAutoloadPath(__DIR__ . \'/lib/models\');',
+ ]);
+ file_put_contents(
+ "$pluginPath/bootstrap.php",
+ $bootstrap
+ );
+
+ if ($withDefaultController) {
+ $controller_name = is_string($withDefaultController) ? strtolower($withDefaultController) : 'show';
+
+ // Create default controller an views
+ $file = new PhpFile();
+ $class = $file->addClass(strtopascalcase($controller_name . ' Controller'));
+ $class->addProperty('_autobind', true);
+ $class->setExtends(\PluginController::class);
+ $method = $class->addMethod('index_action');
+ $method->addBody("//add your code here");
+
+ $printer = new PsrPrinter();
+ $result = $printer->printFile($file);
+ $filename = "$pluginPath/controllers/$controller_name.php";
+
+ file_put_contents($filename, $result);
+ $viewPath = "$pluginPath/views/$controller_name";
+ mkdir($viewPath, 0755, true);
+
+ file_put_contents("$viewPath/index.php", $result);
+
+ }
+
+ $output->writeln('<info>Your plugin has been created!</info>');
+
+ return Command::SUCCESS;
+ }
+
+
+ private function generatePluginManifest(
+ string $name,
+ string $class_name,
+ string $origin,
+ string $version,
+ string $minVersion,
+ string $maxVersion,
+ string $description,
+ ): string {
+ if ($version === '') {
+ $version = '1.0';
+ }
+
+ $manifest = "pluginname=$name\n";
+ $manifest .= "pluginclassname=$class_name\n";
+ $manifest .= "origin=$origin\n";
+ $manifest .= "version=$version\n";
+
+ if ($description) {
+ $manifest .= "description=$description\n";
+ }
+ if ($minVersion) {
+ $manifest .= "studipMinVersion=$minVersion\n";
+ }
+ if ($maxVersion) {
+ $manifest .= "studipMaxVersion=$maxVersion\n";
+ }
+
+ return $manifest;
+ }
+
+ private function getKnownOrigins(): array
+ {
+ $origins = glob($GLOBALS['STUDIP_BASE_PATH'] . '/public/plugins_packages/*', GLOB_ONLYDIR);
+ $origins = array_map('basename', $origins);
+ natcasesort($origins);
+ return $origins;
+ }
+}
diff --git a/cli/Commands/Make/StudipClassPrinter.php b/cli/Commands/Make/StudipClassPrinter.php
deleted file mode 100644
index 5e86085..0000000
--- a/cli/Commands/Make/StudipClassPrinter.php
+++ /dev/null
@@ -1,18 +0,0 @@
-<?php
-namespace Studip\Cli\Commands\Make;
-
-use Nette\PhpGenerator\Printer;
-
-final class StudipClassPrinter extends Printer
-{
- /** length of the line after which the line will break */
- public int $wrapLength = 120;
- /** indentation character, can be replaced with a sequence of spaces */
- public string $indentation = ' ';
- /** number of blank lines between properties */
- public int $linesBetweenProperties = 0;
- /** number of blank lines between methods */
- public int $linesBetweenMethods = 1;
-
- public string $returnTypeColon = ': ';
-}
diff --git a/cli/studip b/cli/studip
index 4d19e2b..8effcae 100755
--- a/cli/studip
+++ b/cli/studip
@@ -34,6 +34,7 @@ $commands = [
Commands\DB\MoveMatrikelnummer::class,
Commands\Make\Migration::class,
Commands\Make\Model::class,
+ Commands\Make\Plugin::class,
Commands\DI\Reset::class,
Commands\Files\Dump::class,
Commands\Fix\Biest7789::class,