diff --git a/cli/Commands/AbstractCommand.php b/cli/Commands/AbstractCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..b3ce3cca185563fb74133b43c8163505693b80da --- /dev/null +++ b/cli/Commands/AbstractCommand.php @@ -0,0 +1,72 @@ +<?php +namespace Studip\Cli\Commands; + +use FilesystemIterator; +use Iterator; +use RecursiveDirectoryIterator; +use RecursiveIteratorIterator; +use RecursiveRegexIterator; +use RegexIterator; +use Symfony\Component\Console\Command\Command; + +abstract class AbstractCommand extends Command +{ + /** + * Returns a folder iterator accessing all files inside that folder. + * + * @param string $folder Folder to return iterator for + * @param bool $recursive Recurse into subfolders as well + * @param array|null $extensions Optional list of extensions for files to be returned + * + * @return Iterator + */ + protected function getFolderIterator(string $folder, bool $recursive = false, ?array $extensions = null): Iterator + { + if ($recursive) { + $iterator = new RecursiveDirectoryIterator( + $folder, + FilesystemIterator::FOLLOW_SYMLINKS | FilesystemIterator::UNIX_PATHS + ); + $iterator = new RecursiveIteratorIterator($iterator); + } else { + $iterator = new FilesystemIterator($folder); + } + + if ($extensions) { + $extensions = array_map(function ($extension) { + return preg_quote($extension, '/'); + }, $extensions); + $iterator = new RegexIterator( + $iterator, + '/\.(?:' . implode('|', $extensions) . ')$/', + RecursiveRegexIterator::MATCH + ); + } + + return $iterator; + } + + protected function relativeFilePath(string $filepath, bool $plugin = false): string + { + $filepath = str_replace($GLOBALS['STUDIP_BASE_PATH'] . '/', '', $filepath); + + if ($plugin) { + $filepath = str_replace('public/plugins_packages/', '', $filepath); + } + + return $filepath; + } + + protected function absoluteFilePath(string $filepath, bool $plugin = false): string + { + if ($plugin && mb_strpos($filepath, 'public/plugins_packages') === false) { + $filepath = 'public/plugins_packages/' . ltrim($filepath, '/'); + } + + if (mb_strpos($filepath, $GLOBALS['STUDIP_BASE_PATH']) === false) { + $filepath = $GLOBALS['STUDIP_BASE_PATH'] . '/' . ltrim($filepath, '/'); + } + + return $filepath; + } +} diff --git a/cli/Commands/AbstractPluginCommand.php b/cli/Commands/AbstractPluginCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..149f54c231aef1afa2d0af7dcd5ecc7df5bfd134 --- /dev/null +++ b/cli/Commands/AbstractPluginCommand.php @@ -0,0 +1,21 @@ +<?php +namespace Studip\Cli\Commands; + +abstract class AbstractPluginCommand extends AbstractCommand +{ + protected function findPluginByName(\PluginManager $pluginManager, string $pluginname): ?array + { + $plugins = $pluginManager->getPluginInfos(); + $found = array_filter($plugins, function ($plugin) use ($pluginname) { + return mb_strtolower($pluginname) === mb_strtolower($plugin['name']); + }); + + return count($found) ? reset($found) : null; + } + + protected function findPluginNameByFolder(string $folder) + { + var_dump('foo');die; + return 'foo'; + } +} diff --git a/cli/Commands/Base/Dump.php b/cli/Commands/Base/Dump.php new file mode 100644 index 0000000000000000000000000000000000000000..39be0a27f5f57f46fd04c94b03ed13ec2b8d436f --- /dev/null +++ b/cli/Commands/Base/Dump.php @@ -0,0 +1,51 @@ +<?php + +namespace Studip\Cli\Commands\Base; + +use Config; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +class Dump extends Command +{ + protected static $defaultName = 'base:dump'; + + protected function configure(): void + { + $this->setDescription('Dumping Stud.IP directory'); + $this->addArgument('path', InputArgument::REQUIRED, 'path where the backup should be saved'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + + $dump_dir = realpath($input->getArgument('path')); + $prefix = Config::get()->STUDIP_INSTALLATION_ID ? Config::get()->STUDIP_INSTALLATION_ID : 'studip'; + $today = date('Ymd'); + + $base_path = realpath($GLOBALS['STUDIP_BASE_PATH']); + + if (!$base_path) { + $io->error('Stud.IP directory not found!'); + return Command::FAILURE; + } + + $dumb_studip = $dump_dir . '/' . $prefix . '-BASE-' . $today . '.tar.gz'; + + $io->info('Dumping Stud.IP directory to' . $base_path); + + $cmd = "cd $base_path && tar -czf $dumb_studip ." . ' 2>&1'; + + exec($cmd, $output, $ok); + + if ($ok > 0) { + $io->error(join("\n", array_merge([$cmd], $output))); + return Command::FAILURE; + } + return Command::SUCCESS; + } +} diff --git a/cli/Commands/Checks/Compatibility.php b/cli/Commands/Checks/Compatibility.php new file mode 100644 index 0000000000000000000000000000000000000000..4e6cd3ee1e0e4385f4fd26b8b459c963d3eb8053 --- /dev/null +++ b/cli/Commands/Checks/Compatibility.php @@ -0,0 +1,167 @@ +<?php +namespace Studip\Cli\Commands\Checks; + +use DirectoryIterator; +use FilesystemIterator; +use RecursiveDirectoryIterator; +use RecursiveIteratorIterator; +use RecursiveRegexIterator; +use RegexIterator; +use Studip\Cli\Commands\AbstractCommand; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class Compatibility extends AbstractCommand +{ + protected static $defaultName = 'check:compatibility'; + + protected function configure(): void + { + $this->setDescription('Compatibility scanner'); + $this->setHelp('Scans plugins for common issues (backward compatibility and the like)'); + + $this->addArgument( + 'version', + InputArgument::OPTIONAL, + 'Version to check against (if not suppied, all checks are performed)' + ); + + $this->addArgument( + 'folder', + InputArgument::IS_ARRAY, + 'Folder to scan (will default to the plugins_packages folder)' + ); + + $this->addOption('filenames', 'f', InputOption::VALUE_NONE, 'Display filenames only'); + $this->addOption( + 'recursive', + 'r', + InputOption::VALUE_NONE | InputOption::VALUE_NEGATABLE, + 'Do not scan recursively into subfolders' + ); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $output->getFormatter()->setStyle('issue', new OutputFormatterStyle('red')); + $output->getFormatter()->setStyle('bold', new OutputFormatterStyle(null, null, ['bold'])); + + $rules = $this->getCompatibilityRules($input->getArgument('version')); + $folders = $input->getArgument('folder') ?: $this->getDefaultFolders(); + $recursive = $input->getOption('recursive') ?? true; + + foreach ($folders as $f) { + $folder = $this->validateFolder($f); + if (!$folder) { + $output->writeln("<info>Skipping invalid folder {$f}</info>", OutputInterface::VERBOSITY_VERBOSE); + + continue; + } + + $issues = []; + foreach ($this->getFolderIterator($folder, $recursive, ['php', 'tpl', 'inc', 'js']) as $file) { + $filename = $file->getPathName(); + $output->writeln("<info>Checking {$filename}", OutputInterface::VERBOSITY_VERBOSE); + if ($errors = $this->checkFilecontentsAgainstRules($filename, $rules)) { + $issues[$filename] = $errors; + } + } + + if (count($issues) === 0) { + continue; + } + + if (!$input->getOption('filenames')) { + $issue_count = array_sum(array_map('count', $issues)); + $message = count($issues) === 1 + ? '%u issue found in <bold>%s</bold>' + : '%u issues found in <bold>%s</bold>'; + + $output->writeln(sprintf( + "<issue>{$message}</issue>", + $issue_count, + $this->relativeFilePath($folder) + )); + } + + foreach ($issues as $filename => $errors) { + if ($input->getOption('filenames')) { + $output->writeln($filename); + } else { + $output->writeln(sprintf( + '> File <fg=green;options=bold>%s</>', + $this->relativeFilePath($filename) + )); + foreach ($errors as $needle => $suggestion) { + $output->writeln( + sprintf('- <fg=cyan>%s</> -> %s', $needle, $suggestion ?: '<fg=red>No suggestion available') + ); + } + } + } + } + + return Command::SUCCESS; + } + + private function getCompatibilityRules(?string $version): array + { + if ($version !== null) { + if (!file_exists(__DIR__ . "/compatibility-rules/studip-{$version}.php")) { + throw new \Exception("No rules defined for Stud.IP version {$version}"); + } + + return require __DIR__ . "/compatibility-rules/studip-{$version}.php"; + } + + $rules = []; + foreach (glob(__DIR__ . '/compatbility-rules/*.php') as $file) { + $version_rules = require $file; + $rules = array_merge($rules, $version_rules); + } + + return $rules; + } + + private function getDefaultFolders(): array + { + $folders = rtrim($GLOBALS['STUDIP_BASE_PATH'], '/') . '/public/plugins_packages'; + $folders = glob($folders . '/*/*'); + return $folders; + } + + private function validateFolder(string $folder) + { + if (!file_exists($folder) || !is_dir($folder)) { + return false; + } + + return $folder; + } + + private function checkFilecontentsAgainstRules(string $filename, array $rules) + { + $errors = []; + + $contents = strtolower(file_get_contents($filename)); + foreach ($rules as $needle => $suggestion) { + if ($this->checkRule($contents, $needle)) { + $errors[$needle] = $suggestion; + } + } + return $errors; + } + + private function checkRule(string $contents, string $rule) + { + if ($rule[0] === '/' && $rule[strlen($rule) - 1] === '/') { + return (bool) preg_match("{$rule}s", $contents); + } + + return strpos($contents, strtolower($rule)) > 0; + } +} diff --git a/cli/Commands/Checks/GlobalizedConfig.php b/cli/Commands/Checks/GlobalizedConfig.php new file mode 100644 index 0000000000000000000000000000000000000000..373534a8b2aa17f12666b18fea3c2b77b2f9fabc --- /dev/null +++ b/cli/Commands/Checks/GlobalizedConfig.php @@ -0,0 +1,148 @@ +<?php +namespace Studip\Cli\Commands\Checks; + +use Config; +use DirectoryIterator; +use FilesystemIterator; +use RecursiveDirectoryIterator; +use RecursiveIteratorIterator; +use RecursiveRegexIterator; +use RegexIterator; +use Studip\Cli\Commands\AbstractCommand; +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; + +final class GlobalizedConfig extends AbstractCommand +{ + protected static $defaultName = 'check:globalized-config'; + + protected function configure(): void + { + $this->setDescription( + '<href=https://develop.studip.de/trac/ticket/5671>TIC 5671</> scanner - Globalized config' + ); + $this->setHelp( + 'Scans files for occurences of globalized config items (see <href=https://develop.studip.de/trac/ticket/5671>ticket 5671</> for more info)' + ); + + $this->addOption('filenames', 'f', InputOption::VALUE_NONE, 'Display filenames only (excludes -m and -o)'); + $this->addOption('matches', 'm', InputOption::VALUE_NONE, 'Show matched config variables'); + $this->addOption( + 'recursive', + 'r', + InputOption::VALUE_NONE | InputOption::VALUE_NEGATABLE, + 'Do not scan recursively into subfolders' + ); + $this->addOption('occurences', 'o', InputOption::VALUE_NONE, 'Show occurences in files'); + + $this->addArgument( + 'folder', + InputArgument::IS_ARRAY | InputArgument::OPTIONAL, + 'Folder(s) to scan (pass the special value of "plugins" to scan the plugin folder)', + [$GLOBALS['STUDIP_BASE_PATH']] + ); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $only_filenames = (bool) $input->getOption('filenames'); + $show_occurences = !$only_filenames && ($output->isVerbose() || $input->getOption('occurences')); + $show_matches = !$only_filenames && ($show_occurences || $input->getOption('matches')); + $recursive = $input->getOption('recursive') ?? true; + + $folders = $input->getArgument('folder'); + foreach ($folders as $index => $folder) { + if ($folder === 'plugins') { + $folders[$index] = $GLOBALS['STUDIP_BASE_PATH'] . '/public/plugins_packages/'; + } + } + $folders = array_unique($folders); + + $config = Config::get()->getFields('global'); + $quoted = array_map(function ($item) { + return preg_quote($item, '/'); + }, $config); + $regexp = '/\$(?:GLOBALS\[["\']?)?(' . implode('|', $quoted) . ')\b/S'; + + foreach ($folders as $folder) { + if (!file_exists($folder) || !is_dir($folder)) { + $output->writeln( + "Skipping non-folder argument <fg=red>{$folder}</>", + OutputInterface::VERBOSITY_VERBOSE + ); + continue; + } + $output->writeln("Scanning {$folder}", OutputInterface::VERBOSITY_VERBOSE); + + foreach ($this->getFolderIterator($folder, $recursive, ['php', 'tpl', 'inc']) as $file) { + $filename = $file->getPathName(); + $contents = file_get_contents($filename); + + $output->writeln( + sprintf( + 'Check <fg=magenta>%s</>', + $this->relativeFilePath($filename) + ), + OutputInterface::VERBOSITY_VERBOSE + ); + + if ($matched = preg_match_all($regexp, $contents, $matches)) { + if ($only_filenames) { + $output->writeln($filename); + } else { + $output->writeln( + sprintf( + '%u matched variable(s) in <fg=green;options=bold>%s</>', + $matched, + $this->shorten($filename) + ) + ); + if ($show_matches) { + $variables = array_unique($matches[1]); + foreach ($variables as $variable) { + $output->writeln("> <fg=cyan>{$variable}</>"); + if ($show_occurences) { + $output->writeln($this->highlight($contents, $variable)); + } + } + } + } + } + } + } + + return Command::SUCCESS; + } + + private function highlight(string $content, string $variable): string + { + $lines = explode("\n", $content); + + $result = []; + foreach ($lines as $index => $line) { + if (mb_strpos($line, $variable) === false) { + continue; + } + $result[$index + 1] = $line; + } + + if (!$result) { + return ''; + } + + $max = max(array_map('mb_strlen', array_keys($result))); + + foreach ($result as $index => $line) { + $result[$index] = sprintf( + "<fg=yellow>:%0{$max}u:</> %s", + $index, + str_replace($variable, "<fg=black;bg=yellow>{$variable}</>", $line) + ); + } + + return implode("\n", $result); + } +} diff --git a/cli/Commands/Checks/HelpTours.php b/cli/Commands/Checks/HelpTours.php new file mode 100644 index 0000000000000000000000000000000000000000..04d66c0335efbff9cd59467ad83f4f00d6b8a168 --- /dev/null +++ b/cli/Commands/Checks/HelpTours.php @@ -0,0 +1,114 @@ +<?php + +namespace Studip\Cli\Commands\Checks; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +class HelpTours extends Command +{ + protected static $defaultName = 'check:helptours'; + + protected function configure(): void + { + $this->setDescription('Checks help tours for validity.'); + $this->setHelp('This command will check all active help tours if the sites used are still available'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + + foreach (\HelpTour::findBySQL('1 ORDER BY name ASC') as $tour) { + if (!$tour->settings->active) { + if ($output->isVerbose()) { + $tour_name = $this->getTourName($tour); + $io->info("Skipping inactive tour {$tour_name}"); + } + + continue; + } + + $errors = []; + foreach ($tour->steps->orderBy('step ASC') as $step) { + try { + if (match_route('plugins.php/*', $step->route)) { + $result = \PluginEngine::routeRequest(substr($step->route, strlen('plugins.php') + 1)); + + // retrieve corresponding plugin info + $plugin_manager = \PluginManager::getInstance(); + $plugin_info = $plugin_manager->getPluginInfo($result[0]); + + $file = implode('/', [ + \Config::get()->PLUGINS_PATH, + $plugin_info['path'], + $plugin_info['class'], + ]); + + if (file_exists($file . '.php')) { + $file .= '.php'; + } elseif (file_exists($file . '.class.php')) { + $file .= '.class.php'; + } else { + throw new \Exception(); + } + require_once $file; + $plugin = new $plugin_info['class'](); + + if ($result[1]) { + $dispatcher = new \Trails_Dispatcher( + $GLOBALS['ABSOLUTE_PATH_STUDIP'] . $plugin->getPluginPath(), + rtrim(\PluginEngine::getLink($plugin, [], null, true), '/'), + 'index' + ); + $dispatcher->current_plugin = $plugin; + $parsed = $dispatcher->parse($result[1]); + $controller = $dispatcher->load_controller($parsed[0]); + if ($parsed[1] && !$controller->has_action($parsed[1])) { + throw new \Exception(); + } + } + } elseif (match_route('dispatch.php/*', $step->route)) { + $dispatcher = new \StudipDispatcher(); + $parsed = $dispatcher->parse(substr($step->route, strlen('dispatch.php') + 1)); + $controller = $dispatcher->load_controller($parsed[0]); + if ($parsed[1] && !$controller->has_action($parsed[1])) { + throw new \Exception(); + } + } elseif (!file_exists("{$GLOBALS['ABSOLUTE_PATH_STUDIP']}{$step->route}")) { + throw new \Exception(); + } + } catch (\Exception $e) { + $errors[$step->step] = $step->route; + } + } + + if ($errors) { + $tour_name = $this->getTourName($tour); + $io->error("{$tour_name} has errors in the following steps:"); + + $io->table( + ['Step', 'Route'], + array_map( + function ($step, $route) { + return [$step, $route]; + }, + array_keys($errors), + array_values($errors) + ) + ); + } + } + + return Command::SUCCESS; + } + + private function getTourName(\HelpTour $tour) + { + $type = ucfirst($tour->type); + return "{$type} '{$tour->name}' ({$tour->language})"; + } +} diff --git a/cli/Commands/Checks/compatibility-rules/studip-4.0.php b/cli/Commands/Checks/compatibility-rules/studip-4.0.php new file mode 100644 index 0000000000000000000000000000000000000000..0b3fa22f6481b5f2652c9a284a96108bc22cc704 --- /dev/null +++ b/cli/Commands/Checks/compatibility-rules/studip-4.0.php @@ -0,0 +1,178 @@ +<?php +// "Rules"/definitions for critical changes in 4.0 +return [ + 'cssClassSwitcher' => 'Remove completely, use <fg=yellow><table class="default"></> instead.', + '$csssw' => '[<fg=cyan>cssClassSwitcher</>] Remove completely, use <fg=yellow><table class="default"></> instead.', + + 'DBMigration' => 'Use <fg=yellow>Migration</> instead', + + 'Request::removeMagicQuotes()' => 'Remove completely since magic quotes are removed from php', + + 'base_without_infobox' => 'Use <fg=yellow>layouts/base.php</> instead.', + 'deprecated_tabs_layout' => 'Don\'t use this. Use the global layout <fg=yellow>layouts/base.php</> and <fg=yellow>Navigation</> instead.', + 'setInfoBoxImage' => 'Replace with <fg=yellow>Sidebar</>', + 'addToInfobox' => 'Replace with <fg=yellow>Sidebar</>', + 'InfoboxElement' => 'Replace with appropriate <fg=yellow>Sidebar</> element', + 'InfoboxWidget' => 'Replace with appropriate <fg=yellow>Sidebar</> widget', + + 'details.php' => 'Link to <fg=yellow>dispatch.php/course/details</> instead', + 'institut_main.php' => 'Link to <fg=yellow>dispatch.php/institute/overview</> instead', + 'meine_seminare.php' => 'Link to <fg=yellow>dispatch.php/my_courses</> instead', + 'sms_box.php' => 'Link to <fg=yellow>dispatch.php/messages/overview</> or <fg=yellow>dispatch.php/messages/sent</> instead', + 'sms_send.php' => 'Link to <fg=yellow>dispatch.php/messages/write</> instead', + + 'get_global_perm' => 'Use <fg=yellow>$GLOBALS[\'perm\']->get_perm()</> instead', + 'log_event(' => 'Use <fg=yellow>StudipLog::log()</> instead', + '->removeOutRangedSingleDates' => 'Use <fg=yellow>SeminarCycleDate::removeOutRangedSingleDates</> instead', + + 'HolidayData' => 'Use class <fg=yellow>SemesterHoliday</> instead', + + 'CourseTopic::createFolder' => 'Use <fg=yellow>CourseTopic::connectWithDocumentFolder()</> instead', + 'SimpleORMap::haveData' => 'Use <fg=yellow>SimpleORMap::isDirty()</> or <fg=yellow>SimpleORMap::isNew()</> instead', + 'Seminar::getMetaDateType' => 'Don\'t use this!', + 'UserConfig::setUserId' => 'Don\'t use this. <fg=yellow>Set the user via the constructor</>.', + + 'StudIPTemplateEngine' => 'Time to refactor your plugin.', + 'AbstractStudIPAdministrationPlugin' => 'Time to refactor your plugin.', + 'AbstractStudIPCorePlugin' => 'Time to refactor your plugin.', + 'AbstractStudIPHomepagePlugin' => 'Time to refactor your plugin.', + 'AbstractStudIPLegacyPlugin' => 'Time to refactor your plugin.', + 'AbstractStudIPPortalPlugin' => 'Time to refactor your plugin.', + 'AbstractStudIPStandardPlugin' => 'Time to refactor your plugin.', + 'AbstractStudIPSystemPlugin' => 'Time to refactor your plugin.', + 'new Permission(' => 'Time to refactor your plugin.', + 'Permission::' => 'Time to refactor your plugin.', + 'PluginNavigation' => 'Time to refactor your plugin.', + 'new StudIPUser(' => 'Time to refactor your plugin.', + 'StudIPUser::' => 'Time to refactor your plugin.', + 'StudipPluginNavigation' => 'Time to refactor your plugin.', + 'getLinkToAdministrationPlugin' => 'Time to refactor your plugin.', + 'getCurrentPluginId' => 'Time to refactor your plugin.', + 'saveToSession' => 'Time to refactor your plugin.', + 'getValueFromSession' => 'Time to refactor your plugin.', + + 'ContainerTable' => false, + 'DbCrossTableView' => false, + 'DbPermissions' => false, + + 'pclzip' => 'Use <fg=yellow>Studip\\ZipArchive</> instead', + + 'getSeminarRoomRequest' => 'Use <fg=yellow>RoomRequest</> model instead', + 'getDateRoomRequest' => 'Use <fg=yellow>RoomRequest</> model instead', + + 'ldate' => 'Use PHP\'s <fg=yellow>date()</> or <fg=yellow>strftime()</> function instead', + 'day_diff' => 'Use PHP\'s <fg=yellow>DateTime::diff()</> method instead', + 'get_day_name' => 'Use PHP\'s <fg=yellow>strftime()</> function with <fg=yellow>parameter \'%A\'</> instead', + 'wday(' => 'Use <fg=yellow>strftime("%a")</> or <fg=yellow>strftime("%A")</> instead', + + 'get_ampel_state' => false, + 'get_ampel_write' => false, + 'get_ampel_read' => false, + 'localePictureUrl' => false, + 'localeUrl' => false, + 'isDatesMultiSem' => false, + 'getMetadateCorrespondingDates' => false, + 'getCorrespondingMetadates' => false, + 'create_year_view' => false, + 'javascript_hover_year' => false, + 'js_hover' => false, + 'info_icons' => false, + + 'get_message_attachments' => 'Use <fg=yellow>Message::attachments</> attribute instead', + 'view_turnus' => 'Use <fg=yellow>Seminar::getFormattedTurnus()</> instead', + + 'AddNewStatusgruppe' => 'Use class <fg=yellow>Statusgruppe</> or model <fg=yellow>Statusgruppen</> instead (yupp, this is still pretty fucked up).', + 'CheckSelfAssign' => 'Use class <fg=yellow>Statusgruppe</> or model <fg=yellow>Statusgruppen</> instead (yupp, this is still pretty fucked up).', + 'CheckSelfAssignAll' => 'Use class <fg=yellow>Statusgruppe</> or model <fg=yellow>Statusgruppen</> instead (yupp, this is still pretty fucked up).', + 'CheckAssignRights' => 'Use class <fg=yellow>Statusgruppe</> or model <fg=yellow>Statusgruppen</> instead (yupp, this is still pretty fucked up).', + 'SetSelfAssignAll' => 'Use class <fg=yellow>Statusgruppe</> or model <fg=yellow>Statusgruppen</> instead (yupp, this is still pretty fucked up).', + 'SetSelfAssignExclusive' => 'Use class <fg=yellow>Statusgruppe</> or model <fg=yellow>Statusgruppen</> instead (yupp, this is still pretty fucked up).', + 'EditStatusgruppe' => 'Use class <fg=yellow>Statusgruppe</> or model <fg=yellow>Statusgruppen</> instead (yupp, this is still pretty fucked up).', + 'MovePersonPosition' => 'Use class <fg=yellow>Statusgruppe</> or model <fg=yellow>Statusgruppen</> instead (yupp, this is still pretty fucked up).', + 'SortPersonInAfter' => 'Use class <fg=yellow>Statusgruppe</> or model <fg=yellow>Statusgruppen</> instead (yupp, this is still pretty fucked up).', + 'SortStatusgruppe' => 'Use class <fg=yellow>Statusgruppe</> or model <fg=yellow>Statusgruppen</> instead (yupp, this is still pretty fucked up).', + 'SubSortStatusgruppe' => 'Use class <fg=yellow>Statusgruppe</> or model <fg=yellow>Statusgruppen</> instead (yupp, this is still pretty fucked up).', + 'resortStatusgruppeByRangeId' => 'Use class <fg=yellow>Statusgruppe</> or model <fg=yellow>Statusgruppen</> instead (yupp, this is still pretty fucked up).', + 'SwapStatusgruppe' => 'Use class <fg=yellow>Statusgruppe</> or model <fg=yellow>Statusgruppen</> instead (yupp, this is still pretty fucked up).', + 'CheckStatusgruppe' => 'Use class <fg=yellow>Statusgruppe</> or model <fg=yellow>Statusgruppen</> instead (yupp, this is still pretty fucked up).', + 'GetRangeOfStatusgruppe' => 'Use class <fg=yellow>Statusgruppe</> or model <fg=yellow>Statusgruppen</> instead (yupp, this is still pretty fucked up).', + 'GetGroupsByCourseAndUser' => 'Use class <fg=yellow>Statusgruppe</> or model <fg=yellow>Statusgruppen</> instead (yupp, this is still pretty fucked up).', + 'getOptionsOfStGroups' => 'Use class <fg=yellow>Statusgruppe</> or model <fg=yellow>Statusgruppen</> instead (yupp, this is still pretty fucked up).', + 'setOptionsOfStGroup' => 'Use class <fg=yellow>Statusgruppe</> or model <fg=yellow>Statusgruppen</> instead (yupp, this is still pretty fucked up).', + 'GetStatusgruppeLimit' => 'Use class <fg=yellow>Statusgruppe</> or model <fg=yellow>Statusgruppen</> instead (yupp, this is still pretty fucked up).', + 'CheckStatusgruppeFolder' => 'Use class <fg=yellow>Statusgruppe</> or model <fg=yellow>Statusgruppen</> instead (yupp, this is still pretty fucked up).', + 'CheckStatusgruppeMultipleAssigns' => 'Use class <fg=yellow>Statusgruppe</> or model <fg=yellow>Statusgruppen</> instead (yupp, this is still pretty fucked up).', + 'sortStatusgruppeByName' => 'Use class <fg=yellow>Statusgruppe</> or model <fg=yellow>Statusgruppen</> instead (yupp, this is still pretty fucked up).', + 'getPersons(' => 'Use class <fg=yellow>Statusgruppe</> or model <fg=yellow>Statusgruppen</> instead (yupp, this is still pretty fucked up).', + 'getSearchResults(' => 'Use class <fg=yellow>Statusgruppe</> or model <fg=yellow>Statusgruppen</> instead (yupp, this is still pretty fucked up).', + 'setExternDefaultForUser' => 'Use class <fg=yellow>Statusgruppe</> or model <fg=yellow>Statusgruppen</> instead (yupp, this is still pretty fucked up).', + + 'GetStatusgruppeName' => 'Use <fg=yellow>Statusgruppen::find($id)->name</> instead', + 'GetStatusgruppenForUser' => 'Use class <fg=yellow>Statusgruppe</> or model <fg=yellow>Statusgruppen</> instead (yupp, this is still pretty fucked up).', + + 'get_global_visibility_by_id' => 'Use <fg=yellow>User::find($id)->visible</> instead', + 'get_global_visibility_by_username' => 'Use <fg=yellow>User::findByUsername($username)->visible</> instead', + + 'get_local_visibility_by_username' => false, + 'get_homepage_element_visibility' => false, + 'set_homepage_element_visibility' => false, + 'checkVisibility' => 'Use <fg=yellow>Visibility::verify($param, $this->current_user->user_id)</> instead', + + 'InsertPersonStatusgruppe' => 'Use <fg=Statusgruppen>:addUser()</> instead', + 'RemovePersonStatusgruppe(' => 'Use <fg=yellow>Statusgruppen::find($group_id)->removeUser($user_id)</> instead', + 'RemovePersonStatusgruppeComplete' => 'Use <fg=yellow>Statusgruppen::find($group_id)->removeUser($user_id, true)</> instead. Maybe you will need to do this on a collection of groups for a course or institute.', + 'RemovePersonFromAllStatusgruppen' => 'Use <fg=yellow>StatusgruppeUser::deleteBySQL("user_id = ?", [$user_id])</> instead.', + 'DeleteAllStatusgruppen' => 'Use <fg=yellow>Statusgruppen::deleteBySQL("range_id = ?", [$id]);</> instead', + 'DeleteStatusgruppe' => 'Use <fg=yellow>Statusgruppen::delete()</> - or <fg=yellow>Statusgruppen::remove()</> if you want to keep the child groups.', + 'moveStatusgruppe' => false, + 'CheckUserStatusgruppe' => 'Use <fg=yellow>StatusgruppeUser::exists([$group_id, $user_id])</> instead.', + 'CountMembersStatusgruppen' => false, + 'CountMembersPerStatusgruppe' => false, + 'MakeDatafieldsDefault' => 'No longer neccessary.', + 'MakeUniqueStatusgruppeID' => 'No longer neccessary. SORM will create ids for you.', + 'GetAllSelected' => 'Use <fg=yellow>Statusgruppen::findAllByRangeId()</> instead.', + 'getStatusgruppenIDS' => 'Use <fg=yellow>Statusgruppen::findByRange_id()</> instead.', + 'getAllStatusgruppenIDS' => 'Use <fg=yellow>Statusgruppen::findAllByRangeId()</> instead.', + 'getPersonsForRole' => 'Use <fg=yellow>:Statusgruppen::members</> instead.', + 'isVatherDaughterRelation' => false, + 'SetSelfAssign(' => false, + 'getExternDefaultForUser' => 'Use <fg=yellow>InstituteMember::getDefaultInstituteIdForUser($user_id)</> instead.', + 'checkExternDefaultForUser' => 'Use <fg=yellow>InstituteMember::ensureDefaultInstituteIdForUser($user_id)</> instead.', + 'getAllChildIDs' => false, + 'getKingsInformations' => 'Use <fg=yellow>User</> model instead', + + 'AutoInsert::existSeminars' => false, + 'new ZebraTable' => 'No longer neccessary. Use <fg=yellow>table.default</> instead.', + 'new Table' => 'No longer neccessary. Use <fg=yellow>table.default</> instead.', + + //old datei.inc.php and visual.inc.php functions: + 'createSelectedZip' => 'Removed. Use <fg=yellow>FileArchiveManager::createArchiveFromFileRefs</> instead.', + 'create_zip_from_directory' => 'Removed(?). Use <fg=yellow>FileArchiveManager::createArchiveFromPhysicalFolder</> instead.', + 'getFileExtension' => 'Removed. Use PHP\'s built-in <fg=yellow>pathinfo($filename, PATHINFO_EXTENSION)</> instead.', + 'get_icon_for_mimetype' => 'Removed. Use <fg=yellow>FileManager::getIconNameForMimeType</> instead.', + 'get_upload_file_path' => 'Removed. Use <fg=yellow>File->getPath()</> instead.', + 'GetDownloadLink' => 'Removed. Use one of the following alternatives instead: <fg=yellow>FileRef->getDownloadURL()</>, <fg=yellow>FileManager::getDownloadLinkForArchivedCourse</>, <fg=yellow>FileManager::getDownloadLinkForTemporaryFile</> or <fg=yellow>FileManager::getDownloadURLForTemporaryFile</>', + 'prepareFilename' => 'Removed. Use <fg=yellow>FileManager::cleanFileName</> instead.', + 'GetFileIcon' => 'Removed. Use <fg=yellow>FileManager::getIconNameForMimeType</> instead.', + 'parse_link' => 'Removed. Use <fg=yellow>FileManager::fetchURLMetadata</> instead.', + 'unzip_file' => 'Removed. Use <fg=yellow>Studip\ZipArchive::extractToPath</> or <fg=yellow>Studip\ZipArchive::test</> instead.', + 'datei.inc.php' => 'Removed. Use methods in functions.inc.php, FileManager, FileArchiveManager, FileRef, File or FolderType instead.', + 'TrackAccess' => 'Removed(?). Use <fg=yellow>:FileRef::incrementDownloadCounter</>', + //StudipDocument and related classes: + 'StudipDocument(' => 'Removed(?). Use class <fg=yellow>FileRef</> instead.', + 'DocumentFolder(' => 'Removed(?). Use class <fg=yellow>Folder</> instead.', + 'StudipDocumentTree(' => 'Removed(?). Use class <fg=yellow>Folder</> or <fg=yellow>FolderType</> instead.', + 'WysiwygDocument' => 'Deprecated/To be removed. Use class <fg=yellow>FileRef</> in conjunction with a <fg=yellow>FolderType</> implementation instead.', + + 'ZIP_USE_INTERNAL' => 'Removed. Please avoid querying the value of this configuration variable!', + 'ZIP_PATH' => 'Removed. Please avoid querying the value of this configuration variable!', + 'ZIP_OPTIONS' => 'Removed. Please avoid querying the value of this configuration variable!', + 'UNZIP_PATH' => 'Removed. Please avoid querying the value of this configuration variable!', + + 'RuleAdministrationModel::getAdmissionRuleTypes' => 'Use <fg=yellow>AdmissionRule::getAvailableAdmissionRules(false)</> instead.', + 'SessSemName' => 'Use class <fg=yellow>Context</> instead', + '_SESSION["SessionSeminar"]' => 'Use class <fg=yellow>Context</> instead', + '_SESSION[\'SessionSeminar\']' => 'Use class <fg=yellow>Context</> instead', + + 'Statusgruppe(' => 'Removed(?). Use class <fg=yellow>Statusgruppen</> instead.', +]; diff --git a/cli/Commands/Checks/compatibility-rules/studip-4.2.php b/cli/Commands/Checks/compatibility-rules/studip-4.2.php new file mode 100644 index 0000000000000000000000000000000000000000..c051d7eb2c95bbcb8862f663afb0360defe30703 --- /dev/null +++ b/cli/Commands/Checks/compatibility-rules/studip-4.2.php @@ -0,0 +1,17 @@ +<?php +// "Rules"/definitions for critical changes in 4.2 +return [ + 'get_perm' => 'Use the <fg=yellow>CourseMember</> or <fg=yellow>InstitutMember</> model instead.', + 'get_vorname' => 'Use <fg=yellow>User::find($id)->vorname</> instead', + 'get_nachname' => 'Use <fg=yellow>User::find($id)->nachname</> instead', + 'get_range_tree_path' => false, + 'get_seminar_dozent' => 'Use <fg=yellow>Course::find($id)->getMembersWithStatus(\'dozent\')</> instead.', + 'get_seminar_tutor' => 'Use <fg=yellow>Course::find($id)->getMembersWithStatus(\'tutor\')</> instead.', + 'get_seminar_sem_tree_entries' => false, + 'get_seminars_users' => 'Use <fg=yellow>CourseMember::findByUser($user_id)</> instead to aquire all courses.', + 'remove_magic_quotes' => false, + 'text_excerpt' => false, + 'check_group_new' => false, + 'insertNewSemester' => 'Use the <fg=yellow>Semester</> model instead.', + 'updateExistingSemester' => 'Use the <fg=yellow>Semester</> model instead.', +]; diff --git a/cli/Commands/Checks/compatibility-rules/studip-4.4.php b/cli/Commands/Checks/compatibility-rules/studip-4.4.php new file mode 100644 index 0000000000000000000000000000000000000000..aa4f326bb536242e76e4cde5d47ff94020b13dbe --- /dev/null +++ b/cli/Commands/Checks/compatibility-rules/studip-4.4.php @@ -0,0 +1,6 @@ +<?php +// "Rules"/definitions for critical changes in 4.4 +return [ + 'Token::is_valid' => 'Use <fg=yellow>Token::isValid($token, $user_id)</> instead.', + 'Token::generate' => 'Use <fg=yellow>Token::create($duration = 30, $user_id = null)</> instead.', +]; diff --git a/cli/Commands/Checks/compatibility-rules/studip-5.0.php b/cli/Commands/Checks/compatibility-rules/studip-5.0.php new file mode 100644 index 0000000000000000000000000000000000000000..5aec2492037b03ec053fa9870f0aeace939a44e5 --- /dev/null +++ b/cli/Commands/Checks/compatibility-rules/studip-5.0.php @@ -0,0 +1,60 @@ +<?php +// "Rules"/definitions for critical changes in 5.0 +return [ + // https://develop.studip.de/trac/ticket/11250 + 'userMayAccessRange' => '<fg=yellow>Changed</> - Use <fg=yellow>isAccessibleToUser</> instead', + 'userMayEditRange' => '<fg=yellow>Changed</> - Use <fg=yellow>isEditableByUser</> instead', + 'userMayAdministerRange' => '<fg=red>Removed</>', + + // UTF8-Encode/Decode legacy functions + 'studip_utf8encode' => '<fg=red>Removed</> - Use utf8_encode().', + 'studip_utf8decode' => '<fg=red>Removed</> - Use utf8_decode().', + + // JSON encode/decode legacy functions + 'studip_json_decode' => '<fg=red>Deprecated</> - Use json_decode() and pay attention to the second parameter.', + 'studip_json_encode' => '<fg=red>Deprecated</> - Use json_encode().', + + // https://develop.studip.de/trac/ticket/10806 + 'SemesterData' => '<fg=red>Removed</> - Use <fg=yellow>Semester model</> instead', + + // https://develop.studip.de/trac/ticket/10786 + 'StatusgroupsModel' => '<fg=red>Removed</> - Use <fg=yellow>Statusgruppen model</> instead', + + // https://develop.studip.de/trac/ticket/10796 + 'StudipNullCache' => '<fg=red>Removed</> - Use <fg=yellow>StudipMemoryCache</> instead', + + // https://develop.studip.de/trac/ticket/10838 + 'getDeputies' => '<fg=red>Removed</> - Use <fg=yellow>Deputy::findDeputies()</> instead', + 'getDeputyBosses' => '<fg=red>Removed</> - Use <fg=yellow>Deputy::findDeputyBosses()</> instead', + '/(?<!Deputy::)addDeputy/' => '<fg=red>Removed</> - Use <fg=yellow>Deputy::addDeputy()</> instead', + '/deleteDeputy(?=\()/' => '<fg=red>Removed</> - Use <fg=yellow>Deputy model</> instead', + 'deleteAllDeputies' => '<fg=red>Removed</> - Use <fg=yellow>Deputy::deleteByRange_id</> instead', + '/(?<!Deputy::)isDeputy/' => '<fg=red>Removed</> - Use <fg=yellow>Deputy::isDeputy()</> instead', + 'setDeputyHomepageRights' => '<fg=red>Removed</> - Use <fg=yellow>Deputy model</> instead', + 'getValidDeputyPerms' => '<fg=red>Removed</> - Use <fg=yellow>Deputy::getValidPerms()</> instead', + 'isDefaultDeputyActivated' => '<fg=red>Removed</> - Use <fg=yellow>Deputy::isActivated()</> instead', + 'getMyDeputySeminarsQuery' => '<fg=red>Removed</> - Use <fg=yellow>Deputy::getMySeminarsQuery()</> instead', + 'isDeputyEditAboutActivated' => '<fg=red>Removed</> - Use <fg=yellow>Deputy::isEditActivated()</> instead', + + // https://develop.studip.de/trac/ticket/10870 + 'get_config' => '<fg=red>Deprecated</> - Use <fg=yellow>Config::get()</> instead.', + + // https://develop.studip.de/trac/ticket/10919 + 'RESTAPI\\RouteMap' => '<fg=red>Deprecated</> - Use the <fg=yellow>JSONAPI</> instead.', + + // https://develop.studip.de/trac/ticket/10878 + 'Leafo\\ScssPhp' => 'Library was replaced by <fg=yellow>scssphp/scssphp</>', + 'sfYamlParser' => 'Library was replaced by <fg=yellow>symfony/yaml</>', + 'DocBlock::of' => 'Library was replaced by <fg=yellow>gossi/docblock</>', + + 'vendor/idna_convert' => 'Remove include/require. Will be autoloaded.', + 'vendor/php-htmldiff' => 'Remove include/require. Will be autoloaded.', + 'vendor/HTMLPurifier' => 'Remove include/require. Will be autoloaded.', + 'vendor/phplot' => 'Remove include/require. Will be autoloaded.', + 'vendor/phpCAS' => 'Remove include/require. Will be autoloaded.', + 'vendor/phpxmlrpc' => 'Remove include/require. Will be autoloaded.', + + // https://develop.studip.de/trac/ticket/10964 + 'periodicalPushData' => '<fg=red>Removed</> - Use <fg=yellow>STUDIP.JSUpdater.register()</> instead', + '/UpdateInformtion::setInformation\(.+\..+\)/' => '<fg=red>Removed</> - Use <fg=yellow>STUDIP.JSUpdater.register()</> instead', +]; diff --git a/cli/Commands/CleanupAdmissionRules.php b/cli/Commands/CleanupAdmissionRules.php new file mode 100644 index 0000000000000000000000000000000000000000..3079ae4c3ecad5462bdbecbdd7f6a0ef89ca7dde --- /dev/null +++ b/cli/Commands/CleanupAdmissionRules.php @@ -0,0 +1,60 @@ +<?php + +namespace Studip\Cli\Commands; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class CleanupAdmissionRules extends Command +{ + protected static $defaultName = 'cleanup:admission-rules'; + + protected function configure(): void + { + $this->setDescription('Cleanup admission-rules.'); + $this->setHelp('Deletes entries in %admissions tables.'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + require_once 'lib/classes/admission/CourseSet.class.php'; + + $course_set = new \CourseSet(); + + $sql = "SELECT * FROM +( +SELECT rule_id,'ConditionalAdmission' as class FROM `conditionaladmissions` +UNION +SELECT rule_id,'CourseMemberAdmission' as class FROM `coursememberadmissions` +UNION +SELECT rule_id,'LimitedAdmission' as class FROM limitedadmissions +UNION +SELECT rule_id,'LockedAdmission' as class FROM lockedadmissions +UNION +SELECT rule_id,'ParticipantRestrictedAdmission' as class FROM participantrestrictedadmissions +UNION +SELECT rule_id,'PasswordAdmission' as class FROM passwordadmissions +UNION +SELECT rule_id,'TimedAdmission' as class FROM timedadmissions +) a +LEFT JOIN courseset_rule USING(rule_id) WHERE set_id IS NULL"; + + $c1 = $c2 = 0; + \DBManager::get()->fetchAll($sql, null, function ($data) use (&$c1, &$c2, $output) { + $c1++; + $class_name = '\\' . $data['class']; + if (class_exists($class_name)) { + $rule = new $class_name($data['rule_id']); + if ($rule->getId() === $data['rule_id']) { + $output->writeln(sprintf('deleting: %s with id: %s', $rule->getName(), $rule->getId())); + $c2++; + $rule->delete(); + } + } + }); + $output->writeln(sprintf('found: %s deleted: %s', $c1, $c2)); + + return Command::SUCCESS; + } +} diff --git a/cli/Commands/Cronjobs/CronjobExecute.php b/cli/Commands/Cronjobs/CronjobExecute.php new file mode 100644 index 0000000000000000000000000000000000000000..23592a1bac9af9decf84b55c97a0697984744182 --- /dev/null +++ b/cli/Commands/Cronjobs/CronjobExecute.php @@ -0,0 +1,42 @@ +<?php + +namespace Studip\Cli\Commands\Cronjobs; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class CronjobExecute extends Command +{ + protected static $defaultName = 'cronjobs:execute'; + + protected function configure(): void + { + $this->setDescription('Execute cronjob task.'); + $this->setHelp('This command will execute a cronjob task.'); + $this->addArgument('task_id', InputArgument::REQUIRED, 'Id of the desired cron job'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $task_id = $input->getArgument('task_id'); + $task = \CronjobTask::find($task_id); + if (!$task) { + $output->writeln('<error>Unknown task id</error>'); + return Command::FAILURE; + } + if (!file_exists($GLOBALS['STUDIP_BASE_PATH'] . '/' . $task->filename)) { + $output->writeln(sprintf('<error>Invalid task, unknown filename %s</error>', $task->filename)); + return Command::FAILURE; + } + require_once $GLOBALS['STUDIP_BASE_PATH'] . '/' . $task->filename; + if (!class_exists('\\' . $task->class)) { + fwrite(STDOUT, 'Invalid task, unknown class "' . $task->class . '"' . PHP_EOL); + $output->writeln(sprintf('<error>Invalid task, unknown class %s</error>', $task->class)); + return Command::FAILURE; + } + $task->engage(''); + return Command::SUCCESS; + } +} diff --git a/cli/Commands/Cronjobs/CronjobList.php b/cli/Commands/Cronjobs/CronjobList.php new file mode 100644 index 0000000000000000000000000000000000000000..af4913d582f1e714b1b10aa08148bde90085afa0 --- /dev/null +++ b/cli/Commands/Cronjobs/CronjobList.php @@ -0,0 +1,39 @@ +<?php + +namespace Studip\Cli\Commands\Cronjobs; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Helper\TableSeparator; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class CronjobList extends Command +{ + protected static $defaultName = 'cronjobs:list'; + + protected function configure(): void + { + $this->setDescription('List cronjobs.'); + $this->setHelp('This command lists all available cronjobs.'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $tasks = \CronjobTask::findBySql('1'); + + if ($tasks) { + $table = new Table($output); + $table->setStyle('compact'); + $table->setHeaders(['Task-ID', 'Description']); + foreach ($tasks as $task) { + $description = call_user_func(['\\' . $task->class, 'getDescription']); + if ($description) { + $table->addRow([$task->id, $description]); + } + } + $table->render(); + } + return Command::SUCCESS; + } +} diff --git a/cli/Commands/Cronjobs/CronjobWorker.php b/cli/Commands/Cronjobs/CronjobWorker.php new file mode 100644 index 0000000000000000000000000000000000000000..209f11286bc0834a8a23cb69b7895688fda5295d --- /dev/null +++ b/cli/Commands/Cronjobs/CronjobWorker.php @@ -0,0 +1,24 @@ +<?php + +namespace Studip\Cli\Commands\Cronjobs; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class CronjobWorker extends Command +{ + protected static $defaultName = 'cronjobs:worker'; + + protected function configure(): void + { + $this->setDescription('Cronjob worker.'); + $this->setHelp('Worker process for the cronjobs.'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + \CronjobScheduler::getInstance()->run(); + return Command::SUCCESS; + } +} diff --git a/cli/Commands/DB/Dump.php b/cli/Commands/DB/Dump.php new file mode 100644 index 0000000000000000000000000000000000000000..e2a8a2f8a27213b4be4ee19c304276195a55d0bf --- /dev/null +++ b/cli/Commands/DB/Dump.php @@ -0,0 +1,136 @@ +<?php + +namespace Studip\Cli\Commands\DB; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +class Dump extends Command +{ + protected static $defaultName = 'db:dump'; + + protected function configure(): void + { + $this->setDescription('Dump the given database schema'); + $this->addArgument('path', InputArgument::REQUIRED, 'path where the backup should be saved'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + $dump_dir = realpath($input->getArgument('path')); + $today = date('Ymd'); + $prefix = \Config::get()->STUDIP_INSTALLATION_ID ? \Config::get()->STUDIP_INSTALLATION_ID : 'studip'; + + if (!is_writeable($dump_dir)) { + $io->error('Directory: ' . $dump_dir . ' is not writeable!'); + return Command::FAILURE; + } + $dump_db_dir = $dump_dir . '/db-' . $today; + if (!\is_dir($dump_db_dir)) { + \mkdir($dump_db_dir); + } + $command = $this->getBaseDumpCommand(); + $output = []; + $result_code = 0; + + foreach (\DBManager::get()->query('SHOW TABLES') as $tables) { + $table = $tables[0]; + + $dump_table = $dump_db_dir . '/' . $table . '-' . $today . '.sql'; + + $io->writeln('<info>Dumping database table ' . $table . '</info>'); + + $cmd = $command . ' ' . $table . ' > ' . $dump_table; + $this->runCommand($cmd, $output, $result_code); + + if ($result_code > 0) { + $io->error($this->parseOutput($cmd, $output)); + return Command::FAILURE; + } + } + $dump_db = $dump_dir . '/' . $prefix . '-DB-' . $today . '.tar.gz'; + + $io->writeln('<info>Packing database to ' . $dump_db . '</info>'); + + $cmd = "cd $dump_db_dir && tar -czf $dump_db *"; + $this->runCommand($cmd, $output, $result_code); + + if ($result_code > 0) { + $io->error($this->parseOutput($cmd, $output)); + return Command::FAILURE; + } + + $cmd = "rm -rf $dump_db_dir"; + $this->runCommand($cmd, $output, $result_code); + + if ($result_code > 0) { + $io->error($this->parseOutput($cmd, $output)); + return Command::FAILURE; + } + + return Command::SUCCESS; + } + + /** + * Get the base dump command arguments for MySQL as a string. + * + * @return string + */ + private function getBaseDumpCommand(): string + { + $command = + 'mysqldump ' . + $this->getConnectionString() . + ' --skip-add-locks --skip-comments --skip-set-charset --tz-utc'; + + if (!\DBManager::get()->isMariaDB()) { + $command .= ' --column-statistics=0 --set-gtid-purged=OFF'; + } + return $command . ' ' . $GLOBALS['DB_STUDIP_DATABASE']; + } + + /** + * Generate a basic connection string (--socket, --host, --port, --user, --password) for the database. + * + * @return string + */ + private function getConnectionString(): string + { + return ' --user="' . + $GLOBALS['DB_STUDIP_USER'] . + '" --password="' . + $GLOBALS['DB_STUDIP_PASSWORD'] . + '" --host="' . + $GLOBALS['DB_STUDIP_HOST'] . + '"'; + } + + /** + * Execute dump command + * @param string $cmd + * @param array $output + * @param int $result_code + */ + private function runCommand(string $cmd, array &$output, int &$result_code) + { + exec($cmd . ' 2>&1', $output, $result_code); + } + + /** + * Parse output of exec() + * @param string $cmd + * @param array $output + * @return string + */ + private function parseOutput(string &$cmd, array &$output): string + { + $result = join('\n', array_merge([$cmd], $output)); + $cmd = ''; + $output = []; + return $result; + } +} diff --git a/cli/Commands/DB/MigrateEngine.php b/cli/Commands/DB/MigrateEngine.php new file mode 100644 index 0000000000000000000000000000000000000000..6ae5219e29a7c46687b99ee8545f29fc7dae3609 --- /dev/null +++ b/cli/Commands/DB/MigrateEngine.php @@ -0,0 +1,148 @@ +<?php + +namespace Studip\Cli\Commands\DB; + +use DBManager; +use StudipPDO; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +class MigrateEngine extends Command +{ + protected static $defaultName = 'db:migrate-engine'; + + protected function configure(): void + { + $this->setDescription('MyISAM to InnoDB'); + $this->setHelp('Migrate the Engine from MyISAM to InnoDB.'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + $io->info('Migration starting at ' . date('d.m.Y H:i:s')); + $start = microtime(true); + // Check if InnoDB is enabled in database server. + $engines = DBManager::get()->fetchAll('SHOW ENGINES'); + $innodb = false; + foreach ($engines as $e) { + // InnoDB is found and enabled. + if ($e['Engine'] == 'InnoDB' && in_array(mb_strtolower($e['Support']), ['default', 'yes'])) { + $innodb = true; + break; + } + } + if ($innodb) { + // Get version of database system (MySQL/MariaDB/Percona) + $data = DBManager::get()->fetchFirst('SELECT VERSION() AS version'); + $version = $data[0]; + + // Tables to ignore on engine conversion. + $ignore_tables = []; + // Fetch all tables that need to be converted. + $tables = DBManager::get()->fetchFirst( + "SELECT TABLE_NAME + FROM `information_schema`.TABLES + WHERE TABLE_SCHEMA=:database AND ENGINE=:oldengine + ORDER BY TABLE_NAME", + [ + ':database' => $GLOBALS['DB_STUDIP_DATABASE'], + ':oldengine' => 'MyISAM', + ] + ); + + /* + * lit_catalog needs fulltext indices which InnoDB doesn't support + * in older versions. + */ + if (version_compare($version, '5.6', '<')) { + $stmt_fulltext = DBManager::get()->prepare( + "SHOW INDEX FROM :database.:table WHERE Index_type = 'FULLTEXT'" + ); + foreach ($tables as $k => $t) { + $stmt_fulltext->bindParam(':table', $t, StudipPDO::PARAM_COLUMN); + $stmt_fulltext->bindParam(':database', $DB_STUDIP_DATABASE, StudipPDO::PARAM_COLUMN); + $stmt_fulltext->execute(); + if ($stmt_fulltext->fetch()) { + $ignore_tables[] = $t; + unset($tables[$k]); + } + } + if (count($ignore_tables)) { + $io->info( + 'The following tables needs fulltext indices ' . + 'which are not supported for InnoDB in your database ' . + 'version, so the tables will be left untouched: ' . + join(',', $ignore_tables) + ); + } + } + + // Use Barracuda format if database supports it (5.5 upwards). + if (version_compare($version, '5.5', '>=')) { + $io->info('Found MySQL in version >= 5.5, checking if Barracuda file format is supported...'); + // Get innodb_file_per_table setting + $data = DBManager::get()->fetchOne("SHOW VARIABLES LIKE 'innodb_file_per_table'"); + + $file_per_table = $data['Value']; + + // Check if Barracuda file format is enabled + $data = DBManager::get()->fetchOne("SHOW VARIABLES LIKE 'innodb_file_format'"); + $file_format = $data['Value']; + + if (mb_strtolower($file_per_table) == 'on' && mb_strtolower($file_format) == 'barracuda') { + $rowformat = 'DYNAMIC'; + } else { + if (mb_strtolower($file_per_table) != 'on') { + $io->info('file_per_table not set'); + } + if (mb_strtolower($file_format) != 'barracuda') { + $io->info('file_format not set to Barracuda (but to ' . $file_format . ')'); + } + $rowformat = 'COMPACT'; + } + } + + // Prepare query for table conversion. + $stmt = DBManager::get()->prepare('ALTER TABLE :database.:table ROW_FORMAT=:rowformat ENGINE=:newengine'); + $stmt->bindParam(':database', $DB_STUDIP_DATABASE, StudipPDO::PARAM_COLUMN); + $stmt->bindParam(':rowformat', $rowformat, StudipPDO::PARAM_COLUMN); + $newengine = 'InnoDB'; + $stmt->bindParam(':newengine', $newengine, StudipPDO::PARAM_COLUMN); + + // Now convert the found tables. + foreach ($tables as $t) { + $local_start = microtime(true); + $stmt->bindParam(':table', $t, StudipPDO::PARAM_COLUMN); + $stmt->execute(); + $local_end = microtime(true); + $local_duration = $local_end - $local_start; + $human_local_duration = sprintf( + '%02d:%02d:%02d', + ($local_duration / 60 / 60) % 24, + ($local_duration / 60) % 60, + $local_duration % 60 + ); + $io->info('Conversion of table ' . $t . ' took ' . $human_local_duration); + } + + $end = microtime(true); + + $duration = $end - $start; + $human_duration = sprintf( + '%02d:%02d:%02d', + ($duration / 60 / 60) % 24, + ($duration / 60) % 60, + $duration % 60 + ); + + $io->info('Migration finished at ' . date('d.m.Y H:i:s') . ', duration ' . $human_duration); + } else { + $io->error( + 'The storage engine InnoDB is not enabled in your database installation, tables cannot be converted.' + ); + } + } +} diff --git a/cli/Commands/DB/MigrateFileFormat.php b/cli/Commands/DB/MigrateFileFormat.php new file mode 100644 index 0000000000000000000000000000000000000000..0c97824797a6d22c4e0fbb8dcdbc1e451bca8413 --- /dev/null +++ b/cli/Commands/DB/MigrateFileFormat.php @@ -0,0 +1,128 @@ +<?php + +namespace Studip\Cli\Commands\DB; + +use DBManager; +use StudipPDO; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +class MigrateFileFormat extends Command +{ + protected static $defaultName = 'db:migrate-file-format'; + + protected function configure(): void + { + $this->setDescription('Antelope to Barracuda'); + $this->setHelp('Migrate the FileFormat from Antelope to Barracuda.'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + $io->info('Migration starting at ' . date('d.m.Y H:i:s')); + $start = microtime(true); + // Check if InnoDB is enabled in database server. + $engines = DBManager::get()->fetchAll('SHOW ENGINES'); + $innodb = false; + foreach ($engines as $e) { + // InnoDB is found and enabled. + if ($e['Engine'] == 'InnoDB' && in_array(mb_strtolower($e['Support']), ['default', 'yes'])) { + $innodb = true; + break; + } + } + if ($innodb) { + // Get version of database system (MySQL/MariaDB/Percona) + $data = DBManager::get()->fetchFirst('SELECT VERSION() AS version'); + $version = $data[0]; + + // Use Barracuda format if database supports it (5.5 upwards). + if (version_compare($version, '5.5', '>=')) { + $io->info('Checking if Barracuda file format is supported...'); + // Get innodb_file_per_table setting + $data = DBManager::get()->fetchOne("SHOW VARIABLES LIKE 'innodb_file_per_table'"); + $file_per_table = $data['Value']; + + // Check if Barracuda file format is enabled + $data = DBManager::get()->fetchOne("SHOW VARIABLES LIKE 'innodb_file_format'"); + $file_format = $data['Value']; + if (mb_strtolower($file_per_table) == 'on' && mb_strtolower($file_format) == 'barracuda') { + // Fetch all tables that need to be converted. + $tables = DBManager::get()->fetchFirst( + "SELECT TABLE_NAME + FROM `information_schema`.TABLES + WHERE TABLE_SCHEMA=:database AND ENGINE=:engine + AND ROW_FORMAT IN (:rowformats) + ORDER BY TABLE_NAME", + [ + ':database' => $GLOBALS['DB_STUDIP_DATABASE'], + ':engine' => 'InnoDB', + ':rowformats' => ['Compact', 'Redundant'], + ] + ); + + $newformat = 'DYNAMIC'; + + // Prepare query for table conversion. + $stmt = DBManager::get()->prepare('ALTER TABLE :database.:table ROW_FORMAT=:newformat'); + $stmt->bindParam(':database', $DB_STUDIP_DATABASE, StudipPDO::PARAM_COLUMN); + $stmt->bindParam(':newformat', $newformat, StudipPDO::PARAM_COLUMN); + + if (count($tables) > 0) { + // Now convert the found tables. + foreach ($tables as $t) { + $local_start = microtime(true); + $stmt->bindParam(':table', $t, StudipPDO::PARAM_COLUMN); + $stmt->execute(); + $local_end = microtime(true); + $local_duration = $local_end - $local_start; + $human_local_duration = sprintf( + '%02d:%02d:%02d', + ($local_duration / 60 / 60) % 24, + ($local_duration / 60) % 60, + $local_duration % 60 + ); + $io->info('Converserion of table' . $t . ' took ' . $human_local_duration); + } + } else { + $io->error('No Antelope format tables found'); + return Command::FAILURE; + } + } else { + if (mb_strtolower($file_per_table) != 'on') { + $io->error('file_per_table not set'); + return Command::FAILURE; + } + if (mb_strtolower($file_format) != 'barracuda') { + $io->error('file_format not set to Barracuda (but to ' . $file_format . ')'); + return Command::FAILURE; + } + } + $end = microtime(true); + $duration = $end - $start; + $human_duration = sprintf( + '%02d:%02d:%02d', + ($duration / 60 / 60) % 24, + ($duration / 60) % 60, + $duration % 60 + ); + + $io->info('Migration finished at ' . date('d.m.Y H:i:s') . ', duration ' . $human_duration); + return Command::SUCCESS; + } else { + $io->error( + 'Your database server does not yet support the Barracuda row format (you need at least 5.5)' + ); + return Command::FAILURE; + } + } else { + $io->error( + 'The storage engine InnoDB is not enabled in your database installation, tables cannot be converted.' + ); + return Command::INVALID; + } + } +} diff --git a/cli/Commands/Files/Dump.php b/cli/Commands/Files/Dump.php new file mode 100644 index 0000000000000000000000000000000000000000..6d5c48c472c444dfa7903b3d834c83902e6584e8 --- /dev/null +++ b/cli/Commands/Files/Dump.php @@ -0,0 +1,50 @@ +<?php + +namespace Studip\Cli\Commands\Files; + +use Config; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +class Dump extends Command +{ + protected static $defaultName = 'files:dump'; + + protected function configure(): void + { + $this->setDescription('Dumping data directory'); + $this->addArgument('path', InputArgument::REQUIRED, 'path where the backup should be saved'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + $dump_dir = realpath($input->getArgument('path')); + $today = date('Ymd'); + $prefix = Config::get()->STUDIP_INSTALLATION_ID ? Config::get()->STUDIP_INSTALLATION_ID : 'studip'; + + $data_path = realpath($GLOBALS['UPLOAD_PATH'] . '/../'); + + if (!$data_path) { + $io->error('Stud.IP upload folder not found!'); + return Command::FAILURE; + } + + $dumb_data = $dump_dir . '/' . $prefix . '-DATA-' . $today . '.tar.gz'; + + $io->info('Dumping data directory to ' . $dumb_data); + + $cmd = "cd $data_path && tar -czf $dumb_data ." . ' 2>&1'; + + exec($cmd, $output, $ok); + + if ($ok > 0) { + $io->error(join("\n", array_merge([$cmd], $output))); + return Command::FAILURE; + } + return Command::SUCCESS; + } +} diff --git a/cli/Commands/Fix/Biest7789.php b/cli/Commands/Fix/Biest7789.php new file mode 100644 index 0000000000000000000000000000000000000000..43f0c680e0eb294d50172979d841d6bdebed9c23 --- /dev/null +++ b/cli/Commands/Fix/Biest7789.php @@ -0,0 +1,114 @@ +<?php + +namespace Studip\Cli\Commands\Fix; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +/** + * For example + * + * php cli/studip fix:biest-7789 extern_config config + * php cli/studip fix:biest-7789 aux_lock_rules attributes + * php cli/studip fix:biest-7789 aux_lock_rules sorting + * php cli/studip fix:biest-7789 user_config value "field = 'MY_COURSES_ADMIN_VIEW_FILTER_ARGS'" + * php cli/studip fix:biest-7789 mail_queue_entries mail + */ +class Biest7789 extends Command +{ + protected static $defaultName = 'fix:biest-7789'; + + protected function configure(): void + { + $this->setDescription('Fix Biest #7789'); + $this->addArgument('table', InputArgument::REQUIRED, 'database table'); + $this->addArgument('column', InputArgument::REQUIRED, 'table column'); + $this->addArgument('where', InputArgument::OPTIONAL, 'where clause'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + ini_set('default_charset', 'utf-8'); + $io = new SymfonyStyle($input, $output); + $table = $input->getArgument('table'); + $column = $input->getArgument('column'); + $where = $input->getArgument('where'); + + $db = \DBManager::get(); + + $io->title($table); + // get primary keys + $result = $db->query("SHOW KEYS FROM $table WHERE Key_name = 'PRIMARY'"); + $keys = []; + + while ($data = $result->fetch(\PDO::FETCH_ASSOC)) { + $keys[] = $data['Column_name']; + } + + // retrieve and convert data + $result = $db->query( + 'SELECT `' . implode('`,`', $keys) . "`, `$column` FROM `$table` WHERE " . ($where ?: '1') + ); + + while ($data = $result->fetch(\PDO::FETCH_ASSOC)) { + $content = unserialize(\legacy_studip_utf8decode($data[$column])); + + if ($content === false) { + // try to fix string length denotations + $fixed = preg_replace_callback( + '/s:([0-9]+):\"(.*?)\";/s', + function ($matches) { + return 's:' . strlen($matches[2]) . ':"' . $matches[2] . '";'; + }, + $data[$column] + ); + + $content = unserialize(\legacy_studip_utf8decode($fixed)); + } + + if ($content !== false) { + // encode all data + $json = json_encode($this->legacy_studip_utf8encode($content), true); + + $query = "UPDATE `$table` SET `$column` = " . $db->quote($json) . "\n WHERE "; + + $where_query = []; + foreach ($keys as $key) { + $where_query[] = "`$key` = " . $db->quote($data[$key]); + } + + $q = $query . implode(' AND ', $where_query); + $db->exec($q); + $io->writeln("<info>$q</info>"); + } else { + $io->writeln(sprintf('<error>Could not convert: %s</error>', print_r($data, 1))); + } + } + return Command::SUCCESS; + } + + private function legacy_studip_utf8encode($data) + { + if (is_array($data)) { + $new_data = []; + foreach ($data as $key => $value) { + $key = $this->legacy_studip_utf8encode($key); + $new_data[$key] = $this->legacy_studip_utf8encode($value); + } + return $new_data; + } + + if (!\preg_match('/[\200-\377]/', $data) && !\preg_match("'&#[0-9]+;'", $data)) { + return $data; + } else { + return \mb_decode_numericentity( + \mb_convert_encoding($data, 'UTF-8', 'WINDOWS-1252'), + [0x100, 0xffff, 0, 0xffff], + 'UTF-8' + ); + } + } +} diff --git a/cli/Commands/Fix/Biest7866.php b/cli/Commands/Fix/Biest7866.php new file mode 100644 index 0000000000000000000000000000000000000000..591e79f469d7b56d41ddc6b318b98aa48ee1272d --- /dev/null +++ b/cli/Commands/Fix/Biest7866.php @@ -0,0 +1,56 @@ +<?php + +namespace Studip\Cli\Commands\Fix; + +use DBManager; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +class Biest7866 extends Command +{ + protected static $defaultName = 'fix:biest-7866'; + + protected function configure(): void + { + $this->setDescription('Fix Biest #7866'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + $root_folders = DBManager::get()->fetchAll("SELECT `id`, `range_id` FROM `folders` WHERE `parent_id` = ''"); + + foreach ($root_folders as $r) { + $this->setFolderRangeId($io, $r['id'], $r['range_id']); + } + return Command::SUCCESS; + } + + /** + * Sets the range_id of all child folders to the given range_id. + * @param SymfonyStyle $io + * @param string $parent_folder + * @param string $range_id + */ + private function setFolderRangeId(SymfonyStyle $io, string $parent_folder, string $range_id) + { + // Update all child folder range_ids. + DBManager::get()->execute('UPDATE `folders` SET `range_id` = :range WHERE `parent_id` = :parent', [ + 'range' => $range_id, + 'parent' => $parent_folder, + ]); + + // Recursion: set correct range_id for child folders with wrong range_id. + $children = DBManager::get()->fetchAll('SELECT `id`, `range_id` FROM `folders` WHERE `parent_id` = :parent', [ + 'parent' => $parent_folder, + ]); + foreach ($children as $child) { + if ($child['range_id'] != $range_id) { + $io->info(sprintf("Folder %s -> range_id %s.\n", $child['id'], $range_id)); + } + $this->setFolderRangeId($io, $child['id'], $range_id); + } + } +} diff --git a/cli/Commands/Fix/Biest8136.php b/cli/Commands/Fix/Biest8136.php new file mode 100644 index 0000000000000000000000000000000000000000..17fe2490f159e31f853073351c8ba99280bb1f22 --- /dev/null +++ b/cli/Commands/Fix/Biest8136.php @@ -0,0 +1,41 @@ +<?php + +namespace Studip\Cli\Commands\Fix; + +use DBManager; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +class Biest8136 extends Command +{ + protected static $defaultName = 'fix:biest-8136'; + + protected function configure(): void + { + $this->setDescription('Fix Biest #8136'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + + $query = "UPDATE `activities` + SET `actor_type` = 'anonymous', + `actor_id` = '' + WHERE `provider` = :provider + AND `actor_type` != 'anonymous' + AND `object_id` IN ( + SELECT `topic_id` + FROM `forum_entries` + WHERE `anonymous` != 0 + )"; + $statement = DBManager::get()->prepare($query); + $statement->bindValue(':provider', 'Studip\\Activity\\ForumProvider'); + $statement->execute(); + + $io->info(sprintf('%u forum post activities were anonymized', $statement->rowCount())); + return Command::SUCCESS; + } +} diff --git a/cli/Commands/Fix/EndTimeWeeklyRecurredEvents.php b/cli/Commands/Fix/EndTimeWeeklyRecurredEvents.php new file mode 100644 index 0000000000000000000000000000000000000000..40e7a2b0e0290d92c27c162c70bd83ebd0b0b489 --- /dev/null +++ b/cli/Commands/Fix/EndTimeWeeklyRecurredEvents.php @@ -0,0 +1,40 @@ +<?php + +namespace Studip\Cli\Commands\Fix; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +class EndTimeWeeklyRecurredEvents extends Command +{ + protected static $defaultName = 'fix:end-time-weekly-recurred-events'; + + protected function configure(): void + { + $this->setDescription('Fix end time weekly recurred events'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + $events = \EventData::findBySQL("rtype = 'WEEKLY' AND IFNULL(count, 0) > 0"); + $cal_event = new \CalendarEvent(); + + $i = 0; + + foreach ($events as $event) { + $id = $event->getId(); + $cal_event->event = $event; + $rrule = $cal_event->getRecurrence(); + $cal_event->setRecurrence($rrule); + $event->expire = $cal_event->event->expire; + $event->setId($id); + $event->store(); + $i++; + } + $io->info('Wrong end time of recurrence fixed for ' . $i . ' events.'); + return Command::SUCCESS; + } +} diff --git a/cli/Commands/Fix/IconDimensions.php b/cli/Commands/Fix/IconDimensions.php new file mode 100644 index 0000000000000000000000000000000000000000..13703e9ac91af94a9238e457770f4a54b0908fcd --- /dev/null +++ b/cli/Commands/Fix/IconDimensions.php @@ -0,0 +1,49 @@ +<?php +namespace Studip\Cli\Commands\Fix; + +use FilesystemIterator; +use RecursiveDirectoryIterator; +use RecursiveIteratorIterator; +use RecursiveRegexIterator; +use RegexIterator; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class IconDimensions extends Command +{ + protected static $defaultName = 'fix:icon-dimensions'; + + protected function configure(): void + { + $this->setDescription('Fix icon dimensions in their svg files'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $folder = $GLOBALS['STUDIP_BASE_PATH'] . '/public/assets/images/icons'; + $iterator = new RecursiveDirectoryIterator( + $folder, + FilesystemIterator::FOLLOW_SYMLINKS | FilesystemIterator::UNIX_PATHS + ); + $iterator = new RecursiveIteratorIterator($iterator); + $regexp_iterator = new RegexIterator($iterator, '/\.svg$/', RecursiveRegexIterator::MATCH); + + foreach ($regexp_iterator as $file) { + $contents = file_get_contents($file); + + $xml = simplexml_load_string($contents); + $attr = $xml->attributes(); + if ($attr->width && $attr->height) { + continue; + } + + $contents = str_replace('<svg ', '<svg width="16" height="16" ', $contents); + file_put_contents($file, $contents); + + $output->writeln("Adjusted {$file}"); + } + + return Command::SUCCESS; + } +} diff --git a/cli/Commands/HelpContent/Migrate.php b/cli/Commands/HelpContent/Migrate.php new file mode 100644 index 0000000000000000000000000000000000000000..8ab790a00f1c55aa30be1953dbd947682d6093da --- /dev/null +++ b/cli/Commands/HelpContent/Migrate.php @@ -0,0 +1,114 @@ +<?php + +namespace Studip\Cli\Commands\HelpContent; + +use DBManager; +use PDO; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +class Migrate extends Command +{ + protected static $defaultName = 'help-content:migrate'; + + protected function configure(): void + { + $this->setDescription('Migrate help-content.'); + $this->addArgument('version', InputArgument::REQUIRED, 'Version of the help content'); + $this->addArgument('language', InputArgument::REQUIRED, 'Language of the help content'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + $version = $input->getArgument('version'); + $language = $input->getArgument('language'); + $help_content_path = $GLOBALS['STUDIP_BASE_PATH'] . '/doc/helpbar'; + + $query = 'SELECT * FROM help_content WHERE studip_version = ? LIMIT 1'; + $statement = DBManager::get()->prepare($query); + $statement->execute([$version]); + $ret = $statement->fetchGrouped(PDO::FETCH_ASSOC); + + if ($ret && count($ret)) { + $io->info('Helpbar content already present for this version!'); + return Command::SUCCESS; + } + + $filename = $help_content_path . '/' . $language . '/helpcontent.json'; + + if (!is_file($filename)) { + $io->error('File not found: ' . $filename); + return Command::FAILURE; + } + + $json = json_decode(file_get_contents($filename), true); + + if ($json === null) { + $io->error('Helpbar content could not be loaded. File: ' . $filename); + return Command::FAILURE; + } + + $count = []; + foreach ($json as $row) { + if (!is_array($row['text'])) { + $row['text'] = [$row['text']]; + } + if (!$row['label'] || !$row['icon']) { + $row['label'] = ''; + } + $installation_id = \Config::get()->STUDIP_INSTALLATION_ID; + foreach ($row['text'] as $index => $text) { + $count[$language . $row['route']]++; + $statement = DBManager::get()->prepare( + "INSERT INTO help_content ( + `content_id`, + `language`, + `label`, + `icon`, + `content`, + `route`, + `studip_version`, + `position`, + `custom`, + `visible`, + `author_id`, + `installation_id`, + `mkdate` + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, 1, '', ?, UNIX_TIMESTAMP())" + ); + $statement->execute([ + md5(uniqid(rand(), true)), + $language, + $index == 0 ? $row['label'] : '', + $index == 0 ? $row['icon'] : '', + $text, + $row['route'], + $version, + $count[$language . $row['route']], + $installation_id, + ]); + } + } + + if (count($count)) { + if (!\Config::get()->getValue('HELP_CONTENT_CURRENT_VERSION')) { + \Config::get()->create('HELP_CONTENT_CURRENT_VERSION', [ + 'value' => $version, + 'is_default' => 0, + 'type' => 'string', + 'range' => 'global', + 'section' => 'global', + 'description' => _('Aktuelle Version der Helpbar-Einträge in Stud.IP'), + ]); + } else { + \Config::get()->store('HELP_CONTENT_CURRENT_VERSION', $version); + } + } + $io->success('help content added for ' . count($count) . ' routes.'); + return Command::SUCCESS; + } +} diff --git a/cli/Commands/Migrate/Migrate.php b/cli/Commands/Migrate/Migrate.php new file mode 100644 index 0000000000000000000000000000000000000000..f5acff89dac9e0bc8ae07e7ba87de4feea407e5a --- /dev/null +++ b/cli/Commands/Migrate/Migrate.php @@ -0,0 +1,44 @@ +<?php + +namespace Studip\Cli\Commands\Migrate; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class Migrate extends Command +{ + protected static $defaultName = 'migrate'; + + protected function configure(): void + { + $this->setDescription('Run the database migrations.'); + $this->setHelp('This command runs all pending database migrations.'); + + $this->addOption('branch', 'b', InputOption::VALUE_OPTIONAL, 'the branch of the migrations', '0'); + + $this->addOption('domain', 'd', InputOption::VALUE_OPTIONAL, 'the domain of the migrations', 'studip'); + + $defaultPath = $GLOBALS['STUDIP_BASE_PATH'] . '/db/migrations'; + $this->addOption('path', 'p', InputOption::VALUE_OPTIONAL, 'the id of the migration to list to', $defaultPath); + + $this->addOption('target', 't', InputOption::VALUE_OPTIONAL, 'the target version', null); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $branch = $input->getOption('branch'); + $domain = $input->getOption('domain'); + $path = $input->getOption('path'); + $target = $input->getOption('target'); + $verbose = $input->getOption('verbose'); + + $version = new \DBSchemaVersion($domain, $branch); + $migrator = new \Migrator($path, $version, $verbose); + $migrator->migrateTo($target); + + return Command::SUCCESS; + } +} diff --git a/cli/Commands/Migrate/MigrateList.php b/cli/Commands/Migrate/MigrateList.php new file mode 100644 index 0000000000000000000000000000000000000000..cff6ce190d4886a86307b8d60ab7a67a8e3d7b50 --- /dev/null +++ b/cli/Commands/Migrate/MigrateList.php @@ -0,0 +1,59 @@ +<?php + +namespace Studip\Cli\Commands\Migrate; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class MigrateList extends Command +{ + protected static $defaultName = 'migrate:list'; + + protected function configure(): void + { + $this->setDescription('Shows all pending migrations.'); + $this->setHelp('This command shows a list of all pending migrations.'); + + $this->addOption('branch', 'b', InputOption::VALUE_OPTIONAL, 'the branch of the migrations', '0'); + + $this->addOption('domain', 'd', InputOption::VALUE_OPTIONAL, 'the domain of the migrations', 'studip'); + + $defaultPath = $GLOBALS['STUDIP_BASE_PATH'] . '/db/migrations'; + $this->addOption('path', 'p', InputOption::VALUE_OPTIONAL, 'the path to the migrations', $defaultPath); + + $this->addOption('target', 't', InputOption::VALUE_OPTIONAL, 'the target version', null); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $branch = $input->getOption('branch'); + $domain = $input->getOption('domain'); + $path = $input->getOption('path'); + $target = $input->getOption('target'); + $verbose = $input->getOption('verbose'); + + $version = new \DBSchemaVersion($domain, $branch); + $migrator = new \Migrator($path, $version, $verbose); + + $migrationClasses = $migrator->migrationClasses(); + $migrations = $migrator->relevantMigrations($target); + if (count($migrations)) { + $data = []; + foreach ($migrations as $number => $migration) { + $description = $migration->description() ?: '(no description)'; + $name = basename($migrationClasses[$number][0], '.php'); + $data[] = [$number, $name, $description]; + } + + $table = new Table($output); + $table->setHeaders(['ID', 'Migration', 'Description'])->setRows($data); + $table->setStyle('box'); + $table->render(); + } + + return Command::SUCCESS; + } +} diff --git a/cli/Commands/Migrate/MigrateStatus.php b/cli/Commands/Migrate/MigrateStatus.php new file mode 100644 index 0000000000000000000000000000000000000000..e07bbe18bf21d26413efa91a2b313d217d18a690 --- /dev/null +++ b/cli/Commands/Migrate/MigrateStatus.php @@ -0,0 +1,53 @@ +<?php + +namespace Studip\Cli\Commands\Migrate; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class MigrateStatus extends Command +{ + protected static $defaultName = 'migrate:status'; + + protected function configure(): void + { + $this->setDescription('Shows the state of all migrations.'); + $this->setHelp('This command shows the state of all migrations.'); + + $this->addOption('domain', 'd', InputOption::VALUE_OPTIONAL, 'the domain of the migrations', 'studip'); + + $defaultPath = $GLOBALS['STUDIP_BASE_PATH'] . '/db/migrations'; + $this->addOption('path', 'p', InputOption::VALUE_OPTIONAL, 'the id of the migration to list to', $defaultPath); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $domain = $input->getOption('domain'); + $path = $input->getOption('path'); + $verbose = $input->getOption('verbose'); + + $version = new \DBSchemaVersion($domain); + $migrator = new \Migrator($path, $version, $verbose); + + $migrations = $migrator->migrationClasses(); + uksort($migrations, 'version_compare'); + $migrations = array_reverse($migrations, true); + + $pending = $migrator->relevantMigrations(null); + + $rows = []; + foreach ($migrations as $number => $migration) { + $rows[] = [isset($pending[$number]) ? 'No' : 'Yes', $number, basename($migration[0], '.php')]; + } + + $table = new Table($output); + $table->setHeaders(['Ran?', 'ID', 'Migration'])->setRows($rows); + $table->setStyle('box'); + $table->render(); + + return Command::SUCCESS; + } +} diff --git a/cli/Commands/Plugins/I18N/I18NCommand.php b/cli/Commands/Plugins/I18N/I18NCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..6fa9f77c9cab3807509e3e14b6085ac48bcf9bad --- /dev/null +++ b/cli/Commands/Plugins/I18N/I18NCommand.php @@ -0,0 +1,77 @@ +<?php +namespace Studip\Cli\Commands\Plugins\I18N; + +use Exception; +use Studip\Cli\Commands\AbstractPluginCommand; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; + +abstract class I18NCommand extends AbstractPluginCommand +{ + private $plugin_manager = null; + + protected function configure(): void + { + $this->addOption('pluginname', 'p', InputArgument::OPTIONAL, 'name of the plugin'); + $this->addOption('folder', 'f', InputArgument::OPTIONAL, 'folder to scan (overrides pluginname)'); + } + + protected function getPluginFolder(InputInterface $input): string + { + $pluginname = $input->getOption('pluginname'); + $folder = $input->getOption('folder'); + + if (!$pluginname && !$folder) { + throw new Exception('You must specify either pluginname or folder.'); + } + + if (!$folder && $pluginname) { + $plugin = $this->findPluginByName($this->getPluginManager(), $pluginname); + if ($plugin === null) { + throw new Exception('Could not find plugin of that name.'); + } + + $folder = "{$GLOBALS['PLUGINS_PATH']}/{$plugin['path']}"; + } + + if (!$folder || !file_exists($folder) || !is_readable($folder)) { + throw new Exception('Could not access folder.'); + } + + return $folder; + } + + protected function getPluginManifest(string $folder): array + { + return $this->getPluginManager()->getPluginManifest($folder); + } + + protected function getPluginLocaleDomainByFolder(string $folder): string + { + $manifest = $this->getPluginManifest($folder); + if (!$manifest) { + throw new Exception("Could not detect plugin manifest in folder {$folder}"); + } + + if (!isset($manifest['localedomain'])) { + throw new Exception('Manifest has no defined localedomain'); + } + + return $manifest['localedomain']; + } + + protected function getPluginManager(): \PluginManager + { + if ($this->plugin_manager === null) { + $this->plugin_manager = \PluginManager::getInstance(); + } + return $this->plugin_manager; + } + + protected function getSystemLanguages(): array + { + return array_map(function ($lang) { + return explode('_', $lang)[0]; + }, array_keys($GLOBALS['INSTALLED_LANGUAGES'])); + } +} diff --git a/cli/Commands/Plugins/I18N/I18NCompile.php b/cli/Commands/Plugins/I18N/I18NCompile.php new file mode 100644 index 0000000000000000000000000000000000000000..178152e7d9c310395b0bdb1dd3203248aa6cf759 --- /dev/null +++ b/cli/Commands/Plugins/I18N/I18NCompile.php @@ -0,0 +1,57 @@ +<?php +namespace Studip\Cli\Commands\Plugins\I18N; + +use Exception; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Process\Exception\ProcessFailedException; +use Symfony\Component\Process\Process; + +final class I18NCompile extends I18NCommand +{ + protected static $defaultName = 'plugin:i18n:compile'; + + protected function configure(): void + { + parent::configure(); + + $this->setDescription('Compile translations'); + $this->setHelp('This command compiles all .po files in the locale folder of the plugin.'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + try { + $folder = $this->getPluginFolder($input); + + foreach (glob("{$folder}/locale/*/LC_MESSAGES/*.po") as $po) { + $command_line = 'msgfmt "${:PO}" -o "${:MO}"'; + $process = Process::fromShellCommandline($command_line); + + try { + $process->mustRun(null, [ + 'PO' => $po, + 'MO' => preg_replace('/\.po$/', '.mo', $po), + ]); + + $out = $process->getOutput(); + if ($out) { + $output->writeln($out, OutputInterface::VERBOSITY_VERBOSE); + } + + $output->writeln("Translations have been compiled successfully."); + } catch (ProcessFailedException $e) { + $output->writeln("<error>Could not execute shell command</error>"); + $output->writeln($e->getmessage()); + return Command::FAILURE; + } + } + + return Command::SUCCESS; + } catch (Exception $e) { + $output->writeln("<error>{$e->getMessage()}</error>"); + return Command::FAILURE; + } + } +} diff --git a/cli/Commands/Plugins/I18N/I18NDetect.php b/cli/Commands/Plugins/I18N/I18NDetect.php new file mode 100644 index 0000000000000000000000000000000000000000..584fe00500bdc664f1341d7c4d0edf56f5584fba --- /dev/null +++ b/cli/Commands/Plugins/I18N/I18NDetect.php @@ -0,0 +1,59 @@ +<?php +namespace Studip\Cli\Commands\Plugins\I18N; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +final class I18NDetect extends I18NCommand +{ + protected static $defaultName = 'plugin:i18n:detect'; + + protected function configure(): void + { + parent::configure(); + + $this->setDescription('Detect unmarked strings.'); + $this->setHelp('This command detects probably unmarked strings for localization in php files.'); + $this->addOption('only-filenames', '1', InputOption::VALUE_NONE, 'display only the filenames'); + $this->addOption('absolute-filenames', 'a', InputOption::VALUE_NONE, 'display absolute filenames'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + try { + $folder = $this->getPluginFolder($input); + } catch (\Exception $e) { + $output->writeln("<error>{$e->getMessage()}</error>"); + return Command::FAILURE; + } + + $iterator = $this->getFolderIterator($folder, true, ['php']); + + $found = false; + foreach ($iterator as $file) { + $filename = $file->getPathName(); + + $matched = preg_match('/(?<![$>])_\(/', file_get_contents($filename)); + if ($matched) { + $output_filename = $input->getOption('absolute-filenames') + ? $filename + : $this->relativeFilePath($filename, true); + + $message = $input->getOption('only-filenames') + ? $output_filename + : "<info>{$output_filename}</info>: {$matched} occurence(s)"; + + $output->writeln($message); + $found = true; + } + } + + if (!$found) { + $output->writeln('<info>No unmarked translation strings found</info>'); + } + + return Command::SUCCESS; + } +} diff --git a/cli/Commands/Plugins/I18N/I18NExtract.php b/cli/Commands/Plugins/I18N/I18NExtract.php new file mode 100644 index 0000000000000000000000000000000000000000..e1f479fbe8e4a9ba95b207a91a1f4214852a579c --- /dev/null +++ b/cli/Commands/Plugins/I18N/I18NExtract.php @@ -0,0 +1,74 @@ +<?php +namespace Studip\Cli\Commands\Plugins\I18N; + +use Exception; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Process\Exception\ProcessFailedException; +use Symfony\Component\Process\Process; + +final class I18NExtract extends I18NCommand +{ + protected static $defaultName = 'plugin:i18n:extract'; + + protected function configure(): void + { + parent::configure(); + + $this->setDescription('Extract localizable strings.'); + $this->setHelp('This command extracts the localizable string from php files into a .pot file.'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + try { + $folder = $this->getPluginFolder($input); + $manifest = $this->getPluginManifest($folder); + $localedomain = $this->getPluginLocaleDomainByFolder($folder); + + foreach ($this->getSystemLanguages() as $lang) { + $lang = explode('_', $lang)[0]; + $language_dir = "{$folder}/locale/{$lang}/LC_MESSAGES"; + if (!file_exists($language_dir)) { + mkdir($language_dir, 0755, true); + } + } + + $main_lang = $this->getSystemLanguages()[0]; + $pot_file = "{$folder}/locale/{$main_lang}/LC_MESSAGES/{$localedomain}.pot"; + file_put_contents($pot_file, ''); + + $command_line = implode(' | ', [ + 'find "${:FOLDER}" -iname "*.php"', + 'xargs xgettext --keyword=_n:1,2 --from-code=UTF-8 -j -n --language=PHP --add-location=never --package-name="${:PACKAGENAME}" -o "${:POTFILE}"', + ]); + + $process = Process::fromShellCommandline($command_line); + + try { + $process->mustRun(null, [ + 'FOLDER' => $folder, + 'PACKAGENAME' => $manifest['pluginclassname'], + 'POTFILE' => $pot_file, + ]); + + $out = $process->getOutput(); + if ($out) { + $output->writeln($out, OutputInterface::VERBOSITY_VERBOSE); + } + + $output->writeln("Translation strings have been extracted successfully."); + return Command::SUCCESS; + } catch (ProcessFailedException $e) { + $output->writeln("<error>Could not execute shell command</error>"); + $output->writeln($e->getmessage()); + return Command::FAILURE; + } + + } catch (Exception $e) { + $output->writeln("<error>{$e->getMessage()}</error>"); + return Command::FAILURE; + } + } +} diff --git a/cli/Commands/Plugins/PluginActivate.php b/cli/Commands/Plugins/PluginActivate.php new file mode 100644 index 0000000000000000000000000000000000000000..df5763593fab6dca53ff6c029523c5b9e456b738 --- /dev/null +++ b/cli/Commands/Plugins/PluginActivate.php @@ -0,0 +1,39 @@ +<?php + +namespace Studip\Cli\Commands\Plugins; + +use Studip\Cli\Commands\AbstractPluginCommand; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class PluginActivate extends AbstractPluginCommand +{ + protected static $defaultName = 'plugin:activate'; + + protected function configure(): void + { + $this->setDescription('Activate a plugin.'); + $this->setHelp('This command activates a plugin.'); + $this->addArgument('pluginname', InputArgument::REQUIRED, 'name of the plugin'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $pluginname = $input->getArgument('pluginname'); + + $pluginManager = \PluginManager::getInstance(); + $plugin = $this->findPluginByName($pluginManager, $pluginname); + if (null === $plugin) { + $output->writeln('<error>Could not find plugin of that name.</error>'); + + return Command::FAILURE; + } + + $pluginManager->setPluginEnabled($plugin['id'], true); + $output->writeln('Plugin activated.'); + + return Command::SUCCESS; + } +} diff --git a/cli/Commands/Plugins/PluginDeactivate.php b/cli/Commands/Plugins/PluginDeactivate.php new file mode 100644 index 0000000000000000000000000000000000000000..637ad7f1e78147bc21a745a43d1e2199f49f725c --- /dev/null +++ b/cli/Commands/Plugins/PluginDeactivate.php @@ -0,0 +1,39 @@ +<?php + +namespace Studip\Cli\Commands\Plugins; + +use Studip\Cli\Commands\AbstractPluginCommand; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class PluginDeactivate extends AbstractPluginCommand +{ + protected static $defaultName = 'plugin:deactivate'; + + protected function configure(): void + { + $this->setDescription('Deactivate a plugin.'); + $this->setHelp('This command deactivates a plugin.'); + $this->addArgument('pluginname', InputArgument::REQUIRED, 'name of the plugin'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $pluginname = $input->getArgument('pluginname'); + + $pluginManager = \PluginManager::getInstance(); + $plugin = $this->findPluginByName($pluginManager, $pluginname); + if (null === $plugin) { + $output->writeln('<error>Could not find plugin of that name.</error>'); + + return Command::FAILURE; + } + + $pluginManager->setPluginEnabled($plugin['id'], false); + $output->writeln('Plugin deactivated.'); + + return Command::SUCCESS; + } +} diff --git a/cli/Commands/Plugins/PluginInfo.php b/cli/Commands/Plugins/PluginInfo.php new file mode 100644 index 0000000000000000000000000000000000000000..492c1aea14ea9a8688ab28207e7a3106baa3842d --- /dev/null +++ b/cli/Commands/Plugins/PluginInfo.php @@ -0,0 +1,80 @@ +<?php + +namespace Studip\Cli\Commands\Plugins; + +use Studip\Cli\Commands\AbstractPluginCommand; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class PluginInfo extends AbstractPluginCommand +{ + protected static $defaultName = 'plugin:info'; + + protected function configure(): void + { + $this->setDescription('Shows information about matching plugins.'); + $this->setHelp('This command shows information about plugins whose name contains the optional pattern.'); + $this->addArgument('pattern', InputArgument::OPTIONAL, 'pattern to search for'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $pattern = $input->getArgument('pattern'); + + $pluginManager = \PluginManager::getInstance(); + $plugins = $pluginManager->getPluginInfos(); + + if ($pattern) { + $plugins = array_filter($plugins, function ($plugin) use ($pattern) { + return false !== mb_stripos($plugin['name'], $pattern); + }); + } + + $basepath = \Config::get()->PLUGINS_PATH; + foreach ($plugins as $plugin) { + $plugindir = $basepath . '/' . $plugin['path'] . '/'; + + $plugin['class_exists'] = $this->pluginClassExists($plugindir, $plugin); + $plugin['type'] = join(',', $plugin['type']); + + if (is_dir($plugindir . '/migrations')) { + $schemaVersion = new \DBSchemaVersion($plugin['name']); + $migrator = new \Migrator($plugindir . '/migrations', $schemaVersion); + $plugin['migration_top_version'] = $migrator->topVersion(); + $plugin['schema_version'] = $schemaVersion->get(); + } + + $pairs = []; + foreach ($plugin as $key => $value) { + $pairs[] = [$key, $value]; + } + + $table = new Table($output); + $table->setHeaders(['Field', 'Value'])->setRows($pairs); + $table->setStyle('box'); + $table->render(); + + $output->writeln(''); + } + + return Command::SUCCESS; + } + + private function pluginClassExists(string $plugindir, array $plugin) + { + $pluginfile = $plugindir . $plugin['class'] . '.class.php'; + if (file_exists($pluginfile)) { + return 1; + } else { + $pluginfile = $plugindir . $plugin['class'] . '.php'; + if (file_exists($pluginfile)) { + return 1; + } + } + + return 0; + } +} diff --git a/cli/Commands/Plugins/PluginInstall.php b/cli/Commands/Plugins/PluginInstall.php new file mode 100644 index 0000000000000000000000000000000000000000..1277f4f15d0fd73aefbe037f67fcb411f7f0254c --- /dev/null +++ b/cli/Commands/Plugins/PluginInstall.php @@ -0,0 +1,41 @@ +<?php + +namespace Studip\Cli\Commands\Plugins; + +use Studip\Cli\Commands\AbstractPluginCommand; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class PluginInstall extends AbstractPluginCommand +{ + protected static $defaultName = 'plugin:install'; + + protected function configure(): void + { + $this->setDescription('Install a plugin.'); + $this->setHelp('This command installs a plugin from a ZIP file.'); + $this->addArgument('zipfile', InputArgument::REQUIRED, 'path to the ZIP file'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $zipfile = $input->getArgument('zipfile'); + + try { + $plugin_admin = new \PluginAdministration(); + if (parse_url($zipfile, \PHP_URL_SCHEME)) { + $plugin_admin->installPluginFromURL($zipfile); + } else { + $plugin_admin->installPlugin($zipfile); + } + $output->writeln('The plugin was installed successfully.'); + } catch (\PluginInstallationException $ex) { + $output->writeln('<error>' . $ex->getMessage() . '</error>'); + return Command::FAILURE; + } + + return Command::SUCCESS; + } +} diff --git a/cli/Commands/Plugins/PluginListMigrations.php b/cli/Commands/Plugins/PluginListMigrations.php new file mode 100644 index 0000000000000000000000000000000000000000..2af092f88a71b6b2c470ab434cd3532459df6996 --- /dev/null +++ b/cli/Commands/Plugins/PluginListMigrations.php @@ -0,0 +1,68 @@ +<?php + +namespace Studip\Cli\Commands\Plugins; + +use Studip\Cli\Commands\AbstractPluginCommand; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class PluginListMigrations extends AbstractPluginCommand +{ + protected static $defaultName = 'plugin:list-migrations'; + + protected function configure(): void + { + $this->setDescription('List all migrations of a plugin.'); + $this->setHelp('This command lists all migrations of a plugin.'); + $this->addArgument('pluginname', InputArgument::REQUIRED, 'name of the plugin'); + $this->addOption('branch', 'b', InputOption::VALUE_OPTIONAL, 'branch of the migrations', '0'); + $this->addOption('target', 't', InputOption::VALUE_OPTIONAL, 'target of the migrator', null); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $pluginname = $input->getArgument('pluginname'); + $branch = $input->getOption('branch'); + $target = $input->getOption('target'); + $verbose = $input->getOption('verbose'); + + $pluginManager = \PluginManager::getInstance(); + $plugin = $this->findPluginByName($pluginManager, $pluginname); + if (null === $plugin) { + $output->writeln('<error>Could not find plugin of that name.</error>'); + + return Command::FAILURE; + } + + $pluginpath = \Config::get()->PLUGINS_PATH . '/' . $plugin['path']; + + if (!is_dir($pluginpath . '/migrations')) { + $output->writeln('<comment>Could not find any migrations of that plugin.</comment>'); + + return Command::SUCCESS; + } + + // if there are migrations, migrate + $schemaVersion = new \DBSchemaVersion($plugin['name'], $branch); + $migrator = new \Migrator($pluginpath . '/migrations', $schemaVersion, $verbose); + + $migrations = $migrator->relevantMigrations($target); + + $rows = []; + foreach ($migrations as $number => $migration) { + $description = $migration->description() ?: '(no description)'; + $rows[] = [$number, get_class($migration), $description]; + } + + $table = new Table($output); + $table->setHeaders(['ID', 'Class', 'Description'])->setRows($rows); + $table->setStyle('box'); + $table->render(); + + return Command::SUCCESS; + } +} diff --git a/cli/Commands/Plugins/PluginMigrate.php b/cli/Commands/Plugins/PluginMigrate.php new file mode 100644 index 0000000000000000000000000000000000000000..8bfd03663cb9f1007a896566753cca64ae79e23e --- /dev/null +++ b/cli/Commands/Plugins/PluginMigrate.php @@ -0,0 +1,66 @@ +<?php + +namespace Studip\Cli\Commands\Plugins; + +use Studip\Cli\Commands\AbstractPluginCommand; +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; + +class PluginMigrate extends AbstractPluginCommand +{ + protected static $defaultName = 'plugin:migrate'; + + protected function configure(): void + { + $this->setDescription('Migrate a plugin.'); + $this->setHelp('This command migrates a plugin.'); + $this->addArgument('pluginname', InputArgument::REQUIRED, 'name of the plugin'); + $this->addOption('branch', 'b', InputOption::VALUE_OPTIONAL, 'branch of the migrations', '0'); + $this->addOption( + 'target', + 't', + InputOption::VALUE_REQUIRED, + 'the id number of the migration to migrate to', + null + ); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $pluginname = $input->getArgument('pluginname'); + $branch = $input->getOption('branch'); + $target = $input->getOption('target'); + $verbose = $input->getOption('verbose'); + + if (null !== $target) { + $target = (int) $target; + } + + $pluginManager = \PluginManager::getInstance(); + $plugin = $this->findPluginByName($pluginManager, $pluginname); + if (null === $plugin) { + $output->writeln('<error>Could not find plugin of that name.</error>'); + + return Command::FAILURE; + } + + $pluginpath = \Config::get()->PLUGINS_PATH . '/' . $plugin['path']; + + if (!is_dir($pluginpath . '/migrations')) { + $output->writeln('<comment>Could not find any migrations of that plugin.</comment>'); + + return Command::SUCCESS; + } + + // if there are migrations, migrate + $schemaVersion = new \DBSchemaVersion($plugin['name'], $branch); + $migrator = new \Migrator($pluginpath . '/migrations', $schemaVersion, $verbose); + + $migrator->migrateTo($target); + + return Command::SUCCESS; + } +} diff --git a/cli/Commands/Plugins/PluginRegister.php b/cli/Commands/Plugins/PluginRegister.php new file mode 100644 index 0000000000000000000000000000000000000000..5167fa3939f060b7b004a66f0568a40ea93391e6 --- /dev/null +++ b/cli/Commands/Plugins/PluginRegister.php @@ -0,0 +1,86 @@ +<?php + +namespace Studip\Cli\Commands\Plugins; + +use Studip\Cli\Commands\AbstractPluginCommand; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class PluginRegister extends AbstractPluginCommand +{ + protected static $defaultName = 'plugin:register'; + + protected function configure(): void + { + $this->setDescription('Register a plugin.'); + $this->setHelp('This command registers an installed plugin.'); + $this->addArgument('pluginpath', InputArgument::REQUIRED, 'path to the plugin'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $pluginpath = $input->getArgument('pluginpath'); + $pluginManager = \PluginManager::getInstance(); + $manifest = $pluginManager->getPluginManifest($pluginpath); + if (!$manifest) { + $output->writeln('<error>The plugin\'s manifest is missing.</error>'); + return Command::FAILURE; + } + + // get plugin meta data + $pluginclass = $manifest['pluginclassname']; + $origin = $manifest['origin']; + $minVersion = $manifest['studipMinVersion']; + $maxVersion = $manifest['studipMaxVersion']; + + // check for compatible version + if ( + (isset($minVersion) && \StudipVersion::olderThan($minVersion)) || + (isset($maxVersion) && \StudipVersion::newerThan($maxVersion)) + ) { + $output->writeln('<error>The plugin is not compatible with this version of Stud.IP.</error>'); + return Command::FAILURE; + } + + // determine the plugin path + $basepath = \Config::get()->PLUGINS_PATH; + $pluginpath = $origin . '/' . $pluginclass; + $pluginregistered = $pluginManager->getPluginInfo($pluginclass); + + // create database schema if needed + if (isset($manifest['dbscheme']) && !$pluginregistered) { + $schemafile = $pluginpath . '/' . $manifest['dbscheme']; + $contents = file_get_contents($schemafile); + $statements = preg_split("/;[[:space:]]*\n/", $contents, -1, PREG_SPLIT_NO_EMPTY); + $db = \DBManager::get(); + foreach ($statements as $statement) { + $db->exec($statement); + } + } + + // check for migrations + if (is_dir($pluginpath . '/migrations')) { + $schemaVersion = new \DBSchemaVersion($manifest['pluginname']); + $migrator = new \Migrator($pluginpath . '/migrations', $schemaVersion); + $migrator->migrateTo(null); + } + + // now register the plugin in the database + $pluginid = $pluginManager->registerPlugin($manifest['pluginname'], $pluginclass, $pluginpath); + + // register additional plugin classes in this package + $additionalclasses = $manifest['additionalclasses']; + + if (is_array($additionalclasses)) { + foreach ($additionalclasses as $class) { + $pluginManager->registerPlugin($class, $class, $pluginpath, $pluginid); + } + } + + $output->writeln('The plugin was successfully registered.'); + + return Command::SUCCESS; + } +} diff --git a/cli/Commands/Plugins/PluginScan.php b/cli/Commands/Plugins/PluginScan.php new file mode 100644 index 0000000000000000000000000000000000000000..72b443fe1c3f358cbb777daa9d4ab0fc13daea95 --- /dev/null +++ b/cli/Commands/Plugins/PluginScan.php @@ -0,0 +1,46 @@ +<?php + +namespace Studip\Cli\Commands\Plugins; + +use Studip\Cli\Commands\AbstractPluginCommand; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class PluginScan extends AbstractPluginCommand +{ + protected static $defaultName = 'plugin:scan'; + + protected function configure(): void + { + $this->setDescription('Scans for unregistered plugins.'); + $this->setHelp( + 'This command scans the plugin path for plugin.manifest files belonging to not registered plugins.' + ); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $pluginAdmin = new \PluginAdministration(); + $pluginManager = \PluginManager::getInstance(); + foreach ($pluginAdmin->scanPluginDirectory() as $manifest) { + if (!$pluginManager->getPluginInfo($manifest['pluginclassname'])) { + $pairs = []; + foreach ($manifest as $key => $value) { + $pairs[] = [$key, is_array($value) ? join(",", $value) : (string) $value]; + } + + $table = new Table($output); + $table->setHeaders(['Field', 'Value'])->setRows($pairs); + $table->setStyle('box'); + $table->render(); + + $output->writeln(''); + } + } + + return Command::SUCCESS; + } +} diff --git a/cli/Commands/Plugins/PluginStatusMigrations.php b/cli/Commands/Plugins/PluginStatusMigrations.php new file mode 100644 index 0000000000000000000000000000000000000000..8bc087522dfd0fd5f845e66781ef9c21705c3824 --- /dev/null +++ b/cli/Commands/Plugins/PluginStatusMigrations.php @@ -0,0 +1,68 @@ +<?php + +namespace Studip\Cli\Commands\Plugins; + +use Studip\Cli\Commands\AbstractPluginCommand; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class PluginStatusMigrations extends AbstractPluginCommand +{ + protected static $defaultName = 'plugin:status-migrations'; + + protected function configure(): void + { + $this->setDescription('Shows the state of all migrations of a plugin.'); + $this->setHelp('This command shows the state of all migrations of a plugin.'); + $this->addArgument('pluginname', InputArgument::REQUIRED, 'name of the plugin'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $pluginname = $input->getArgument('pluginname'); + $verbose = $input->getOption('verbose'); + + $pluginManager = \PluginManager::getInstance(); + $plugin = $this->findPluginByName($pluginManager, $pluginname); + if (null === $plugin) { + $output->writeln('<error>Could not find plugin of that name.</error>'); + + return Command::FAILURE; + } + + $pluginpath = \Config::get()->PLUGINS_PATH . '/' . $plugin['path']; + + if (!is_dir($pluginpath . '/migrations')) { + $output->writeln('<comment>Could not find any migrations of that plugin.</comment>'); + + return Command::SUCCESS; + } + + // if there are migrations, migrate + $schemaVersion = new \DBSchemaVersion($plugin['name']); + $migrator = new \Migrator($pluginpath . '/migrations', $schemaVersion, $verbose); + + + $migrations = $migrator->migrationClasses(); + uksort($migrations, 'version_compare'); + $migrations = array_reverse($migrations, true); + + $pending = $migrator->relevantMigrations(null); + + $rows = []; + foreach ($migrations as $number => $migration) { + $rows[] = [isset($pending[$number]) ? 'No' : 'Yes', $number, basename($migration[0], '.php')]; + } + + $table = new Table($output); + $table->setHeaders(['Ran?', 'ID', 'Migration'])->setRows($rows); + $table->setStyle('box'); + $table->render(); + + return Command::SUCCESS; + } +} diff --git a/cli/Commands/Plugins/PluginUnregister.php b/cli/Commands/Plugins/PluginUnregister.php new file mode 100644 index 0000000000000000000000000000000000000000..7b08c93770c8bbcbb6fe7d77b492ebd0350a9557 --- /dev/null +++ b/cli/Commands/Plugins/PluginUnregister.php @@ -0,0 +1,51 @@ +<?php + +namespace Studip\Cli\Commands\Plugins; + +use Studip\Cli\Commands\AbstractPluginCommand; +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; + +class PluginUnregister extends AbstractPluginCommand +{ + protected static $defaultName = 'plugin:unregister'; + + protected function configure(): void + { + $this->setDescription('Unregister a plugin.'); + $this->setHelp('This command unregisters a plugin.'); + $this->addArgument('pluginname', InputArgument::REQUIRED, 'name of the plugin'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $pluginname = $input->getArgument('pluginname'); + $verbose = $input->getOption('verbose'); + + $pluginManager = \PluginManager::getInstance(); + $plugin = $this->findPluginByName($pluginManager, $pluginname); + if (null === $plugin) { + $output->writeln('<error>Could not find plugin of that name.</error>'); + + return Command::FAILURE; + } + + $pluginManager->unregisterPlugin($plugin['id']); + + // if there are any migrations, un-migrate + $pluginpath = \Config::get()->PLUGINS_PATH . '/' . $plugin['path']; + if (is_dir($pluginpath . '/migrations')) { + $schemaVersion = new \DBSchemaVersion($plugin['name']); + $migrator = new \Migrator($pluginpath . '/migrations', $schemaVersion, $verbose); + + $migrator->migrateTo(0); + } + + $output->writeln('The plugin was unregistered successfully.'); + + return Command::SUCCESS; + } +} diff --git a/cli/Commands/Resources/UpdateBookingIntervals.php b/cli/Commands/Resources/UpdateBookingIntervals.php new file mode 100644 index 0000000000000000000000000000000000000000..0d0121126ef274b6ab2895ebdcf920d5a1624c16 --- /dev/null +++ b/cli/Commands/Resources/UpdateBookingIntervals.php @@ -0,0 +1,44 @@ +<?php + +namespace Studip\Cli\Commands\Resources; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class UpdateBookingIntervals extends Command +{ + protected static $defaultName = 'resources:update-booking-intervals'; + + protected function configure(): void + { + $this->setDescription('Update booking intervals.'); + $this->addOption( + 'remove-exceptions', + 'r', + InputOption::VALUE_OPTIONAL, + 'exceptions for a booking with repetitions twill be removed', + false + ); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $keep_exceptions = $input->getOption('remove-exceptions'); + if ($keep_exceptions !== false) { + $keep_exceptions = true; + } + $bookings = \ResourceBooking::findBySql('TRUE'); + + if ($bookings) { + foreach ($bookings as $booking) { + $booking->updateIntervals($keep_exceptions); + } + $output->writeln('The resource_booking_intervals table is up to date again!'); + } else { + $output->writeln('There are no bookings in your database! Nothing to do!'); + } + return Command::SUCCESS; + } +} diff --git a/cli/Commands/SORM/DescribeModels.php b/cli/Commands/SORM/DescribeModels.php new file mode 100644 index 0000000000000000000000000000000000000000..3d1256ff300a3c0abf57a7b256486cabb2b62a3e --- /dev/null +++ b/cli/Commands/SORM/DescribeModels.php @@ -0,0 +1,285 @@ +<?php +namespace Studip\Cli\Commands\SORM; + +use SimpleORMapCollection; +use Studip\Cli\Commands\AbstractCommand; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\ProgressBar; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +final class DescribeModels extends AbstractCommand +{ + protected static $defaultName = 'sorm:describe'; + + private $progress; + + protected function configure(): void + { + $this->setDescription('Describe models'); + $this->setHelp('This command will add neccessary @property annotations to SimpleORMap classes in a folder'); + + $this->addArgument( + 'folder', + InputArgument::OPTIONAL, + 'Folder to scan (will default to lib/models)', + 'lib/models' + ); + + $this->addOption( + 'recursive', + 'r', + InputOption::VALUE_NEGATABLE, + 'Scan into subfolders recursively' + ); + $this->addOption( + 'bootstrap', + 'b', + InputOption::VALUE_OPTIONAL, + 'Execute bootstrap file before scanning folder' + ); + } + + public function execute(InputInterface $input, OutputInterface $output) + { + $bootstrap = $input->getOption('bootstrap'); + if ($bootstrap) { + if (!file_exists($bootstrap)) { + throw new \Exception("Invalid bootstrap file {$bootstrap} provided"); + } + require_once $bootstrap; + } + + $recursive = $input->getOption('recursive') ?? false; + $folder = $input->getArgument('folder'); + $iterator = $this->getFolderIterator($folder, $recursive, ['php']); + + $this->progress = new ProgressBar($output, iterator_count($iterator)); + $this->progress->start(); + + foreach ($iterator as $file) { + $filename = $file->getFilename(); + + if (!is_writable($file->getRealPath())) { + $this->outputForFile( + $output, + '<comment>Skipping not writable file ' . $this->relativeFilePath($file->getPathname()) . '</comment>' + ); + continue; + } + + $class_name = str_replace('.class.php', '.php', $filename); + $class_name = pathinfo($class_name, PATHINFO_FILENAME); + if (!class_exists($class_name)) { + $class_name = $this->getClassNameFromFile($file->getPathname()) ?? $class_name; + } + + if (!class_exists($class_name) || !is_subclass_of($class_name, \SimpleORMap::class)) { + $this->outputForFile( + $output, + "Skipping invalid class file {$filename} (class {$class_name})", + OutputInterface::VERBOSITY_VERBOSE + ); + continue; + } + + try { + $reflection = new \ReflectionClass($class_name); + } catch (\Error $e) { + $this->outputForFile( + $output, + "<error>Could not get reflection for class {$class_name} ({$e->getMessage()})</error>" + ); + continue; + } + + if ($reflection->isAbstract()) { + $this->outputForFile( + $output, + "Skipping abstract class {$class_name}", + OutputInterface::VERBOSITY_VERBOSE + ); + continue; + } + + $model = $reflection->newInstance(); + $meta = $model->getTableMetaData(); + + $properties = []; + + foreach ($meta['fields'] as $field => $info) { + $name = mb_strtolower($field); + $type = $this->getPHPType($info); + $properties[$name] = [ + 'type' => $type, + 'description' => 'database column', + ]; + if ($alias = array_search($name, $meta['alias_fields'])) { + $properties[$alias] = [ + 'type' => $type, + 'description' => "alias column for {$name}", + ]; + } + } + + foreach ($meta['relations'] as $relation) { + $options = $model->getRelationOptions($relation); + $related_class_name = $options['class_name']; + if (in_array($options['type'], ['has_many', 'has_and_belongs_to_many'])) { + $related_class_name = SimpleORMapCollection::class; + } + + if ($reflection->inNamespace()) { + $related_class_name = "\\{$related_class_name}"; + if (mb_strpos($related_class_name, "\\{$reflection->getNamespaceName()}") === 0) { + $related_class_name = substr($related_class_name, strlen($reflection->getNamespaceName()) + 2); + } + } + + $properties[$relation] = [ + 'type' => $related_class_name, + 'description' => "{$options['type']} {$options['class_name']}", + ]; + } + + if ($this->updateDocBlockOfClass($reflection, $properties)) { + $this->outputForFile( + $output, + '<info>Updated ' . $this->relativeFilePath($file->getPathname()) . '</info>' + ); + } else { + $this->outputForFile( + $output, + 'No changes in ' . $this->relativeFilePath($file->getPathname()), + OutputInterface::VERBOSITY_VERBOSE + ); + } + } + + $this->progress->clear(); + + return Command::SUCCESS; + } + + private function outputForFile($output, ...$args) + { + $this->progress->advance(); + $this->progress->clear(); + $output->writeln(...$args); + $this->progress->display(); + + } + + private function getPHPType($info) + { + if (preg_match('/^(?:tiny|small|medium|big)?int(?:eger)?/iS', $info['type'])) { + return 'int'; + } + + if (preg_match('/^(?:decimal|double|float|numeric)/iS', $info['type'])) { + return 'float'; + } + + if (preg_match('/^bool(?:ean)?/iS', $info['type'])) { + return 'bool'; + } + + return 'string'; + } + + private function updateDocBlockOfClass(\ReflectionClass $reflection, array $properties): bool + { + $has_docblock = (bool) $reflection->getDocComment(); + $docblock = $reflection->getDocComment() ?: $this->getDefaultDocblock(); + + $docblock_lines = array_map('rtrim', explode("\n", $docblock)); + + $properties_started = false; + $docblock_lines = array_filter($docblock_lines, function ($line) use (&$properties_started) { + $line = ltrim($line, '* '); + if ($properties_started) { + return $line === '/'; + } + + $properties_started = strpos($line, '@property ') === 0; + return !$properties_started; + }); + + $docblock_lines = array_reverse($docblock_lines); + while ($docblock_lines[1] === ' *') { + array_splice($docblock_lines, 1, 1); + } + $docblock_lines = array_reverse($docblock_lines); + + $properties = array_map(function ($variable, $property) { + return " * @property {$property['type']} \${$variable} {$property['description']}"; + }, array_keys($properties), array_values($properties)); + + array_unshift($properties, ' *'); + array_splice($docblock_lines, -1, 0, $properties); + + $new_docblock = implode("\n", $docblock_lines); + + if ($docblock === $new_docblock) { + return false; + } + + $contents = file_get_contents($reflection->getFileName()); + if ($has_docblock) { + $contents = str_replace($docblock, $new_docblock, $contents); + } else { + $contents = preg_replace( + '/^class/m', + $new_docblock . "\nclass", + $contents, + 1 + ); + } + + file_put_contents($reflection->getFileName(), $contents); + + return true; + } + + private function getDefaultDocBlock(): string + { + return implode("\n", [ + '/**', + ' * @license GPL2 or any later version', + ' */', + ]); + } + + /** + * @see https://stackoverflow.com/a/14250011 + */ + private function getClassNameFromFile(string $filename): ?string + { + $code = file_get_contents($filename); + $tokens = token_get_all($code); + + $namespace = ''; + + for ($i = 0, $l = count($tokens); $i < $l; $i += 1) { + if ($tokens[$i][0] === T_NAMESPACE) { + for ($j= $i + 1; $j < $l; $j += 1) { + if ($tokens[$j][0] === T_STRING) { + $namespace .= "\\" . $tokens[$j][1]; + } elseif ($tokens[$j] === '{' || $tokens[$j] === ';') { + break; + } + } + } + if ($tokens[$i][0] === T_CLASS) { + for ($j = $i + 1; $j < $l; $j += 1) { + if ($tokens[$j] === '{') { + return ltrim($namespace . "\\" . $tokens[$i + 2][1], "\\"); + } + } + } + } + return null; + } +} diff --git a/cli/Commands/Translations/VueGettextSplitTranslations.php b/cli/Commands/Translations/VueGettextSplitTranslations.php new file mode 100644 index 0000000000000000000000000000000000000000..d9579f203f169e568a6b5ce794e4b8f25bc8e3fe --- /dev/null +++ b/cli/Commands/Translations/VueGettextSplitTranslations.php @@ -0,0 +1,35 @@ +<?php + +namespace Studip\Cli\Commands\Translations; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class VueGettextSplitTranslations extends Command +{ + protected static $defaultName = 'translations:vue-gettext-split'; + + protected function configure(): void + { + $this->setDescription('Split vue-gettext.'); + $this->setHelp('Split vue-gettext translations'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $translationsFile = $GLOBALS['STUDIP_BASE_PATH'] . '/resources/locales/translations.json'; + if (file_exists($translationsFile)) { + $file = file_get_contents($translationsFile); + $json = json_decode($file, true); + foreach ($json as $lang => $content) { + $langFile = realpath(__DIR__ . '/../resources/locales/') . '/' . $lang . '.json'; + file_put_contents($langFile, json_encode($content)); + } + return Command::SUCCESS; + } else { + $output->writeln(sprintf('<error>Could not find translations in %s</error>', $translationsFile)); + return Command::FAILURE; + } + } +} diff --git a/cli/Commands/Users/UserDelete.php b/cli/Commands/Users/UserDelete.php new file mode 100644 index 0000000000000000000000000000000000000000..2ba854e60d8c0d61edb37e8928a424174306ff31 --- /dev/null +++ b/cli/Commands/Users/UserDelete.php @@ -0,0 +1,89 @@ +<?php + +namespace Studip\Cli\Commands\Users; + +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; + +class UserDelete extends Command +{ + protected static $defaultName = 'user:delete'; + + protected function configure(): void + { + $this->setDescription('Delete users.'); + $this->setHelp('Delete multiple studip user accounts'); + $this->addArgument('range', InputArgument::REQUIRED, 'Username or path to csv-file'); + $this->addOption( + 'file_range', + 'f', + InputOption::VALUE_OPTIONAL, + 'Set to true, if you want use a txt file with username', + true + ); + $this->addOption('email', 'e', InputOption::VALUE_OPTIONAL, 'Send a deletion email', true); + $this->addOption( + 'delete_admins', + 'd', + InputOption::VALUE_OPTIONAL, + 'Admins can also be deleted on request', + false + ); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $range = $input->getArgument('range'); + $file_range = $input->getOption('file_range'); + $email = $input->getOption('email'); + $delete_admins = $input->getOption('delete_admins'); + + if ($email && !($MAIL_LOCALHOST && $MAIL_HOST_NAME && $ABSOLUTE_URI_STUDIP)) { + $output->writeln( + "<error>To use this script you MUST set correct values for $MAIL_LOCALHOST, $MAIL_HOST_NAME and $ABSOLUTE_URI_STUDIP in local.inc!</error>" + ); + } + + if (!(bool) $file_range) { + $usernames = [$range]; + } else { + if (!is_file($range)) { + $output->writeln(sprintf('<error>File not found: %s</error>', $range)); + return Command::FAILURE; + } + $file = fopen($range, 'r'); + $list = ''; + while (!feof($file)) { + $list .= fgets($file, 1024); + } + $usernames = preg_split('/[\s,;]+/', $list, -1, PREG_SPLIT_NO_EMPTY); + $usernames = array_unique($usernames); + } + + $users = \User::findBySQL('username IN (?)', [$usernames]); + + if (!empty($users)) { + foreach ($users as $user) { + if (!$delete_admins && ($user->perms == 'admin' || $user->perms == 'root')) { + $output->writeln(sprintf('User: %s is %s, NOT deleted', $user->username, $user->perms)); + } else { + $umanager = new \UserManagement($user->id); + //wenn keine Email gewünscht, Adresse aus den Daten löschen + if (!$email) { + $umanager->user_data['auth_user_md5.Email'] = ''; + } + if ($umanager->deleteUser()) { + $output->writeln(sprintf('<info>User: %s successfully deleted:</info>', $user->username)); + } else { + $output->writeln(sprintf('<error>User: %s NOT deleted</error>', $user->username)); + } + $output->writeln(parse_msg_to_clean_text($umanager->msg)); + } + } + } + return Command::SUCCESS; + } +} diff --git a/cli/antelope_to_barracuda.php b/cli/antelope_to_barracuda.php deleted file mode 100755 index 4360ffd4a68ea359767b780d474e0685758f8cda..0000000000000000000000000000000000000000 --- a/cli/antelope_to_barracuda.php +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/env php -<?php -require_once(__DIR__.'/studip_cli_env.inc.php'); - -echo 'Migration starting at '.date('d.m.Y H:i:s').".\n"; -$start = microtime(true); - -global $DB_STUDIP_DATABASE; - -// Tables to ignore on engine conversion. -$ignore_tables = []; - -// Check if InnoDB is enabled in database server. -$engines = DBManager::get()->fetchAll("SHOW ENGINES"); -$innodb = false; -foreach ($engines as $e) { - // InnoDB is found and enabled. - if ($e['Engine'] == 'InnoDB' && in_array(mb_strtolower($e['Support']), ['default', 'yes'])) { - $innodb = true; - break; - } -} - -if ($innodb) { - // Get version of database system (MySQL/MariaDB/Percona) - $data = DBManager::get()->fetchFirst("SELECT VERSION() AS version"); - $version = $data[0]; - - // Use Barracuda format if database supports it (5.5 upwards). - if (version_compare($version, '5.5', '>=')) { - echo "\tChecking if Barracuda file format is supported..."; - // Get innodb_file_per_table setting - $data = DBManager::get()->fetchOne("SHOW VARIABLES LIKE 'innodb_file_per_table'"); - $file_per_table = $data['Value']; - - // Check if Barracuda file format is enabled - $data = DBManager::get()->fetchOne("SHOW VARIABLES LIKE 'innodb_file_format'"); - $file_format = $data['Value']; - - if (mb_strtolower($file_per_table) == 'on' && mb_strtolower($file_format) == 'barracuda') { - - echo " yes.\n"; - - // Fetch all tables that need to be converted. - $tables = DBManager::get()->fetchFirst("SELECT TABLE_NAME - FROM `information_schema`.TABLES - WHERE TABLE_SCHEMA=:database AND ENGINE=:engine - AND ROW_FORMAT IN (:rowformats) - ORDER BY TABLE_NAME", - [ - ':database' => $DB_STUDIP_DATABASE, - ':engine' => 'InnoDB', - ':rowformats' => ['Compact', 'Redundant'] - ]); - - $newformat = 'DYNAMIC'; - - // Prepare query for table conversion. - $stmt = DBManager::get()->prepare("ALTER TABLE :database.:table ROW_FORMAT=:newformat"); - $stmt->bindParam(':database', $DB_STUDIP_DATABASE, StudipPDO::PARAM_COLUMN); - $stmt->bindParam(':newformat', $newformat, StudipPDO::PARAM_COLUMN); - - if (count($tables) > 0) { - - // Now convert the found tables. - foreach ($tables as $t) { - $local_start = microtime(true); - $stmt->bindParam(':table', $t, StudipPDO::PARAM_COLUMN); - $stmt->execute(); - $local_end = microtime(true); - $local_duration = $local_end - $local_start; - $human_local_duration = sprintf("%02d:%02d:%02d", - ($local_duration / 60 / 60) % 24, ($local_duration / 60) % 60, $local_duration % 60); - - echo "\tConversion of table " . $t . " took " . $human_local_duration . ".\n"; - } - - } else { - echo "\tNo Antelope format tables found.\n"; - } - - } else { - echo " no:\n"; - if (mb_strtolower($file_per_table) != 'on') { - echo "\t- file_per_table not set\n"; - } - if (mb_strtolower($file_format) != 'barracuda') { - echo "\t- file_format not set to Barracuda (but to " . $file_format . ")\n"; - } - } - - $end = microtime(true); - - $duration = $end - $start; - $human_duration = sprintf("%02d:%02d:%02d", - ($duration / 60 / 60) % 24, ($duration / 60) % 60, $duration % 60); - - echo 'Migration finished at ' . date('d.m.Y H:i:s') . ', duration ' . $human_duration . ".\n"; - - } else { - echo "Your database server does not yet support the Barracuda row format (you need at least 5.5).\n"; - } - -} else { - echo "The storage engine InnoDB is not enabled in your ". - "database installation, tables cannot be converted.\n"; -} diff --git a/cli/biest7783-fix.php b/cli/biest7783-fix.php deleted file mode 100755 index e3cad62b0889ded50e60d958beebbb3653ebd9e5..0000000000000000000000000000000000000000 --- a/cli/biest7783-fix.php +++ /dev/null @@ -1,137 +0,0 @@ -#!/usr/bin/env php -<?php -/** - * This script removes all members from a course that should not have been - * members in the first place. - * - * @author Jan-Hendrik Willms <tleilax+studip@gmail.com> - * @see https://develop.studip.de/trac/ticket/7783 - */ - -require_once __DIR__ . '/studip_cli_env.inc.php'; -require_once __DIR__ . '/../config/config_local.inc.php'; - -function output($what) { - if (StudipVersion::olderThan(4)) { - $what = studip_utf8encode($what); - } - - fwrite(STDOUT, $what); -} - -$opts = getopt('d', ['dry-run']); -$dry_run = isset($opts['d']) || isset($opts['dry-run']); - -// Reduce arguments by options (this is far from perfect) -$args = $_SERVER['argv']; -$arg_stop = array_search('--', $args); -if ($arg_stop !== false) { - $args = array_slice($args, $arg_stop + 1); -} elseif (count($opts)) { - $args = array_slice($args, 1 + count($opts)); -} else { - $args = array_slice($args, 1); -} - -if (count($args) < 1) { - output("Fix for Biest 7783 - Use {$argv[0]} [--dry-run/-d] <semester_id,current,next>\n"); - exit(0); -} - -$semester_ids = explode(',', implode(',', array_map('trim', $args))); -foreach ($semester_ids as $index => $semester_id) { - if ($semester_id === 'current') { - $semester_id = Semester::findCurrent()->id; - } elseif ($semester_id === 'next') { - $semester_id = Semester::findNext()->id; - } elseif (Semester::find($semester_id) === null) { - output("Semester id {$semester_id} is invalid\n"); - exit(0); - } - - $semester_ids[$index] = $semester_id; -} - -$query = "SELECT DISTINCT - cs.`set_id`, s.`seminar_id` - FROM `semester_data` AS sd - JOIN `seminare` AS s - ON (s.`start_time` <= sd.`beginn` - AND ( - sd.`beginn` <= s.`start_time` + s.`duration_time` - OR s.`duration_time` = -1 - ) - ) - JOIN `seminar_courseset` AS scs USING (`seminar_id`) - JOIN `coursesets` AS cs USING (`set_id`) - JOIN `auth_user_md5` USING (`user_id`) - JOIN `courseset_rule` AS csr USING (`set_id`) - JOIN `admission_condition` AS ac USING (`rule_id`) - JOIN `userfilter` AS uf USING (`filter_id`) - JOIN `userfilter_fields` AS uff USING (`filter_id`) - WHERE `semester_id` IN (:semester_ids) - AND `algorithm_run` = 0 - AND uff.`type` = 'SemesterOfStudyCondition' - AND uff.`value` > 1 - ORDER BY cs.`name` ASC, s.`name`"; -$statement = DBManager::get()->prepare($query); -$statement->bindValue(':semester_ids', $semester_ids); -$statement->execute(); -$sets = $statement->fetchAll(PDO::FETCH_GROUP | PDO::FETCH_COLUMN); - -foreach ($sets as $set_id => $course_ids) { - $courseset = new CourseSet($set_id); - $remove = []; - foreach ($course_ids as $course_id) { - $course = Course::find($course_id); - $members = new MembersModel($course_id, $course->getFullname()); - $applicants = $members->getAdmissionMembers(); - foreach (['awaiting', 'claiming'] as $status) { - foreach ($applicants[$status] as $applicant) { - $errors = $courseset->checkAdmission($applicant->user_id, $course_id); - if (count($errors) === 0) { - continue; - } - - if (!isset($remove[$course_id])) { - $remove[$course_id] = [ - 'course' => $course, - 'members' => $members, - 'status' => [], - ]; - } - if (!isset($remove[$course_id]['status'][$status])) { - $remove[$course_id]['status'][$status] = []; - } - - $remove[$course_id]['status'][$status][] = User::find($applicant['user_id']); - } - } - } - - if ($remove) { - $owner = User::find($courseset->getUserId())->getFullname(); - output("= Anmeldeset {$courseset->getName()} ({$owner}):\n"); - - foreach ($remove as $row) { - output(" - Veranstaltung {$row['course']->getFullname()}:\n"); - foreach ($row['status'] as $status => $users) { - $user_ids = array_map(function (User $user) { - return $user->id; - }, $users); - - if ($dry_run) { - foreach ($users as $user) { - output(" - Nutzer {$user->getFullname()}\n"); - } - } else { - $result = $row['members']->cancelAdmissionSubscription($user_ids, $status); - foreach ($result as $row) { - output(" - Nutzer {$row}\n"); - } - } - - } - } - } -} diff --git a/cli/biest7789-fix.php b/cli/biest7789-fix.php deleted file mode 100755 index e94a9f35d143e92140f14f14e27293892c1f78b8..0000000000000000000000000000000000000000 --- a/cli/biest7789-fix.php +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env php -<?php -/** - * This script converts selected database columns from php serialization to json - * - * @author Till Glöggler <studip@tillgloeggler.de> - * @see https://develop.studip.de/trac/ticket/7789 - */ - -require_once __DIR__ . '/studip_cli_env.inc.php'; -require_once __DIR__ . '/../config/config_local.inc.php'; - -ini_set('default_charset', 'utf-8'); - -function legacy_studip_utf8encode($data) -{ - if (is_array($data)) { - $new_data = []; - foreach ($data as $key => $value) { - $key = legacy_studip_utf8encode($key); - $new_data[$key] = legacy_studip_utf8encode($value); - } - return $new_data; - } - - if (!preg_match('/[\200-\377]/', $data) && !preg_match("'&#[0-9]+;'", $data)) { - return $data; - } else { - return mb_decode_numericentity( - mb_convert_encoding($data,'UTF-8', 'WINDOWS-1252'), - [0x100, 0xffff, 0, 0xffff], - 'UTF-8' - ); - } -} - - -function convert_to_json($table, $column, $where = null) -{ - $db = DBManager::get(); - - echo "\n\n /*************************************************\n"; - echo " ***** " . $table ." ***** "; - echo "\n *************************************************/\n\n"; - - // get primary keys - $result = $db->query("SHOW KEYS FROM $table WHERE Key_name = 'PRIMARY'"); - $keys = []; - - while ($data = $result->fetch(PDO::FETCH_ASSOC)) { - $keys[] = $data['Column_name']; - } - - // retrieve and convert data - $result = $db->query("SELECT `". implode('`,`', $keys) ."`, `$column` FROM `$table` WHERE ". ($where ?: '1')); - - while ($data = $result->fetch(PDO::FETCH_ASSOC)) { - $content = unserialize(legacy_studip_utf8decode($data[$column])); - - if ($content === false) { - // try to fix string length denotations - $fixed = preg_replace_callback( - '/s:([0-9]+):\"(.*?)\";/s', - function ($matches) { return "s:".strlen($matches[2]).':"'.$matches[2].'";'; }, - $data[$column] - ); - - $content = unserialize(legacy_studip_utf8decode($fixed)); - } - - if ($content !== false) { - // encode all data - $json = json_encode(legacy_studip_utf8encode($content), true); - - $query = "UPDATE `$table` SET `$column` = ". $db->quote($json) ."\n WHERE "; - - $where_query = []; - foreach ($keys as $key) { - $where_query[] = "`$key` = ". $db->quote($data[$key]); - } - - $q = $query . implode(' AND ', $where_query); - $db->exec($q); - echo $q .";\n"; - } else { - echo '/* Could not convert: '. print_r($data, 1) ." */\n"; - } - } -} - -convert_to_json('extern_config', 'config'); -convert_to_json('aux_lock_rules', 'attributes'); -convert_to_json('aux_lock_rules', 'sorting'); -convert_to_json('user_config', 'value', "field = 'MY_COURSES_ADMIN_VIEW_FILTER_ARGS'"); -convert_to_json('mail_queue_entries', 'mail'); diff --git a/cli/biest7866-fix.php b/cli/biest7866-fix.php deleted file mode 100755 index ca1f90c094dad07855a0b3ca3ea06dc567902df8..0000000000000000000000000000000000000000 --- a/cli/biest7866-fix.php +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env php -<?php -/** - * This script sets folder range_ids to the range_ids of their parent folder. - * - * @author Thomas Hackl <thomas.hackl@uni-passau.de> - * @see https://develop.studip.de/trac/ticket/7866 - */ - -require_once __DIR__ . '/studip_cli_env.inc.php'; - -/** - * Sets the range_id of all child folders to the given range_id. - * @param $parent_folder - * @param $range_id - */ -function setFolderRangeId($parent_folder, $range_id) { - // Update all child folder range_ids. - DBManager::get()->execute( - "UPDATE `folders` SET `range_id` = :range WHERE `parent_id` = :parent", - [ - 'range' => $range_id, - 'parent' => $parent_folder - ] - ); - - // Recursion: set correct range_id for child folders with wrong range_id. - $children = DBManager::get()->fetchAll( - "SELECT `id`, `range_id` FROM `folders` WHERE `parent_id` = :parent", - [ - 'parent' => $parent_folder - ] - ); - foreach ($children as $child) { - if ($child['range_id'] != $range_id) { - echo sprintf("Folder %s -> range_id %s.\n", $child['id'], $range_id); - } - setFolderRangeId($child['id'], $range_id); - } -} - -// Fetch all root folders and process their children recursively. -$root_folders = DBManager::get()->fetchAll("SELECT `id`, `range_id` FROM `folders` WHERE `parent_id` = ''"); - -foreach ($root_folders as $r) { - setFolderRangeId($r['id'], $r['range_id']); -} diff --git a/cli/biest8136-fix.php b/cli/biest8136-fix.php deleted file mode 100755 index c1bad9a5c81b725cfc5f25df4dd8ab76cedfde5f..0000000000000000000000000000000000000000 --- a/cli/biest8136-fix.php +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env php -<?php -/** - * This script adjusts all activities so that anonymous posts will actually be - * anonymous. - * - * @author Jan-Hendrik Willms <tleilax+studip@gmail.com> - * @see https://develop.studip.de/trac/ticket/8136 - */ - -require_once __DIR__ . '/studip_cli_env.inc.php'; -require_once __DIR__ . '/../config/config_local.inc.php'; - -$query = "UPDATE `activities` - SET `actor_type` = 'anonymous', - `actor_id` = '' - WHERE `provider` = :provider - AND `actor_type` != 'anonymous' - AND `object_id` IN ( - SELECT `topic_id` - FROM `forum_entries` - WHERE `anonymous` != 0 - )"; -$statement = DBManager::get()->prepare($query); -$statement->bindValue(':provider', 'Studip\\Activity\\ForumProvider'); -$statement->execute(); - -printf( - "%u forum post activities were anonymized\n", - $statement->rowCount() -); diff --git a/cli/check-help-tours.php b/cli/check-help-tours.php deleted file mode 100755 index e028621285c1bacb44d37e70a2de4a23ab314ce1..0000000000000000000000000000000000000000 --- a/cli/check-help-tours.php +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/env php -<?php -/** - * This script will check whether the help tours steps are still valid - * regarding the controllers and actions. - * - * @author Jan-Hendrik Willms <tleilax+studip@gmail.com> - */ - -require_once __DIR__ . '/studip_cli_env.inc.php'; -require_once __DIR__ . '/../config/config_local.inc.php'; - -foreach (HelpTour::findBySQL('1 ORDER BY name ASC') as $tour) { - if (!$tour->settings->active) { - continue; - } - - $errors = []; - foreach ($tour->steps->orderBy('step ASC') as $step) { - try { - if (strpos($step->route, 'plugins.php') === 0) { - $result = PluginEngine::routeRequest(substr($step->route, strlen('plugins.php') + 1)); - - // retrieve corresponding plugin info - $plugin_manager = PluginManager::getInstance(); - $plugin_info = $plugin_manager->getPluginInfo($result[0]); - - $file = implode('/', [ -// $GLOBALS['ABSOLUTE_PATH_STUDIP'], - Config::get()->PLUGINS_PATH, - $plugin_info['path'], - $plugin_info['class'], - ]); - - if (file_exists($file . '.php')) { - $file .= '.php'; - } elseif (file_exists($file . '.class.php')) { - $file .= '.class.php'; - } else { - throw new Exception(); - } - require_once $file; - $plugin = new $plugin_info['class']; - - if ($result[1]) { - $dispatcher = new Trails_Dispatcher( - $GLOBALS['ABSOLUTE_PATH_STUDIP'] . $plugin->getPluginPath(), - rtrim(PluginEngine::getLink($plugin, [], null, true), '/'), - 'index' - ); - $dispatcher->current_plugin = $plugin; - $parsed = $dispatcher->parse($result[1]); - $controller = $dispatcher->load_controller($parsed[0]); - if ($parsed[1] && !$controller->has_action($parsed[1])) { - throw new Exception(); - } - } - } elseif (strpos($step->route, 'dispatch.php') === 0) { - $dispatcher = new StudipDispatcher(); - $parsed = $dispatcher->parse(substr($step->route, strlen('dispatch.php') + 1)); - $controller = $dispatcher->load_controller($parsed[0]); - if ($parsed[1] && !$controller->has_action($parsed[1])) { - throw new Exception(); - } - } elseif (!file_exists("{$GLOBALS['ABSOLUTE_PATH_STUDIP']}{$step->route}")) { - throw new Exception(); - } - } catch (Exception $e) { - $errors[$step->step] = $step->route; - } - } - - if ($errors) { - $type = ucfirst($tour->type); - echo "{$type} '{$tour->name}' has errors in the following steps:\n"; - foreach ($errors as $step => $route) { - echo "- Step {$step}: {$route}\n"; - } - } -} diff --git a/cli/cleanup_admission_rules.php b/cli/cleanup_admission_rules.php deleted file mode 100755 index 49998ff1e77d995e8bcd9b8a9af0a75d0a9241d0..0000000000000000000000000000000000000000 --- a/cli/cleanup_admission_rules.php +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env php -<?php -/** - * cleanup_admission_rules.php - * - * deletes entries in %admissions tables - * which were orphaned by BIEST #6617 - * - * @author André Noack <noack@data-quest.de> - * @license GPL2 or any later version - * @copyright Stud.IP Core Group - */ -require_once 'studip_cli_env.inc.php'; -require_once 'lib/classes/admission/CourseSet.class.php'; - -$sql = "SELECT * FROM -( -SELECT rule_id,'ConditionalAdmission' as class FROM `conditionaladmissions` -UNION -SELECT rule_id,'CourseMemberAdmission' as class FROM `coursememberadmissions` -UNION -SELECT rule_id,'LimitedAdmission' as class FROM limitedadmissions -UNION -SELECT rule_id,'LockedAdmission' as class FROM lockedadmissions -UNION -SELECT rule_id,'ParticipantRestrictedAdmission' as class FROM participantrestrictedadmissions -UNION -SELECT rule_id,'PasswordAdmission' as class FROM passwordadmissions -UNION -SELECT rule_id,'TimedAdmission' as class FROM timedadmissions -) a -LEFT JOIN courseset_rule USING(rule_id) WHERE set_id IS NULL"; - -$foo = new CourseSet(); -$c1 = $c2 = 0; -DBManager::get() -->fetchAll($sql, null, function ($data) use (&$c1,&$c2) { - $c1++; - if (class_exists($data['class'])) { - $rule = new $data['class']($data['rule_id']); - if ($rule->getId() === $data['rule_id']) { - echo 'deleting: ' . $rule->getName() . ' with id: ' . $rule->getId() . chr(10); - $c2++; - $rule->delete(); - } - } -} -); -printf("found: %s deleted: %s \n", $c1,$c2); diff --git a/cli/compatibility-rules/studip-4.0.php b/cli/compatibility-rules/studip-4.0.php deleted file mode 100644 index 70475b838c502a63ac41d6fbe697fb54277aa534..0000000000000000000000000000000000000000 --- a/cli/compatibility-rules/studip-4.0.php +++ /dev/null @@ -1,179 +0,0 @@ -<?php -// "Rules"/definitions for critical changes in 4.0 -return [ - 'cssClassSwitcher' => 'Remove completely, use #{yellow:<table class="default">} instead.', - '$csssw' => '[#{cyan:cssClassSwitcher}] Remove completely, use #{yellow:<table class="default">} instead.', - - 'DBMigration' => 'Use #{yellow:Migration} instead', - - 'Request::removeMagicQuotes()' => 'Remove completely since magic quotes are removed from php', - - 'base_without_infobox' => 'Use #{yellow:layouts/base.php} instead.', - 'deprecated_tabs_layout' => 'Don\'t use this. Use the global layout #{yellow:layouts/base.php} and #{yellow:Navigation} instead.', - 'setInfoBoxImage' => 'Replace with #{yellow:Sidebar}', - 'addToInfobox' => 'Replace with #{yellow:Sidebar}', - 'InfoboxElement' => 'Replace with appropriate #{yellow:Sidebar} element', - 'InfoboxWidget' => 'Replace with appropriate #{yellow:Sidebar} widget', - - 'details.php' => 'Link to #{yellow:dispatch.php/course/details} instead', - 'institut_main.php' => 'Link to #{yellow:dispatch.php/institute/overview} instead', - 'meine_seminare.php' => 'Link to #{yellow:dispatch.php/my_courses} instead', - 'sms_box.php' => 'Link to #{yellow:dispatch.php/messages/overview} or #{yellow:dispatch.php/messages/sent} instead', - 'sms_send.php' => 'Link to #{yellow:dispatch.php/messages/write} instead', - - 'get_global_perm' => 'Use #{yellow:$GLOBALS[\'perm\']->get_perm()} instead', - 'log_event(' => 'Use #{yellow:StudipLog::log()} instead', - '->removeOutRangedSingleDates' => 'Use #{yellow:SeminarCycleDate::removeOutRangedSingleDates} instead', - - 'HolidayData' => 'Use class #{yellow:SemesterHoliday} instead', - - 'CourseTopic::createFolder' => 'Use #{yellow:CourseTopic::connectWithDocumentFolder()} instead', - 'SimpleORMap::haveData' => 'Use #{yellow:SimpleORMap::isDirty()} or #{yellow:SimpleORMap::isNew()} instead', - 'Seminar::getMetaDateType' => 'Don\'t use this!', - 'UserConfig::setUserId' => 'Don\'t use this. #{yellow:Set the user via the constructor}.', - - 'StudIPTemplateEngine' => 'Time to refactor your plugin.', - 'AbstractStudIPAdministrationPlugin' => 'Time to refactor your plugin.', - 'AbstractStudIPCorePlugin' => 'Time to refactor your plugin.', - 'AbstractStudIPHomepagePlugin' => 'Time to refactor your plugin.', - 'AbstractStudIPLegacyPlugin' => 'Time to refactor your plugin.', - 'AbstractStudIPPortalPlugin' => 'Time to refactor your plugin.', - 'AbstractStudIPStandardPlugin' => 'Time to refactor your plugin.', - 'AbstractStudIPSystemPlugin' => 'Time to refactor your plugin.', - 'new Permission(' => 'Time to refactor your plugin.', - 'Permission::' => 'Time to refactor your plugin.', - 'PluginNavigation' => 'Time to refactor your plugin.', - 'new StudIPUser(' => 'Time to refactor your plugin.', - 'StudIPUser::' => 'Time to refactor your plugin.', - 'StudipPluginNavigation' => 'Time to refactor your plugin.', - 'getLinkToAdministrationPlugin' => 'Time to refactor your plugin.', - 'getCurrentPluginId' => 'Time to refactor your plugin.', - 'saveToSession' => 'Time to refactor your plugin.', - 'getValueFromSession' => 'Time to refactor your plugin.', - - 'ContainerTable' => false, - 'DbCrossTableView' => false, - 'DbPermissions' => false, - - 'pclzip' => 'Use #{yellow:Studip\\ZipArchive} instead', - 'get_global_visibility_by_id' => 'Use #{yellow:User::visible} attribute instead', - - 'getSeminarRoomRequest' => 'Use #{yellow:RoomRequest} model instead', - 'getDateRoomRequest' => 'Use #{yellow:RoomRequest} model instead', - - 'ldate' => 'Use PHP\'s #{yellow:date()} or #{yellow:strftime()} function instead', - 'day_diff' => 'Use PHP\'s #{yellow:DateTime::diff()} method instead', - 'get_day_name' => 'Use PHP\'s #{yellow:strftime()} function with #{yellow:parameter \'%A\'} instead', - 'wday(' => 'Use #{strftime("%a")} or #{strftime("%A")} instead', - - 'get_ampel_state' => false, - 'get_ampel_write' => false, - 'get_ampel_read' => false, - 'localePictureUrl' => false, - 'localeUrl' => false, - 'isDatesMultiSem' => false, - 'getMetadateCorrespondingDates' => false, - 'getCorrespondingMetadates' => false, - 'create_year_view' => false, - 'javascript_hover_year' => false, - 'js_hover' => false, - 'info_icons' => false, - - 'get_message_attachments' => 'Use #{yellow:Message::attachments} attribute instead', - 'view_turnus' => 'Use #{yellow:Seminar::getFormattedTurnus()} instead', - - 'AddNewStatusgruppe' => 'Use class #{yellow:Statusgruppe} or model #{yellow:Statusgruppen} instead (yupp, this is still pretty fucked up).', - 'CheckSelfAssign' => 'Use class #{yellow:Statusgruppe} or model #{yellow:Statusgruppen} instead (yupp, this is still pretty fucked up).', - 'CheckSelfAssignAll' => 'Use class #{yellow:Statusgruppe} or model #{yellow:Statusgruppen} instead (yupp, this is still pretty fucked up).', - 'CheckAssignRights' => 'Use class #{yellow:Statusgruppe} or model #{yellow:Statusgruppen} instead (yupp, this is still pretty fucked up).', - 'SetSelfAssignAll' => 'Use class #{yellow:Statusgruppe} or model #{yellow:Statusgruppen} instead (yupp, this is still pretty fucked up).', - 'SetSelfAssignExclusive' => 'Use class #{yellow:Statusgruppe} or model #{yellow:Statusgruppen} instead (yupp, this is still pretty fucked up).', - 'EditStatusgruppe' => 'Use class #{yellow:Statusgruppe} or model #{yellow:Statusgruppen} instead (yupp, this is still pretty fucked up).', - 'MovePersonPosition' => 'Use class #{yellow:Statusgruppe} or model #{yellow:Statusgruppen} instead (yupp, this is still pretty fucked up).', - 'SortPersonInAfter' => 'Use class #{yellow:Statusgruppe} or model #{yellow:Statusgruppen} instead (yupp, this is still pretty fucked up).', - 'SortStatusgruppe' => 'Use class #{yellow:Statusgruppe} or model #{yellow:Statusgruppen} instead (yupp, this is still pretty fucked up).', - 'SubSortStatusgruppe' => 'Use class #{yellow:Statusgruppe} or model #{yellow:Statusgruppen} instead (yupp, this is still pretty fucked up).', - 'resortStatusgruppeByRangeId' => 'Use class #{yellow:Statusgruppe} or model #{yellow:Statusgruppen} instead (yupp, this is still pretty fucked up).', - 'SwapStatusgruppe' => 'Use class #{yellow:Statusgruppe} or model #{yellow:Statusgruppen} instead (yupp, this is still pretty fucked up).', - 'CheckStatusgruppe' => 'Use class #{yellow:Statusgruppe} or model #{yellow:Statusgruppen} instead (yupp, this is still pretty fucked up).', - 'GetRangeOfStatusgruppe' => 'Use class #{yellow:Statusgruppe} or model #{yellow:Statusgruppen} instead (yupp, this is still pretty fucked up).', - 'GetGroupsByCourseAndUser' => 'Use class #{yellow:Statusgruppe} or model #{yellow:Statusgruppen} instead (yupp, this is still pretty fucked up).', - 'getOptionsOfStGroups' => 'Use class #{yellow:Statusgruppe} or model #{yellow:Statusgruppen} instead (yupp, this is still pretty fucked up).', - 'setOptionsOfStGroup' => 'Use class #{yellow:Statusgruppe} or model #{yellow:Statusgruppen} instead (yupp, this is still pretty fucked up).', - 'GetStatusgruppeLimit' => 'Use class #{yellow:Statusgruppe} or model #{yellow:Statusgruppen} instead (yupp, this is still pretty fucked up).', - 'CheckStatusgruppeFolder' => 'Use class #{yellow:Statusgruppe} or model #{yellow:Statusgruppen} instead (yupp, this is still pretty fucked up).', - 'CheckStatusgruppeMultipleAssigns' => 'Use class #{yellow:Statusgruppe} or model #{yellow:Statusgruppen} instead (yupp, this is still pretty fucked up).', - 'sortStatusgruppeByName' => 'Use class #{yellow:Statusgruppe} or model #{yellow:Statusgruppen} instead (yupp, this is still pretty fucked up).', - 'getPersons(' => 'Use class #{yellow:Statusgruppe} or model #{yellow:Statusgruppen} instead (yupp, this is still pretty fucked up).', - 'getSearchResults(' => 'Use class #{yellow:Statusgruppe} or model #{yellow:Statusgruppen} instead (yupp, this is still pretty fucked up).', - 'setExternDefaultForUser' => 'Use class #{yellow:Statusgruppe} or model #{yellow:Statusgruppen} instead (yupp, this is still pretty fucked up).', - - 'GetStatusgruppeName' => 'Use #{yellow:Statusgruppen::find($id)->name} instead', - 'GetStatusgruppenForUser' => 'Use class #{yellow:Statusgruppe} or model #{yellow:Statusgruppen} instead (yupp, this is still pretty fucked up).', - - 'get_global_visibility_by_id' => 'Use #{yellow:User::find($id)->visible} instead', - 'get_global_visibility_by_username' => 'Use #{yellow:User::findByUsername($username)->visible} instead', - - 'get_local_visibility_by_username' => false, - 'get_homepage_element_visibility' => false, - 'set_homepage_element_visibility' => false, - 'checkVisibility' => 'Use #{yellow:Visibility::verify($param, $this->current_user->user_id)} instead', - - 'InsertPersonStatusgruppe' => 'Use #{Statusgruppen::addUser()} instead', - 'RemovePersonStatusgruppe(' => 'Use #{yellow:Statusgruppen::find($group_id)->removeUser($user_id)} instead', - 'RemovePersonStatusgruppeComplete' => 'Use #{yellow:Statusgruppen::find($group_id)->removeUser($user_id, true)} instead. Maybe you will need to do this on a collection of groups for a course or institute.', - 'RemovePersonFromAllStatusgruppen' => 'Use #{yellow:StatusgruppeUser::deleteBySQL("user_id = ?", [$user_id])} instead.', - 'DeleteAllStatusgruppen' => 'Use #{yellow:Statusgruppen::deleteBySQL("range_id = ?", [$id]);} instead', - 'DeleteStatusgruppe' => 'Use #{yellow:Statusgruppen::delete()} - or #{yellow:Statusgruppen::remove()} if you want to keep the child groups.', - 'moveStatusgruppe' => false, - 'CheckUserStatusgruppe' => 'Use #{yellow:StatusgruppeUser::exists([$group_id, $user_id])} instead.', - 'CountMembersStatusgruppen' => false, - 'CountMembersPerStatusgruppe' => false, - 'MakeDatafieldsDefault' => 'No longer neccessary.', - 'MakeUniqueStatusgruppeID' => 'No longer neccessary. SORM will create ids for you.', - 'GetAllSelected' => 'Use #{yellow:Statusgruppen::findAllByRangeId()} instead.', - 'getStatusgruppenIDS' => 'Use #{yellow:Statusgruppen::findByRange_id()} instead.', - 'getAllStatusgruppenIDS' => 'Use #{yellow:Statusgruppen::findAllByRangeId()} instead.', - 'getPersonsForRole' => 'Use #{yellow::Statusgruppen::members} instead.', - 'isVatherDaughterRelation' => false, - 'SetSelfAssign(' => false, - 'getExternDefaultForUser' => 'Use #{yellow:InstituteMember::getDefaultInstituteIdForUser($user_id)} instead.', - 'checkExternDefaultForUser' => 'Use #{yellow:InstituteMember::ensureDefaultInstituteIdForUser($user_id)} instead.', - 'getAllChildIDs' => false, - 'getKingsInformations' => 'Use #{yellow:User} model instead', - - 'AutoInsert::existSeminars' => false, - 'new ZebraTable' => 'No longer neccessary. Use #{table.default} instead.', - 'new Table' => 'No longer neccessary. Use #{table.default} instead.', - - //old datei.inc.php and visual.inc.php functions: - 'createSelectedZip' => 'Removed. Use #{yellow:FileArchiveManager::createArchiveFromFileRefs} instead.', - 'create_zip_from_directory' => 'Removed(?). Use #{yellow:FileArchiveManager::createArchiveFromPhysicalFolder} instead.', - 'getFileExtension' => 'Removed. Use PHP\'s built-in #{yellow:pathinfo($filename, PATHINFO_EXTENSION)} instead.', - 'get_icon_for_mimetype' => 'Removed. Use #{yellow:FileManager::getIconNameForMimeType} instead.', - 'get_upload_file_path' => 'Removed. Use #{yellow:File->getPath()} instead.', - 'GetDownloadLink' => 'Removed. Use one of the following alternatives instead: #{yellow:FileRef->getDownloadURL()}, #{yellow:FileManager::getDownloadLinkForArchivedCourse}, #{yellow:FileManager::getDownloadLinkForTemporaryFile} or #{yellow:FileManager::getDownloadURLForTemporaryFile}', - 'prepareFilename' => 'Removed. Use #{yellow:FileManager::cleanFileName} instead.', - 'GetFileIcon' => 'Removed. Use #{yellow:FileManager::getIconNameForMimeType} instead.', - 'parse_link' => 'Removed. Use #{yellow:FileManager::fetchURLMetadata} instead.', - 'unzip_file' => 'Removed. Use #{yellow:Studip\ZipArchive::extractToPath} or #yellow:Studip\ZipArchive::test} instead.', - 'datei.inc.php' => 'Removed. Use methods in functions.inc.php, FileManager, FileArchiveManager, FileRef, File or FolderType instead.', - 'TrackAccess' => 'Removed(?). Use {yellow:FileRef::incrementDownloadCounter}', - //StudipDocument and related classes: - 'StudipDocument(' => 'Removed(?). Use class #{yellow:FileRef} instead.', - 'DocumentFolder(' => 'Removed(?). Use class #{yellow:Folder} instead.', - 'StudipDocumentTree(' => 'Removed(?). Use class #{yellow:Folder} or #{yellow:FolderType} instead.', - 'WysiwygDocument' => 'Deprecated/To be removed. Use class #{yellow:FileRef} in conjunction with a #{yellow:FolderType} implementation instead.', - - 'ZIP_USE_INTERNAL' => 'Removed. Please avoid querying the value of this configuration variable!', - 'ZIP_PATH' => 'Removed. Please avoid querying the value of this configuration variable!', - 'ZIP_OPTIONS' => 'Removed. Please avoid querying the value of this configuration variable!', - 'UNZIP_PATH' => 'Removed. Please avoid querying the value of this configuration variable!', - - 'RuleAdministrationModel::getAdmissionRuleTypes' => 'Use #{yellow:AdmissionRule::getAvailableAdmissionRules(false)} instead.', - 'SessSemName' => 'Use class #{yellow:Context} instead', - '_SESSION["SessionSeminar"]' => 'Use class #{yellow:Context} instead', - '_SESSION[\'SessionSeminar\']' => 'Use class #{yellow:Context} instead', - - 'Statusgruppe(' => 'Removed(?). Use class #{yellow:Statusgruppen} instead.', -]; diff --git a/cli/compatibility-rules/studip-4.2.php b/cli/compatibility-rules/studip-4.2.php deleted file mode 100644 index 4420e9f710c3c1baba6e5f07beb746386ea93ad4..0000000000000000000000000000000000000000 --- a/cli/compatibility-rules/studip-4.2.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php -// "Rules"/definitions for critical changes in 4.2 -return [ - 'get_perm' => 'Use the #{yellow:CourseMember} or #{yellow:InstitutMember} model instead.', - 'get_vorname' => 'Use #{yellow:User::find($id)->vorname} instead', - 'get_nachname' => 'Use #{yellow:User::find($id)->nachname} instead', - 'get_range_tree_path' => false, - 'get_seminar_dozent' => 'Use #{yellow:Course::find($id)->getMembersWithStatus(\'dozent\')} instead.', - 'get_seminar_tutor' => 'Use #{yellow:Course::find($id)->getMembersWithStatus(\'tutor\')} instead.', - 'get_seminar_sem_tree_entries' => false, - 'get_seminars_users' => 'Use #{yellow:CourseMember::findByUser($user_id)} instead to aquire all courses.', - 'remove_magic_quotes' => false, - 'text_excerpt' => false, - 'check_group_new' => false, - 'insertNewSemester' => 'Use the #{yellow:Semester} model instead.', - 'updateExistingSemester' => 'Use the #{yellow:Semester} model instead.', -]; diff --git a/cli/compatibility-rules/studip-4.4.php b/cli/compatibility-rules/studip-4.4.php deleted file mode 100644 index 48e5165cc8e8f35b40ee94473c091e596770df20..0000000000000000000000000000000000000000 --- a/cli/compatibility-rules/studip-4.4.php +++ /dev/null @@ -1,6 +0,0 @@ -<?php -// "Rules"/definitions for critical changes in 4.4 -return [ - 'Token::is_valid' => 'Use #{yellow:Token::isValid($token, $user_id)} instead.', - 'Token::generate' => 'Use #{yellow:Token::create($duration = 30, $user_id = null)} instead.', -]; diff --git a/cli/compatibility-rules/studip-5.0.php b/cli/compatibility-rules/studip-5.0.php deleted file mode 100644 index af8a70e101b732d0c63ebc0dd56aeee29597de1e..0000000000000000000000000000000000000000 --- a/cli/compatibility-rules/studip-5.0.php +++ /dev/null @@ -1,60 +0,0 @@ -<?php -// "Rules"/definitions for critical changes in 5.0 -return [ - // https://develop.studip.de/trac/ticket/11250 - 'userMayAccessRange' => '#{yellow:Changed} - Use #{yellow:isAccessibleToUser} instead', - 'userMayEditRange' => '#{yellow:Changed} - Use #{yellow:isEditableByUser} instead', - 'userMayAdministerRange' => '#{red:Removed}', - - // UTF8-Encode/Decode legacy functions - 'studip_utf8encode' => '#{red:Removed} - Use utf8_encode().', - 'studip_utf8decode' => '#{red:Removed} - Use utf8_decode().', - - // JSON encode/decode legacy functions - 'studip_json_decode' => '#{red:Deprecated} - Use json_decode() and pay attention to the second parameter.', - 'studip_json_encode' => '#{red:Deprecated} - Use json_encode().', - - // https://develop.studip.de/trac/ticket/10806 - 'SemesterData' => '#{red:Removed} - Use #{yellow:Semester model} instead', - - // https://develop.studip.de/trac/ticket/10786 - 'StatusgroupsModel' => '#{red:Removed} - Use #{yellow:Statusgruppen model} instead', - - // https://develop.studip.de/trac/ticket/10796 - 'StudipNullCache' => '#{red:Removed} - Use #{yellow:StudipMemoryCache} instead', - - // https://develop.studip.de/trac/ticket/10838 - 'getDeputies' => '#{red:Removed} - Use #{yellow:Deputy::findDeputies()} instead', - 'getDeputyBosses' => '#{red:Removed} - Use #{yellow:Deputy::findDeputyBosses()} instead', - '/(?<!Deputy::)addDeputy/' => '#{red:Removed} - Use #{yellow:Deputy::addDeputy()} instead', - '/deleteDeputy(?=\()/' => '#{red:Removed} - Use #{yellow:Deputy model} instead', - 'deleteAllDeputies' => '#{red:Removed} - Use #{yellow:Deputy::deleteByRange_id} instead', - '/(?<!Deputy::)isDeputy/' => '#{red:Removed} - Use #{yellow:Deputy::isDeputy()} instead', - 'setDeputyHomepageRights' => '#{red:Removed} - Use #{yellow:Deputy model} instead', - 'getValidDeputyPerms' => '#{red:Removed} - Use #{yellow:Deputy::getValidPerms()} instead', - 'isDefaultDeputyActivated' => '#{red:Removed} - Use #{yellow:Deputy::isActivated()} instead', - 'getMyDeputySeminarsQuery' => '#{red:Removed} - Use #{yellow:Deputy::getMySeminarsQuery()} instead', - 'isDeputyEditAboutActivated' => '#{red:Removed} - Use #{yellow:Deputy::isEditActivated()} instead', - - // https://develop.studip.de/trac/ticket/10870 - 'get_config' => '#{red:Deprecated} - Use #{yellow:Config::get()} instead.', - - // https://develop.studip.de/trac/ticket/10919 - 'RESTAPI\\RouteMap' => '#{red:Deprecated} - Use the #{yellow:JSONAPI} instead.', - - // https://develop.studip.de/trac/ticket/10878 - 'Leafo\\ScssPhp' => 'Library was replaced by #{yellow:scssphp/scssphp}', - 'sfYamlParser' => 'Library was replaced by #{yellow:symfony/yaml}', - 'DocBlock::of' => 'Library was replaced by #{yellow:gossi/docblock}', - - 'vendor/idna_convert' => 'Remove include/require. Will be autoloaded.', - 'vendor/php-htmldiff' => 'Remove include/require. Will be autoloaded.', - 'vendor/HTMLPurifier' => 'Remove include/require. Will be autoloaded.', - 'vendor/phplot' => 'Remove include/require. Will be autoloaded.', - 'vendor/phpCAS' => 'Remove include/require. Will be autoloaded.', - 'vendor/phpxmlrpc' => 'Remove include/require. Will be autoloaded.', - - // https://develop.studip.de/trac/ticket/10964 - 'periodicalPushData' => '#{red:Removed} - Use #{yellow:STUDIP.JSUpdater.register()} instead', - '/UpdateInformtion::setInformation\(.+\..+\)/' => '#{red:Removed} - Use #{yellow:STUDIP.JSUpdater.register()} instead', -]; diff --git a/cli/create_table_schemes.php b/cli/create_table_schemes.php deleted file mode 100755 index a7f557eabc1dcd9bae93e5afd8ee99613140260b..0000000000000000000000000000000000000000 --- a/cli/create_table_schemes.php +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env php -<?php -# Lifter007: TODO -# Lifter003: TODO -/** -* create_table_schemes.php -* -* -* -* -* @author André Noack <noack@data-quest.de>, Suchi & Berg GmbH <info@data-quest.de> -* @access public -*/ -// +---------------------------------------------------------------------------+ -// This file is part of Stud.IP -// create_table_schemes.php -// -// Copyright (C) 2006 André Noack <noack@data-quest.de>, -// Suchi & Berg GmbH <info@data-quest.de> -// +---------------------------------------------------------------------------+ -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or any later version. -// +---------------------------------------------------------------------------+ -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -// +---------------------------------------------------------------------------+ -require_once dirname(__FILE__) . '/studip_cli_env.inc.php'; -exec("grep -l 'extends SimpleORMap' $STUDIP_BASE_PATH/lib/classes/*.class.php", $output, $ok); -if(!$ok ){ - fwrite(STDOUT, "<?php\n//copy to \$STUDIP_BASE_PATH/lib/dbviews/table_schemes.inc.php\n//generated ". date('r') ."\n"); - foreach($output as $line){ - require_once $line; - list($classname,,) = explode('.',basename($line)); - $o = new $classname(); - fwrite(STDOUT, $o->exportScheme()); - } - fwrite(STDOUT, "?>"); -} - -?> \ No newline at end of file diff --git a/cli/cronjob-worker.php b/cli/cronjob-worker.php deleted file mode 100755 index 15f6e2ed04d7fc8a12867ea80472c6437e5e9b82..0000000000000000000000000000000000000000 --- a/cli/cronjob-worker.php +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env php -<?php -/** - * cronjob-worker - Worker process for the cronjobs - * - * @author Jan-Hendrik Willms <tleilax+studip@gmail.com> - * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 - * @category Stud.IP - * @since 2.4 - */ - -// +---------------------------------------------------------------------------+ -// This file is part of Stud.IP -// cronjob-worker.php -// -// Copyright (C) 2013 Jan-Hendrik Willms <tleilax+studip@gmail.com> -// +---------------------------------------------------------------------------+ -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or any later version. -// +---------------------------------------------------------------------------+ -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -// +---------------------------------------------------------------------------+ - - require_once 'studip_cli_env.inc.php'; - - CronjobScheduler::getInstance()->run(); \ No newline at end of file diff --git a/cli/cronjobs.php b/cli/cronjobs.php deleted file mode 100755 index 217c6643197386e75a6050e7aff7765103ca76bd..0000000000000000000000000000000000000000 --- a/cli/cronjobs.php +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env php -<?php -/** -* cronjobs - Helper script for the cronjobs -* -* @author Jan-Hendrik Willms <tleilax+studip@gmail.com> -* @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 -* @category Stud.IP -* @since 3.1 -* @todo Parameter handling! -*/ - -require_once 'studip_cli_env.inc.php'; - -$argc = $_SERVER['argc']; -$argv = $_SERVER['argv']; - -$opts = getopt('hl', ['help', 'list']); - -if (isset($opts['l']) || isset($opts['list'])) { - $tasks = CronjobTask::findBySql('1'); - foreach ($tasks as $task) { - $description = call_user_func([$task->class, 'getDescription']); - fwrite(STDOUT, sprintf('%s %s' . PHP_EOL, $task->id, $description)); - } - exit(0); -} - -if ($argc < 2 || isset($opts['h']) || isset($opts['help'])) { - fwrite(STDOUT,'Usage: ' . basename(__FILE__) . ' [--help] [--list] <task_id> [last_result]' . PHP_EOL); - exit(0); -} - - -$id = $_SERVER['argv'][1]; -$last_result = $argc > 2 ? $_SERVER['argv'][2] : null; -$task = CronjobTask::find($id); -if (!$task) { - fwrite(STDOUT, 'Unknown task id' . PHP_EOL); - exit(0); -} - -if (!file_exists($GLOBALS['STUDIP_BASE_PATH'] . '/' . $task->filename)) { - fwrite(STDOUT, 'Invalid task, unknown filename "' . $task->filename . '"' . PHP_EOL); - exit(0); -} - -require_once $task->filename; -if (!class_exists($task->class)) { - fwrite(STDOUT, 'Invalid task, unknown class "' . $task->class . '"' . PHP_EOL); - exit(0); -} - -$task->engage($last_result); diff --git a/cli/describe_models.php b/cli/describe_models.php deleted file mode 100755 index ebe0db396bd5e14cddb7c166dbf52cc63e0806ef..0000000000000000000000000000000000000000 --- a/cli/describe_models.php +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env php -<?php -require_once 'studip_cli_env.inc.php'; - -$dir = new FilesystemIterator($STUDIP_BASE_PATH . '/lib/models'); -foreach ($dir as $fileinfo) { - $class = mb_strstr($fileinfo->getFilename(), '.', true); - if (!in_array($class, words('SimpleCollection SimpleORMap SimpleORMapCollection StudipArrayObject')) && class_exists($class)) { - echo $class . "\n"; - $model = new $class; - $meta = $model->getTableMetaData(); - $props = []; - foreach ($meta['fields'] as $field => $info) { - $name = mb_strtolower($field); - $props[$name] = '@property string ' . $name; - $props[$name] .= ' database column'; - if ($alias = array_search($name, $meta['alias_fields'])) { - $props[$alias] = '@property string ' . $alias; - $props[$alias] .= ' alias column for ' . $name; - } - } - foreach ($meta['additional_fields'] as $field => $info) { - $name = mb_strtolower($field); - $props[$name] = '@property string ' . $name; - $props[$name] .= ' computed column'; - $getter = isset($info['get']) || method_exists($model, 'get' . $name); - $setter = isset($info['set']) || method_exists($model, 'set' . $name); - - if ($setter && $getter) { - $props[$name] .= ' read/write'; - } else if ($setter) { - $props[$name] .= ' read only'; - } - } - foreach ($meta['relations'] as $relation) { - $options = $model->getRelationOptions($relation); - $props[$relation] = '@property '; - if ($options['type'] === 'has_many' || - $options['type'] === 'has_and_belongs_to_many') { - $props[$relation] .= 'SimpleORMapCollection'; - } else { - $props[$relation] .= $options['class_name']; - } - $props[$relation] .= ' ' . $relation; - $props[$relation] .= ' ' . $options['type'] . ' ' . $options['class_name']; - } - $props = array_map(function($p) {return ' * ' . $p . "\n";}, $props); - $file = file($fileinfo->getPathname()); - foreach ($file as $n => $line) if (mb_strpos($line, 'class') === 0) break; - if ($n < count($file)) { - $classstart = $n; - $propend = null; - $propstart = null; - $docend = null; - for ($n; $n >= 0; --$n) { - if (!isset($docend) && mb_strpos($file[$n], ' */') === 0) $docend = $n; - if (!isset($propend) && mb_strpos($file[$n], ' * @property') === 0) $propend = $n; - if (isset($propend) && mb_strpos($file[$n], ' * @property') === 0) $propstart = $n; - } - if (isset($docend)) { - if (isset($propstart)) { - array_splice($file, $propstart, $propend-$propstart+1, $props); - } else { - array_splice($file, $docend, 0, $props); - } - $ok = file_put_contents($fileinfo->getPathname(), join('', array_map(function($l) {return rtrim($l, "\r\n") . PHP_EOL;}, $file))); - if ($ok) echo $fileinfo->getPathname() . " written \n"; - else echo $fileinfo->getPathname() . " not writable \n"; - } else { - echo 'no docblock found in ' . $fileinfo->getPathname() . chr(10); - } - - } - } -} diff --git a/cli/dump_studip.php b/cli/dump_studip.php deleted file mode 100755 index 52ce0c1d33e93d068d58ce146b17c5806ad7241c..0000000000000000000000000000000000000000 --- a/cli/dump_studip.php +++ /dev/null @@ -1,87 +0,0 @@ -#!/usr/bin/env php -<?php -/** -* dump_studip.php -* -* -* -* -* @author André Noack <noack@data-quest.de>, Suchi & Berg GmbH <info@data-quest.de> -* @access public -*/ -// +---------------------------------------------------------------------------+ -// This file is part of Stud.IP -// dump_studip.php -// -// Copyright (C) 2011 André Noack <noack@data-quest.de> -// +---------------------------------------------------------------------------+ -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or any later version. -// +---------------------------------------------------------------------------+ -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -// +---------------------------------------------------------------------------+ -require_once 'studip_cli_env.inc.php'; - -function exec_or_die($cmd) { - exec($cmd . ' 2>&1',$output,$ok); - if ($ok > 0) { - fwrite(STDOUT,join("\n", array_merge([$cmd], $output)) . "\n"); - exit(1); - } -} - -$dump_dir = $_SERVER['argv'][1] ? realpath($_SERVER['argv'][1]) : null; -$dump_only = $_SERVER['argv'][2]; - -if (!$dump_dir) { - fwrite(STDOUT,'Usage: ' . basename(__FILE__) . ' PATH [db|base|data]' .chr(10).'Dump all without second parameter.'.chr(10)); - exit(0); -} -if (!is_writeable($dump_dir)) { - trigger_error('Directory: ' . $dump_dir . ' is not writeable!', E_USER_ERROR); -} - -$today = date("Ymd"); -$prefix = Config::get()->STUDIP_INSTALLATION_ID ? Config::get()->STUDIP_INSTALLATION_ID : 'studip'; -if (!$dump_only || $dump_only == 'db') { - $dump_db_dir = $dump_dir . '/db-' . $today; - if (!is_dir($dump_db_dir)) { - mkdir($dump_db_dir); - } - foreach(DBManager::get()->query("SHOW TABLES") as $tables) { - $table = $tables[0]; - $dump_table = $dump_db_dir . '/' . $table . '-' . $today . '.sql'; - fwrite(STDOUT, 'Dumping database table ' . $table . chr(10)); - exec_or_die("mysqldump -u$DB_STUDIP_USER -h$DB_STUDIP_HOST -p$DB_STUDIP_PASSWORD $DB_STUDIP_DATABASE $table > $dump_table"); - } - $dump_db = $dump_dir . '/' . $prefix . '-DB-' . $today . '.tar.gz'; - fwrite(STDOUT, 'Packing database to ' . $dump_db . chr(10)); - exec_or_die("cd $dump_db_dir && tar -czf $dump_db *"); - exec_or_die("rm -rf $dump_db_dir"); -} -if (!$dump_only || $dump_only == 'base') { - $dumb_studip = $dump_dir . '/' . $prefix . '-BASE-' . $today . '.tar.gz'; - $base_path = realpath($STUDIP_BASE_PATH); - if (!$base_path) { - trigger_error('Stud.IP directory not found!', E_USER_ERROR); - } - fwrite(STDOUT, 'Dumping Stud.IP directory to ' . $dumb_studip . chr(10)); - exec_or_die("cd $base_path && tar -czf $dumb_studip --exclude 'data/*' ."); -} -if (!$dump_only || $dump_only == 'data') { - $data_path = realpath($UPLOAD_PATH . '/../'); - if ($data_path) { - $dumb_data = $dump_dir . '/' . $prefix . '-DATA-' . $today . '.tar.gz'; - fwrite(STDOUT, 'Dumping data directory to ' . $dumb_data . chr(10)); - exec_or_die("cd $data_path && tar -czf $dumb_data ."); - } -} -exit(0); diff --git a/cli/extract-js-localizations.php b/cli/extract-js-localizations.php deleted file mode 100755 index 70d14a9a0417245f762d0c941b3ef13ca8cbe8cc..0000000000000000000000000000000000000000 --- a/cli/extract-js-localizations.php +++ /dev/null @@ -1,204 +0,0 @@ -#!/usr/bin/env php -<?php -/** - * extract-js-localizations.php - * - * Exports all strings from js into app/views/localizations/show.php so - * they can be translated as well. - * - * @author Jan-Hendrik Willms <tleilax+studip@gmail.com> - * @license GPL2 or any later version - * @copyright Stud.IP Core Group - * @since 3.1 - */ - -require 'studip_cli_env.inc.php'; - -/** - * Determines whether the file should be skipped depending on an exclude list - * with an additional include list. This allows inclusion inside of previously - * excluded entries. We need this for the assets directory. - * Furthermore, the file is checked against a list of mime types to include. - * - * @param String $filename Adjusted filename (stripped to path inside trunk) - * @param String $realfile Actual file name (needed for mime type detection) - * @return bool indicating whether the file should be skipped or not - */ -function should_skip_file($filename, $realfile) { - $exclude = [ - 'cli/*', - 'composer/*', - 'config/*', - 'data/*', - 'db/*', - 'doc/*', - 'locale/*', - 'node_modules/*', - 'public/assets/flash*', - 'public/assets/fonts*', - 'public/assets/images*', - 'public/assets/javascripts/*', - 'public/assets/sounds*', - 'public/assets/squeezed*', - 'public/assets/stylesheets*', - 'public/pictures/*', - 'public/plugins_packages/*', - 'test/*', - 'tests/*', - 'vendor/*', - ]; - $include = [ - 'public/assets/javascripts/ckeditor*', - 'public/plugins_packages/core*', - ]; - $mime_types = [ - 'text/*', - 'application/javascript', - ]; - - // Check if the file should be excluded, depending on it's path. - $matching_pattern = null; - $skip = false; - foreach ($exclude as $pattern) { - if (fnmatch($pattern, $filename)) { - $matching_pattern = $pattern; - $skip = true; - break; - } - } - - // If it should be skipped in step 1, check if it matches the include - // patterns and no longer skip it, if it matches. - // Matches are only from patterns that are longer than the pattern that - // set the entry to be skipped. Thus it is detected if the file is in a - // subdirectory. - if ($skip) { - foreach ($include as $pattern) { - if (fnmatch($pattern, $filename) && mb_strlen($pattern) > mb_strlen($matching_pattern)) { - $skip = false; - break; - } - } - } - - // If the file should not be skipped, check it's mime type and skip it - // if the mime type is not allowed. - if (!$skip && is_file($realfile)) { - $mime_type = mime_content_type($realfile); - - $skip = true; - foreach ($mime_types as $pattern) { - if (fnmatch($pattern, $mime_type)) { - $skip = false; - break; - } - } - } - - return $skip; -} - -/** - * Extract the actual text strings from a file. This will only detect single - * line text strings. Multi line strings are just a hassle to handle in js - * anyways. - * - * @param String $file Filename to extract text strings from - * @return mixed Array with found text strings or false if no text strings - * were found - */ -function extract_strings($file) { - $contents = file_get_contents($file); - $regexp = '/(?:\'([^\']+)\'|"([^"]+)")\\.toLocaleString\\(\\s*\\)/'; - - if (preg_match_all($regexp, $contents, $matches, PREG_SET_ORDER)) { - $result = []; - foreach ($matches as $match) { - $result[] = $match[1] ?: $match[2]; - } - return array_unique($result); - } - - return false; -} - -/** - * Recursively find text strings in files in the given directory. - * This skips invalid files. - * - * @param String $directory Directory to search files in - * @param mixed $base Optional base directory to strip from file names, - * will default to the initial passed directory. - * @return Array Associative array with filenames as index and an array of - * the text strings the file contains. - */ -function find_strings_in_dir($directory, $base = null) { - $result = []; - - $base = rtrim($base ?: $directory, '/') . '/'; - - $files = glob(rtrim($directory, '/') . '/*'); - foreach ($files as $file) { - $filename = str_replace($base, '', $file); - $is_dir = is_dir($file); - - if (should_skip_file($filename, $file)) { - continue; - } - - if (is_dir($file)) { - $result += find_strings_in_dir($file, $base); - } elseif ($strings = extract_strings($file)) { - $result[$filename] = $strings; - } - } - - return $result; -} - -// Find text strings in all stud.ip files -$occurences = find_strings_in_dir(realpath(__DIR__ . '/..')); - -// Remove duplicates -$hashes = []; -foreach ($occurences as $file => $strings) { - foreach ($strings as $index => $string) { - $hash = md5($string); - if (in_array($hash, $hashes)) { - unset($strings[$index]); - } else { - $hashes[] = $hash; - } - } - if (empty($strings)) { - unset($occurences[$file]); - } else { - $occurences[$file] = $strings; - } -} - -// Create trails view as output -ob_start(); -?> -<?= '<?php' . PHP_EOL ?> - -$translations = array( -<? foreach ($occurences as $file => $strings): ?> - // <?= $file . PHP_EOL ?> -<? foreach ($strings as $string): ?> - '<?= addcslashes($string, "'") ?>' => _('<?= addcslashes($string, "'") ?>'), -<? endforeach; ?> - -<? endforeach; ?> -); - -?> -<?= '<?=' ?> json_encode($translations) <?= '?>' ?> -<? -$view = ob_get_clean(); - -// Write output to the corresponding file -file_put_contents(__DIR__ . '/../app/views/localizations/show.php', $view); - -// Show some statistics -printf('%u strings written to file' . PHP_EOL, array_sum(array_map('count', $occurences))); diff --git a/cli/fix-icon-dimensions.php b/cli/fix-icon-dimensions.php deleted file mode 100755 index a7e595afe01139661f1794bbfb2dc046afee8cb3..0000000000000000000000000000000000000000 --- a/cli/fix-icon-dimensions.php +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env php -<?php -require_once __DIR__ . '/studip_cli_env.inc.php'; - -$folder = $GLOBALS['STUDIP_BASE_PATH'] . '/public/assets/images/icons'; -$iterator = new RecursiveDirectoryIterator($folder, FilesystemIterator::FOLLOW_SYMLINKS | FilesystemIterator::UNIX_PATHS); -$iterator = new RecursiveIteratorIterator($iterator); -$regexp_iterator = new RegexIterator($iterator, '/\.svg$/', RecursiveRegexIterator::MATCH); - -foreach ($regexp_iterator as $file) { - $contents = file_get_contents($file); - - $xml = simplexml_load_string($contents); - $attr = $xml->attributes(); - if ($attr->width && $attr->height) { - continue; - } - $contents = str_replace('<svg ', '<svg width="16" height="16" ', $contents); - file_put_contents($file, $contents); - - echo "Adjusted $file\n"; -} diff --git a/cli/fix_collate.php b/cli/fix_collate.php deleted file mode 100755 index b9b9f6737f2acc378da095cc0540c48c37ee8721..0000000000000000000000000000000000000000 --- a/cli/fix_collate.php +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env php -<?php -/** - * @author Witali Mik <mik@data-quest.de> - * Script um Collation Konflikte automatisiert zu lösen - */ - -require_once dirname(__FILE__) . '/studip_cli_env.inc.php'; -require_once 'lib/classes/DBManager.class.php'; -require_once 'config/config_local.inc.php'; - -$charset = 'latin1'; -$collate = 'latin1_german1_ci'; -$sql = "SELECT CONCAT('ALTER TABLE `".$DB_STUDIP_DATABASE."`.`', TABLE_NAME, '` CONVERT TO CHARACTER SET ".$charset." COLLATE ".$collate.";') as query FROM `information_schema`.TABLES WHERE TABLE_SCHEMA='".$DB_STUDIP_DATABASE."' AND TABLE_COLLATION!='".$collate."'"; - -$db = DBManager::get(); - - -$result = $db->query($sql); -foreach($result->fetchAll(PDO::FETCH_OBJ) as $row){ - $db->exec($row->query); - fwrite(STDOUT, sprintf("Execute: %s \n",$row->query)); -} -fwrite(STDOUT, "Finished"); \ No newline at end of file diff --git a/cli/fix_endtime_weekly_recurred_events.php b/cli/fix_endtime_weekly_recurred_events.php deleted file mode 100755 index 7e31f2932f7c96d6b501ab7d44dc78f13b0e1ece..0000000000000000000000000000000000000000 --- a/cli/fix_endtime_weekly_recurred_events.php +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env php -<?php -/** - * fix_endtime_weekly_recurred_events.php - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * @author Peter Thienel <thienel@data-quest.de> - * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 - * @category Stud.IP - * @since 3.5 - */ -require_once __DIR__ . '/studip_cli_env.inc.php'; - -$events = EventData::findBySQL("rtype = 'WEEKLY' AND IFNULL(count, 0) > 0"); -$cal_event = new CalendarEvent(); -$i = 0; -foreach ($events as $event) { - $id = $event->getId(); - $cal_event->event = $event; - $rrule = $cal_event->getRecurrence(); - $cal_event->setRecurrence($rrule); - $event->expire = $cal_event->event->expire; - $event->setId($id); - $event->store(); - $i++; -} - -fwrite(STDOUT, 'Wrong end time of recurrence fixed for ' . $i . ' events.' . chr(10)); -exit(1); diff --git a/cli/getopts.php b/cli/getopts.php deleted file mode 100644 index dde439f8e0191a1f509c5f4c93548f30c0c19a94..0000000000000000000000000000000000000000 --- a/cli/getopts.php +++ /dev/null @@ -1,317 +0,0 @@ -<?php - - /* - - getopts by ALeX Kazik - - Code: https://github.com/alexkazik/getopts - Docs: https://github.com/alexkazik/getopts/wiki/Documentation - Homepage: http://alex.kazik.de/195/getopts/ - - License: Creative Commons Attribution 3.0 Unported License - http://creativecommons.org/licenses/by/3.0/ - - */ - - function getopts($params, $args=NULL, $raw=false){ - // check input - if(!is_array($params)){ - trigger_error('Invalid params table', E_USER_ERROR); - } - if($args === NULL && is_array($_SERVER['argv'])){ - $args = $_SERVER['argv']; - array_shift($args); - } - if(!is_array($args)){ - trigger_error('Invalid args table', E_USER_ERROR); - } - if(!is_bool($raw)){ - trigger_error('Invalid raw option', E_USER_ERROR); - } - - // mb_substr, which returns '' in case of an empty mb_substr (usually false) - $mb_substr = function ($string, $start, $length = null) { // is not used, only for definition - $ret = call_user_func_array('mb_substr', func_get_args()); - if ($ret === false){ - return ''; - } else { - return $ret; - } - }; - - // get arg (either implicit or the following) - $get_arg = function (&$next, &$args, &$num) { // pass by reference: num may be changed, others: performance - if ($next !== true) { - return $next; - } elseif ($num + 1 >= count($args)){ - return false; - } else { - $num += 1; - return $args[$num]; - } - }; - - // all types & subtypes - $types_subtypes = ['S' => 'stcr', 'V' => 'smar', 'O' => 'smar', 'A' => 'sr']; - - // output - $Ores = []; - $Oerr = []; - $Oags = []; - - // parsed options - $short = []; - $long = []; - $type = []; - - // parse options - foreach($params AS $opt => $names){ - if(is_string($names)){ - $names = preg_split('/ +/', $names); - } - if(!is_array($names) || count($names) < 2){ - trigger_error('Invalid type/name(s) to param "'.$opt.'"', E_USER_ERROR); - } - - $ty = array_shift($names); - if(!is_string($ty) || mb_strlen($ty) < 1 || mb_strlen($ty) > 2){ - trigger_error('Invalid type to param "'.$opt.'"', E_USER_ERROR); - } - $ty0 = $ty[0]; - if(!isset($types_subtypes[$ty0])){ - trigger_error('Invalid type to param "'.$opt.'"', E_USER_ERROR); - } - if(mb_strlen($ty) == 1){ - $ty1 = $types_subtypes[$ty0][0]; - }else{ - $ty1 = $ty[1]; - if(mb_strpos($types_subtypes[$ty0], $ty1) === false){ - trigger_error('Invalid type to param "'.$opt.'"', E_USER_ERROR); - } - } - $type[$opt] = $ty0.$ty1; - - foreach($names AS $name){ - if(!is_string($name)){ - trigger_error('Invalid names to param "'.$opt.'"', E_USER_ERROR); - } - if(!preg_match('!^(-)?([0-9a-zA-Z]+)$!', $name, $r)){ - trigger_error('Invalid name to param "'.$opt.'"', E_USER_ERROR); - } - if($r[1] == '-' || mb_strlen($r[2]) > 1){ - if(isset($long[$r[2]])){ - trigger_error('Duplicate option name "'.$r[2].'"', E_USER_ERROR); - } - $long[$r[2]] = $opt; - }else{ - if(isset($short[$r[2]])){ - trigger_error('Duplicate option name "'.$r[2].'"', E_USER_ERROR); - } - $short[$r[2]] = $opt; - } - } - - $Ores[$opt] = []; - } - - // parse arguments - for($num=0; $num<count($args); $num++){ - $arg = $args[$num]; - - if($arg == '--'){ - // end of options, copy all other args - $num++; - for(; $num<count($args); $num++){ - $Oags[] = $args[$num]; - } - break; - }else if($arg == ''){ - // empty -> skip - continue; - }else if($arg[0] != '-'){ - // not an option -> copy to args - $Oags[] = $arg; - continue; - } - - // this arg is an option! - if($arg[1] == '-'){ - // long option - $p = mb_strpos($arg, '='); - if($p !== false){ - $next = $mb_substr($arg, $p+1); - $arg = mb_substr($arg, 2, $p-2); - }else{ - $next = true; - $arg = mb_substr($arg, 2); - } - if(!isset($long[$arg])){ - $Oerr[] = 'Unknown option "--'.$arg.'"'; - }else{ - $opt = $long[$arg]; - $Earg = '--'.$arg; - switch($type[$opt][0]){ - case 'S': - $Ores[$opt][] = $next; - break; - case 'V': - if(($val = $get_arg($next,$args,$num)) === false){ - $Oerr[] = 'Missing artument to option "'.$Earg.'"'; - }else{ - $Ores[$opt][] = $val; - } - break; - case 'O': - $Ores[$opt][] = $next; - break; - case 'A': - if(($val = $get_arg($next,$args,$num)) === false){ - $Oerr[] = 'Missing artument to option "'.$Earg.'"'; - }else{ - $p = mb_strpos($val, '='); - if($p === false){ - $Oerr[] = 'Malformed artument to option "'.$Earg.'" (a "=" is missing)'; - }else if(isset($Ores[$opt][mb_substr($val, 0, $p)])){ - $Oerr[] = 'Duplicate key "'.mb_substr($val, 0, $p).'" to option "'.$Earg.'"'; - }else{ - $Ores[$opt][mb_substr($val, 0, $p)] = $mb_substr($val, $p+1); - } - } - break; - } - } - }else{ - // short option(s) - for($i=1; $i<mb_strlen($arg); $i++){ - $c = $arg[$i]; - $next = $mb_substr($arg, $i+1); - if($next == ''){ - $next = true; - }else if($next[0] == '='){ - $next = $mb_substr($next, 1); - } - if(!isset($short[$c])){ - $Oerr[] = 'Unknown option "-'.$c.'"'; - $i = mb_strlen($arg); - }else{ - $opt = $short[$c]; - $Earg = '-'.$c; - switch($type[$opt][0]){ - case 'S': - $Ores[$opt][] = true; - break; - case 'V': - if(($val = $get_arg($next,$args,$num)) === false){ - $Oerr[] = 'Missing artument to option "'.$Earg.'"'; - }else{ - $Ores[$opt][] = $val; - } - $i = mb_strlen($arg); - break; - case 'O': - $Ores[$opt][] = $next; - $i = mb_strlen($arg); - break; - case 'A': - if(($val = $get_arg($next,$args,$num)) === false){ - $Oerr[] = 'Missing artument to option "'.$Earg.'"'; - }else{ - $p = mb_strpos($val, '='); - if($p === false){ - $Oerr[] = 'Malformed artument to option "'.$Earg.'" (a "=" is missing)'; - }else if(isset($Ores[$opt][mb_substr($val, 0, $p)])){ - $Oerr[] = 'Duplicate key "'.mb_substr($val, 0, $p).'" to option "'.$Earg.'"'; - }else{ - $Ores[$opt][mb_substr($val, 0, $p)] = $mb_substr($val, $p+1); - } - } - $i = mb_strlen($arg); - break; - } - } - } - } - } - - // reformat result - if(!$raw){ - foreach($Ores AS $opt => &$r){ - switch($type[$opt]){ - case 'Ss': - $r = count($r) > 0; - break; - case 'St': - $r = (count($r) & 1) == 1; - break; - case 'Sc': - $r = count($r); - break; - - case 'Vs': - if(count($r) == 0){ - // no option - $r = false; - }else{ - // pick last entry - $r = array_pop($r); - } - break; - - case 'Os': - if(count($r) == 0){ - // no option - $r = false; - }else{ - // pick last entry; if possible last used (non true) entry - do{ - $rr = array_pop($r); - }while($rr === true && count($r) > 0); - $r = $rr; - } - break; - - case 'Vm': - case 'Om': - if(count($r) == 0){ - // no option - $r = false; - }else{ - // as array - // (already done) - } - break; - - case 'Va': - case 'Oa': - // false if none, direct (string) if only one, array otherwise - if(count($r) == 0){ - // no option - $r = false; - }else if(count($r) == 1){ - // a single option - $r = array_pop($r); - }else{ - // as array - // (already done) - } - break; - - case 'As': - // as array - // (already done) - break; - - } - } - } - - // errors? - if(count($Oerr) == 0){ - $Oerr = false; - } - - // result - return [$Oerr, $Ores, $Oags]; - } - -?> diff --git a/cli/help-translation-tool.php b/cli/help-translation-tool.php deleted file mode 100755 index 6323eae69c97eee7577544eb3a21dbd79250ce21..0000000000000000000000000000000000000000 --- a/cli/help-translation-tool.php +++ /dev/null @@ -1,543 +0,0 @@ -#!/usr/bin/env php -<?php -/** - * help-translation-tool.php - * - * Exports db data for the help content, tooltips and tours into a .po file or - * reimports the translated strings into the db. - * - * Since we need to obtain the row to inssert/update the translated content, - * this information is coded into the corresponding filename and line number. - * - * By using a specific range for line number, we can determine what type the - * translated string is: - * - * range | context | location | file | line number - * --------------+------------+------------------------+-------+------------- - * 10000 - 19999 | - | help_content.label | route | position - * 20000 - 29999 | content_id | help_content.content | route | position - * 30000 - 39999 | tour_id | help_tours.name | - | version - * 40000 - 49999 | tour_id | help_tours.description | - | version - * 50000 - 59999 | tour_id | help_tour_steps.title | route | step - * 60000 - 69999 | tour_id | help_tour_steps.tip | route | step - * 70000 - 79999 | tooltip_id | help_tooltips.content | route | version - * - * @author Jan-Hendrik Willms <tleilax+studip@gmail.com> - * @license GPL2 or any later version - * @copyright Stud.IP Core Group - * @since 3.1 - */ - -require_once 'studip_cli_env.inc.php'; - -define('MAX_LINE_LENGTH', 60); - -/** - * Escapes a string for use in .po file. - * - * @param String $string String to escape - * @return String Escaped string - */ -function po_escape($string) { - return str_replace('"', '\\"', $string); -} - -/** - * Unescapes a string for use in .po file. - * - * @param String $string String to unescape - * @return String Unescaped string - */ -function po_unescape($string) { - $replaces = [ - '\\"' => '"', - '\\n' => "\n", - ]; - $string = str_replace(array_keys($replaces), array_values($replaces), $string); - return $string; -} - -/** - * Prepares a string for use in .po file. - * - * @param String $string String to use in .po file - * @return String Processed string - */ -function po_stringify($string) { - $string = str_replace("\r", '', $string); - $chunks = explode("\n", $string); - - if (count($chunks) === 1 && mb_strlen($chunks[0]) < MAX_LINE_LENGTH) { - return '"' . po_escape($chunks[0]) . '"'; - } - - $result = '""' . "\n"; - foreach ($chunks as $index => $chunk) { - $chunk = wordwrap($chunk, MAX_LINE_LENGTH); - $parts = explode("\n", $chunk); - foreach ($parts as $idx => $line) { - $current_last = $idx === count($parts) - 1; - $last = ($current_last && $index === count($chunks) - 1); - - $result .= '"' . po_escape($line) . ($last ? '' : ($current_last ? '\\n' : ' ')) . '"'. "\n"; - } - } - return rtrim($result, "\n"); -} - -/** - * Returns the id for a help entitiy based on the given index and other - * credentials. This function also copies existing data and settings if - * the entity in the given language is newly created. - * - * @param String $version Stud.IP version to use for the new entry - * @param String $language Language to use for the new entry - * @param Array $message Complete message item from parsed .po file - * @param String $route Associated route (if any) - * @param int $index Type index for the entity - * @param int $position Position/version of the entity - * @return String Id of the entity - */ -function get_id($version, $language, $message, $route, $index, $position) { - static $ids = []; - - if ($index < 3) { - // Entity is help content - $hash = md5('content#' . join('#', compact(words('temp version language route position')))); - } elseif ($index < 7) { - // Entity is help tour content - $hash = md5('tour#' . $message['context'] . '#' . join('#' , compact(words('version language')))); - } elseif ($index == 7) { - // Entity is help tooltip - $hash = md5('tooltip#' . $message['context'] . '#' . join(words('position language'))); - } else { - throw new RuntimeException('Unknown index "' . $index . '"'); - } - - // If id has not yet been generated - if (!isset($ids[$hash])) { - if ($index < 3) { - // Help content - - // Try to get content id by primary key - $query = "SELECT content_id - FROM help_content - WHERE route = :route AND studip_version = :version - AND language = :language AND position = :position - AND custom = 0"; - $statement = DBManager::get()->prepare($query); - $statement->bindValue(':route', $route); - $statement->bindValue(':version', $version); - $statement->bindValue(':language', $language); - $statement->bindValue(':position', $position); - $statement->execute(); - - // Use found id or generate new one - $id = $statement->fetchColumn() ?: md5(uniqid('help_content', true)); - $ids[$hash] = $id; - } elseif ($index < 7) { - // Help tour - - // Is there any previous generated content? - // We have to use the hash generated above as the new id since - // there is no other way to exactly identify an already created - // entity for the given language and version - $query = "SELECT tour_id - FROM help_tours - WHERE tour_id = :tour_id"; - $statement = DBManager::get()->prepare($query); - $statement->bindValue(':tour_id', $hash); - $statement->execute(); - - $id = $statement->fetchColumn(); - if (!$id) { - // If no previous generated content is available, prepare - // database for new content - $id = $hash; - - // Copy settings from tour - $query = "INSERT INTO help_tours - SELECT :id AS tour_id, '' AS name, '' AS description, - type, roles, version, :language AS language, - :version AS studip_version, installation_id, - UNIX_TIMESTAMP() AS mkdate - FROM help_tours - WHERE tour_id = :tour_id"; - $statement = DBManager::get()->prepare($query); - $statement->bindValue(':id', $id); - $statement->bindValue(':language', $language); - $statement->bindValue(':version', $version); - $statement->bindValue(':tour_id', $message['context']); - $statement->execute(); - - // Copy individual steps - $query = "INSERT INTO help_tour_steps - SELECT :id AS tour_id, step, '' AS title, '' AS tip, - orientation, interactive, css_selector, route, - author_id, UNIX_TIMESTAMP() AS mkdate - FROM help_tour_steps - WHERE tour_id = :tour_id"; - $statement = DBManager::get()->prepare($query); - $statement->bindValue(':id', $id); - $statement->bindValue(':tour_id', $message['context']); - $statement->execute(); - - // Copy tour audiences - $query = "INSERT INTO help_tour_audiences - SELECT :id AS tour_id, range_id, type - FROM help_tour_audiences - WHERE tour_id = :tour_id"; - $statement = DBManager::get()->prepare($query); - $statement->bindValue(':id', $id); - $statement->bindValue(':tour_id', $message['context']); - $statement->execute(); - - // Copy tour settings - $query = "INSERT INTO help_tour_settings - SELECT :id AS tour_id, active, access - FROM help_tour_settings - WHERE tour_id = :tour_id"; - $statement = DBManager::get()->prepare($query); - $statement->bindValue(':id', $id); - $statement->bindValue(':tour_id', $message['context']); - $statement->execute(); - } - $ids[$hash] = $id; - } elseif ($index == 7) { - // Help tooltip - - // Nothing needs to be done, just copy the tooltip id - // (This is the only table that has the id and version/language - // info as primary key) - $ids[$hash] = $message['context']; - } - } - - // Return id from cache - return $ids[$hash]; -} - -// Error message: Not via cli or invalid parameters -if (!isset($_SERVER['argv'], $_SERVER['argc']) || $_SERVER['argc'] < 2) { - print 'Usage: ' . (@$_SERVER['argv'][0] ?: basename(__FILE__)) . ' [--version] [--language] [--force] <import|export> [file]' . "\n"; - die(1); -} - -// Parse command line options -$opts = [ - 'short' => 'v:l:f', - 'long' => [ - 'force', - 'version:', - 'language:' - ] -]; -$options = getopt($opts['short'], $opts['long']); -$force = isset($options['f']) || isset($options['force']); -$version = @$options['version'] ?: @$options['v'] - ?: DBManager::get()->query("SELECT MAX(studip_version) FROM help_content LIMIT 1")->fetchColumn() - ?: $GLOBALS['SOFTWARE_VERSION']; -$language = @$options['language'] ?: @$options['l'] ?: mb_substr(Config::get()->DEFAULT_LANGUAGE, 0, 2); - -// Remove option from arguments -$remove = []; -foreach (str_split($opts['short']) as $opt) { - if ($opt !== ':') { - $remove[] = '-' . $opt; - } -} -foreach ($opts['long'] as $opt) { - $remove[] = '--' . rtrim($opt, ':'); -} -$_SERVER['argv'] = array_values(array_diff($_SERVER['argv'], $remove)); - -if ($_SERVER['argv'][1] === 'export') { - // Export - - // Get output file name - // Either from second parameter or use default at temp path - $output = $_SERVER['argv'][2] ?: ($GLOBALS['TMP_PATH'] . '/studip-help-content-' . $version . '-' . $language . '.po'); - - // Error message: Script will not overwrite existing file unless forced - if (file_exists($output) && !$force) { - printf('Error: Output file "%s" exists. Use --force to overwrite.' . "\n", $output); - die(2); - } - - // Error message: Output directory does not exist - $output_dir = dirname($output); - if (!file_exists($output_dir)) { - printf('Error: Directory for output "%s" does not exist.' . "\n", $output_dir); - die(3); - } - // Error message: Output directory is not writable - if (!is_writable($output_dir)) { - printf('Error: Directory for output "%s" is not writable.' . "\n", $output_dir); - die(4); - } - - // Open output file for writing - $fp = fopen($output, 'w+'); - // Error message: Output file could not be openend for writing - if (!is_resource($fp)) { - printf('Error: Could not open output file "%s" for writing.' . "\n", $output); - die(5); - } - - // Write .po header - fputs($fp, '# Jan-Hendrik Willms <tleilax+studip@gmail.com>, 2014.' . "\n"); - fputs($fp, '# Generated content' . "\n"); - fputs($fp, 'msgid ""' . "\n"); - fputs($fp, 'msgstr ""' . "\n"); - fputs($fp, '"Project-Id-Version: STUDIP-' . $GLOBALS['SOFTWARE_VERSION'] . '\\n"' . "\n"); - fputs($fp, '"Language: STUDIP-' . $language . '\\n"' . "\n"); - fputs($fp, '"Report-Msgid-Bugs-To: tleilax+studip@gmail.com' . '\\n"' . "\n"); - fputs($fp, '"POT-Creation-Date: ' . date('r') . '\\n"' . "\n"); - fputs($fp, '"PO-Revision-Date: ' . date('r') . '\\n"' . "\n"); - fputs($fp, '"Last-Translator: Stud.IP Core Group <info@studip.de>\\n"' . "\n"); - fputs($fp, '"Language-Team: Stud.IP Core Group <info@studip.de>\\n"' . "\n"); - fputs($fp, '"MIME-Version: 1.0\\n"' . "\n"); - fputs($fp, '"Content-Type: text/plain; charset=UTF-8\\n"' . "\n"); - fputs($fp, '"Content-Transfer-Encoding: 8bit\\n"' . "\n"); - fputs($fp, "\n"); - - // Load all data from db in one big query - $query = "SELECT label AS content, CONCAT(route, ':', 10000 + position) AS occurence - FROM help_content - WHERE studip_version = :version - AND language = :language - AND custom = 0 - -- Help content label - - UNION - - SELECT CONCAT(content, '{#$#}', content_id) AS content, CONCAT(route, ':', 20000 + position) AS occurence - FROM help_content - WHERE studip_version = :version - AND language = :language - AND custom = 0 - -- Actual help content - - UNION - - SELECT CONCAT(name, '{#$#}', tour_id) AS content, CONCAT('tours.php:', 30000 + version) AS occurence - FROM help_tours - WHERE studip_version = :version - AND language = :language - -- Help tour name - - UNION - - SELECT CONCAT(description, '{#$#}', tour_id) AS content, CONCAT('tours.php:', 40000 + version) AS occurence - FROM help_tours - WHERE studip_version = :version - AND language = :language - -- Help tour description - - UNION - - SELECT CONCAT(title, '{#$#}', tour_id) AS content, CONCAT(route, ':', 50000 + step) AS occurence - FROM help_tour_steps - JOIN help_tours USING (tour_id) - WHERE studip_version = :version - AND language = :language - -- Individual help tour step title - - UNION - - SELECT CONCAT(tip, '{#$#}', tour_id) AS content, CONCAT(route, ':', 60000 + step) AS occurence - FROM help_tour_steps - JOIN help_tours USING (tour_id) - WHERE studip_version = :version - AND language = :language - -- Individual help tour step content - - UNION - - SELECT CONCAT(t0.content, '{#$#}', t0.tooltip_id) AS content, CONCAT(t0.route, ':', 70000 + t0.version) AS occurence - FROM help_tooltips AS t0 - LEFT JOIN help_tooltips AS t1 - ON t0.language = t1.language - AND t0.tooltip_id = t1.tooltip_id - AND t0.version < t1.version - WHERE t0.language = :language AND t1.tooltip_id IS NULL - -- Help tooltip - "; - $statement = DBManager::get()->prepare($query); - $statement->bindValue(':version', $version); - $statement->bindValue(':language', $language); - $statement->execute(); - $statement->setFetchMode(PDO::FETCH_GROUP | PDO::FETCH_COLUMN); - - // Loop through each row and write .po entry - foreach ($statement as $content => $occurences) { - list($content, $context) = explode('{#$#}', $content); - - fputs($fp, '#: ' . implode(' ', $occurences) . "\n"); - if ($context) { - fputs($fp, 'msgctxt "' . $context . '"' . "\n"); - } - fputs($fp, 'msgid ' . po_stringify($content) . "\n"); - fputs($fp, 'msgstr ""' . "\n"); - fputs($fp, "\n"); - } - - // Close output file - fclose($fp); -} elseif ($_SERVER['argv'][1] === 'import') { - // Import - - // Error message: Invalid parameters - if ($_SERVER['argc'] < 4) { - print 'Usage: ' . $_SERVER['argv'][0] . ' import [--language] <file> <version>'; - die(6); - } - - // Set input file and version from parameters - $input = $_SERVER['argv'][2]; - $version = $_SERVER['argv'][3]; - - // Error message: Input file does not exists or is not readable - if (!file_exists($input) || !is_readable($input)) { - printf('Error: Input file "%s" does not exist or is not readable.' . "\n", $input); - die(7); - } - - // Open input file for reading - $fp = fopen($input, 'r'); - // Error message: Input file could not be opened for reading - if (!is_resource($fp)) { - printf('Error: Could not open input file "%s" for reading.' . "\n", $input); - die(5); - } - - // Parse input .po file - // This is pretty straight forward, yet hacky. - // The script tries to detect comments (only #:, # by itself is ignored), - // message context, message id and message content in this order. - // Any empty line will write to messages array. - // This routine will probably break for any .po file that differs from the - // ones created in transifex. - // This is just supposed to work, not to be beautiful. ;) - $messages = []; - $context = ''; - $id = ''; - $content = ''; - $occurences = []; - $last = false; - $count = 0; - while (!feof($fp) && $row = fgets($fp)) { - $count += 1; - - $row = trim($row); - if ($row[0] === '#' && $row[1] !== ':') { - continue; - } - if ($row[0] === '#') { - $occurences = array_merge($occurences, explode(' ', mb_substr($row, 2))); - $occurences = array_filter($occurences); - $last = 'occurence'; - } elseif (preg_match('/^\msgctxt\\s+"(.*?)"$/', $row, $match)) { - $context = $match[1]; - $last = 'context'; - } elseif (preg_match('/^msgid\\s+"(.*?)"$/', $row, $match)) { - $id = po_unescape($match[1]); - $last = 'id'; - } elseif (preg_match('/^msgstr\\s+"(.*?)"$/', $row, $match)) { - $content = po_unescape($match[1]); - $last = 'content'; - } elseif (preg_match('/^"(.*?)"$/', $row, $match) && in_array($last, words('id content'))) { - if ($last === 'id') { - $id .= po_unescape($match[1]); - } else { - $content .= po_unescape($match[1]); - } - } elseif (!$row && $last === 'content') { - $messages[$context . '#' . $id] = compact(words('context id content occurences')); - - $context = ''; - $id = ''; - $content = ''; - $occurences = []; - $last = false; - } else { - printf('Parse error at line %u.' . "\n", $count); - printf('Last item was "%s".' . "\n", $last); - printf('Current row: %s' . "\n", $row); - die(6); - } - } - fclose($fp); - - // Parse meta information (no context & no id = item at '#') - $meta = []; - foreach (explode("\n", $messages['#']['content']) as $row) { - $row = trim($row); - if (!$row) { - continue; - } - - list($index, $content) = array_map('trim', explode(':', $row, 2)); - $meta[$index] = $content; - } - unset($messages['#']); - - // Get language - $language = mb_strtolower($meta['Language']); - - // Define db queries for each type (see comment block at the top of - // this file, type is distinguished by the line number / 10000) - $queries = []; - $queries[1] = "INSERT INTO help_content (content_id, language, label, icon, content, route, studip_version, position, custom, installation_id, mkdate) - VALUES (:id, :language, :content, 'info', '', :route, :version, :position, 0, '', UNIX_TIMESTAMP()) - ON DUPLICATE KEY UPDATE label = VALUES(label)"; - $queries[2] = "INSERT INTO help_content (content_id, language, label, icon, content, route, studip_version, position, custom, installation_id, mkdate) - VALUES (:id, :language, '', 'info', :content, :route, :version, :position, 0, '', UNIX_TIMESTAMP()) - ON DUPLICATE KEY UPDATE content = VALUES(content)"; - $queries[3] = "INSERT INTO help_tours (tour_id, name, description, type, roles, version, language, studip_version, installation_id, mkdate) - VALUES (:id, :content, '', 'tour', '', :position, :language, :version, '', UNIX_TIMESTAMP()) - ON DUPLICATE KEY UPDATE name = VALUES(name)"; - $queries[4] = "INSERT INTO help_tours (tour_id, name, description, type, roles, version, language, studip_version, installation_id, mkdate) - VALUES (:id, '', :content, 'tour', '', :position, :language, :version, '', UNIX_TIMESTAMP()) - ON DUPLICATE KEY UPDATE description = VALUES(description)"; - $queries[5] = "INSERT INTO help_tour_steps (tour_id, step, title, tip, interactive, css_selector, route, author_id, mkdate) - VALUES (:id, :position, :content, '', 0, '', :route, '', UNIX_TIMESTAMP()) - ON DUPLICATE KEY UPDATE title = VALUES(title)"; - $queries[6] = "INSERT INTO help_tour_steps (tour_id, step, title, tip, interactive, css_selector, route, author_id, mkdate) - VALUES (:id, :position, '', :content, 0, '', :route, '', UNIX_TIMESTAMP()) - ON DUPLICATE KEY UPDATE tip = VALUES(tip)"; - $queries[7] = "INSERT INTO help_tooltips (tooltip_id, language, version, content, author_id, mkdate, route) - VALUES (:id, :language, :position, :content, '', UNIX_TIMESTAMP(), :route) - ON DUPLICATE KEY UPDATE content = VALUES(content)"; - - // Prepare statements and prebind version and language - $statements = array_map([DBManager::get(), 'prepare'], $queries); - foreach ($statements as $index => $statement) { - $statement->bindValue(':version', $version); - $statement->bindValue(':language', $language); - - $statements[$index] = $statement; - } - - // Process each message, skip the ones with empty content - foreach ($messages as $message) { - if (empty($message['content'])) { - continue; - } - - foreach ($message['occurences'] as $occurence) { - list($route, $lineno) = explode(':', $occurence); - $index = floor($lineno / 10000); - $position = $lineno % 10000; - - $id = get_id($version, $language, $message, $route, $index, $position); - - $statement = $statements[$index]; - $statement->bindValue(':id', $id); - $statement->bindValue(':content', $message['content']); - $statement->bindValue(':route', $route); - $statement->bindValue(':position', $position); - $statement->execute(); - } - } -} diff --git a/cli/i18n-plugin.php b/cli/i18n-plugin.php deleted file mode 100755 index e3014131f921c2c6f88863fe3f1593ff6237e106..0000000000000000000000000000000000000000 --- a/cli/i18n-plugin.php +++ /dev/null @@ -1,89 +0,0 @@ -#!/usr/bin/env php -<?php -require_once 'studip_cli_env.inc.php'; - -if ($_SERVER['argc'] < 3) { - fwrite(STDOUT, 'Stud.IP plugin localization tool - Tools for the localization of a plugin' . PHP_EOL); - fwrite(STDOUT, '=========================================================================' . PHP_EOL); - fwrite(STDOUT, 'Usage: ' . basename(__FILE__) . ' <folder> <command>' . PHP_EOL); - fwrite(STDOUT, PHP_EOL); - fwrite(STDOUT, '<folder> is the folder of the plugin you want to localize.' . PHP_EOL); - fwrite(STDOUT, '<command> is any of the commands listed below.' . PHP_EOL); - fwrite(STDOUT, PHP_EOL); - fwrite(STDOUT, 'Commands:' . PHP_EOL); - fwrite(STDOUT, ' detect - Detects probably unmarked strings for localization in php files.' . PHP_EOL); - fwrite(STDOUT, ' extract - Extracts the localizable string from php files into a .pot file.' . PHP_EOL); - fwrite(STDOUT, ' compile - Compiles all .po files in the locale folder of the plugin' . PHP_EOL); - fwrite(STDOUT, PHP_EOL); - exit(0); -} - -$plugin_folder = $_SERVER['argv'][1]; -$command = $_SERVER['argv'][2]; - -if (!is_dir($plugin_folder)) { - $plugin_folder = rtrim($GLOBALS['ABSOLUTE_PATH_STUDIP'], '/') . '/' . ltrim($plugin_folder, '/'); -} -if (!is_dir($plugin_folder)) { - fwrite(STDERR, 'Error: ' . $_SERVER['argv'][2] . ' is not a valid folder' . PHP_EOL); - exit(0); -} - -$plugin_folder = rtrim($plugin_folder, '/'); - -if (!file_exists($plugin_folder. '/plugin.manifest')) { - fwrite(STDERR, 'Error: ' . $_SERVER['argv'][2] . ' is not a valid plugin folder. Manifest is missing.' . PHP_EOL); - exit(0); -} -$manifest = parse_ini_file($plugin_folder . '/plugin.manifest', false, INI_SCANNER_RAW); - -$languages = array_map(function ($lang) { - return explode('_', $lang)[0]; -}, array_keys($GLOBALS['INSTALLED_LANGUAGES'])); - -if ($command === 'detect') { - $iterator = new RecursiveDirectoryIterator($plugin_folder, FilesystemIterator::FOLLOW_SYMLINKS | FilesystemIterator::UNIX_PATHS); - $iterator = new RecursiveIteratorIterator($iterator); - $regexp_iterator = new RegexIterator($iterator, '/\.php$/', RecursiveRegexIterator::MATCH); - - foreach ($regexp_iterator as $file) { - $filename = $file->getPathName(); - if (preg_match('/(?<![$>])_\(/', file_get_contents($filename))) { - fwrite(STDOUT, "{$filename}" . PHP_EOL); - } - } - - // system("ack -l '(?<![$>])_\(' {$plugin_folder}"); -} elseif ($command === 'extract') { - if (!isset($manifest['localedomain'])) { - fwrite(STD_ERROR, 'No localedomain found in plugin manifest' . PHP_EOL); - } - - $pot_name = $manifest['localedomain']; - - foreach (array_keys($GLOBALS['CONTENT_LANGUAGES']) as $lang) { - $lang = explode('_', $lang)[0]; - $language_dir = "{$plugin_folder}/locale/{$lang}/LC_MESSAGES"; - if (!file_exists($language_dir)) { - mkdir($language_dir, 0755, true); - } - } - - $main_lang = reset($languages); - $pot_file = "{$plugin_folder}/locale/{$main_lang}/LC_MESSAGES/{$pot_name}.pot"; - file_put_contents($pot_file, ''); - - system("find {$plugin_folder} -iname '*.php' | xargs xgettext --keyword=_n:1,2 --from-code=UTF-8 -j -n --language=PHP --add-location=never --package-name={$manifest['pluginclassname']} -o {$pot_file}"); -} elseif ($command === 'compile') { - foreach (glob("{$plugin_folder}/locale/*/LC_MESSAGES/*.po") as $po) { - $mo = preg_replace('/\.po$/', '.mo', $po); - system("msgfmt {$po} -o {$mo}"); - } - -} else { - fwrite(STDERR, 'Unknown command: ' . $_SERVER['argv'][1] . PHP_EOL); - exit(0); -} - -exit(1); - diff --git a/cli/kill_studip_user.php b/cli/kill_studip_user.php deleted file mode 100755 index 7798fa44a40c4dc97b06ac1bff078b70e1a7b7ca..0000000000000000000000000000000000000000 --- a/cli/kill_studip_user.php +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env php -<?php -# Lifter003: TEST -# Lifter007: TODO -/** -* kill_studip_user.php -* -* -* -* -* @author André Noack <noack@data-quest.de>, Suchi & Berg GmbH <info@data-quest.de> -* @access public -*/ -// +---------------------------------------------------------------------------+ -// This file is part of Stud.IP -// kill_studip_user.php -// -// Copyright (C) 2006 André Noack <noack@data-quest.de>, -// Suchi & Berg GmbH <info@data-quest.de> -// +---------------------------------------------------------------------------+ -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or any later version. -// +---------------------------------------------------------------------------+ -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -// +---------------------------------------------------------------------------+ -define('SEND_MAIL_ON_DELETE', 1); -define('KILL_ADMINS' , 0); - -require_once __DIR__ . '/studip_cli_env.inc.php'; - -if (SEND_MAIL_ON_DELETE && !($MAIL_LOCALHOST && $MAIL_HOST_NAME && $ABSOLUTE_URI_STUDIP)){ - trigger_error('To use this script you MUST set correct values for $MAIL_LOCALHOST, $MAIL_HOST_NAME and $ABSOLUTE_URI_STUDIP in local.inc!', E_USER_ERROR); -} - -$argc = $_SERVER['argc']; -$argv = $_SERVER['argv']; - -if (!$argv[1]){ - fwrite(STDOUT,'Usage: ' . basename(__FILE__) . ' [file][-] (use - to read from STDIN)' .chr(10)); - exit(0); -} -if ($argv[1] == '-'){ - $fo = STDIN; -} elseif (is_file($argv[1])){ - $fo = fopen($argv[1],'r'); -} else { - trigger_error("File not found: {$argv[1]}", E_USER_ERROR); -} - -$list = ''; -while (!feof($fo)) { - $list .= fgets($fo, 1024); -} - -$kill_list = preg_split("/[\s,;]+/", $list, -1, PREG_SPLIT_NO_EMPTY); -$kill_list = array_unique($kill_list); - -$query = "SELECT * FROM auth_user_md5 WHERE username IN (?)"; -$statement = DBManager::get()->prepare($query); -$statement->execute([$kill_list ?: '']); -while ($row = $statement->fetch(PDO::FETCH_ASSOC)) { - $kill_user[$row['username']] = $row; -} -if (!is_array($kill_user)) { - fwrite(STDOUT, 'No user from list found in database.' . chr(10)); - exit(0); -} - -foreach($kill_user as $uname => $udetail){ - if (!KILL_ADMINS && ($udetail['perms'] == 'admin' || $udetail['perms'] == 'root')){ - fwrite(STDOUT, "user: $uname is '{$udetail['perms']}', NOT deleted". chr(10)); - } else { - $umanager = new UserManagement($udetail['user_id']); - //wenn keine Email gewünscht, Adresse aus den Daten löschen - if (!SEND_MAIL_ON_DELETE) $umanager->user_data['auth_user_md5.Email'] = ''; - if ($umanager->deleteUser()){ - fwrite(STDOUT, "user: $uname successfully deleted:". chr(10) - . parse_msg_to_clean_text($umanager->msg) - . chr(10)); - } else { - fwrite(STDOUT, "user: $uname NOT deleted:". chr(10) - . parse_msg_to_clean_text($umanager->msg) - . chr(10)); - } - } -} -exit(1); diff --git a/cli/migrate.php b/cli/migrate.php deleted file mode 100755 index 618132a8b8255fc0ab8f7dbf51e8f9b835d0daf5..0000000000000000000000000000000000000000 --- a/cli/migrate.php +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env php -<?php -# Lifter007: TODO -# Lifter003: TODO -/* - * migrate.php - Migrations for Stud.IP - * - * Copyright (C) 2006 - Marcus Lunzenauer <mlunzena@uos.de> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - */ - -require_once __DIR__ . '/studip_cli_env.inc.php'; - -if (isset($_SERVER['argv'])) { - # check for command line options - $options = getopt('b:d:lm:t:v'); - if ($options === false) { - exit(1); - } - - # check for options - $domain = 'studip'; - $branch = '0'; - $list = false; - $path = $STUDIP_BASE_PATH . '/db/migrations'; - $verbose = false; - $target = null; - - foreach ($options as $option => $value) { - switch ($option) { - case 'b': - $branch = (string) $value; - break; - case 'd': - $domain = (string) $value; - break; - case 'l': - $list = true; - break; - case 'm': - $path = $value; - break; - case 't': - $target = (int) $value; - break; - case 'v': - $verbose = true; - break; - } - } - - $version = new DBSchemaVersion($domain, $branch); - $migrator = new Migrator($path, $version, $verbose); - - if ($list) { - $migrations = $migrator->relevantMigrations($target); - - foreach ($migrations as $number => $migration) { - $description = $migration->description() ?: '(no description)'; - printf("%6s %-20s %s\n", $number, get_class($migration), $description); - } - } else { - $migrator->migrateTo($target); - } -} diff --git a/cli/migrate_help_content.php b/cli/migrate_help_content.php deleted file mode 100755 index 71b4c663174e855ac6fe6696b09c25f488de8ce4..0000000000000000000000000000000000000000 --- a/cli/migrate_help_content.php +++ /dev/null @@ -1,89 +0,0 @@ -#!/usr/bin/env php -<?php -/** -* migrate_help_content.php -* -* @author Arne Schröder <schroeder@data-quest.de>, Suchi & Berg GmbH <info@data-quest.de> -* @access public -*/ -// +---------------------------------------------------------------------------+ -// This file is part of Stud.IP -// -// Copyright (C) 2014 Arne Schröder <schroeder@data-quest.de>, -// Suchi & Berg GmbH <info@data-quest.de> -// +---------------------------------------------------------------------------+ -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or any later version. -// +---------------------------------------------------------------------------+ -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -// +---------------------------------------------------------------------------+ - -require_once __DIR__ . '/studip_cli_env.inc.php'; - -$help_path = __DIR__ . '/../doc/helpbar'; - -$argc = $_SERVER['argc']; -$argv = $_SERVER['argv']; - -if (!$argv[2]){ - fwrite(STDOUT,'Usage: ' . basename(__FILE__) . ' [version] [language]' .chr(10)); - exit(0); -} - -$query = "SELECT * FROM help_content WHERE studip_version = ? LIMIT 1"; -$statement = DBManager::get()->prepare($query); -$statement->execute([$argv[1]]); -$ret = $statement->fetchGrouped(PDO::FETCH_ASSOC); -if (count($ret)) { - trigger_error('Helpbar content already present for this version!', E_USER_ERROR); -} - -$filename = $help_path .'/'. $argv[2] . '/helpcontent.json'; -if (is_file($filename)){ - $json = json_decode(file_get_contents($filename), true); -} else { - trigger_error("File not found: ".$filename, E_USER_ERROR); -} - -if ($json === null) { - trigger_error('Helpbar content could not be loaded. File: '.$filename, E_USER_ERROR); -} - -foreach ($json as $row) { - if (!is_array($row['text'])) - $row['text'] = [$row['text']]; - if (!$row['label']) - $row['label'] = ''; - if (!$row['icon']) - $row['icon'] = ''; - foreach ($row['text'] as $index => $text) { - $count[$argv[2].$row['route']]++; - $query = "INSERT INTO help_content (content_id, language, label, icon, content, route, studip_version, position, custom, visible, author_id, installation_id, mkdate) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, 1, '', ?, UNIX_TIMESTAMP())"; - $statement = DBManager::get()->prepare($query); - $statement->execute([md5(uniqid(rand(), true)), $argv[2], ($index == 0 ? $row['label'] : ''), ($index == 0 ? $row['icon'] : ''), $text, $row['route'], $argv[1], $count[$argv[2].$row['route']], Config::get()->STUDIP_INSTALLATION_ID]); - } -} -if (count($count)) { - if (!Config::get()->getValue('HELP_CONTENT_CURRENT_VERSION')) - Config::get()->create('HELP_CONTENT_CURRENT_VERSION', [ - 'value' => $argv[1], - 'is_default' => 0, - 'type' => 'string', - 'range' => 'global', - 'section' => 'global', - 'description' => _('Aktuelle Version der Helpbar-Einträge in Stud.IP') - ]); - else - Config::get()->store('HELP_CONTENT_CURRENT_VERSION', $argv[1]); -} -fwrite(STDOUT, 'help content added for '.count($count).' routes.' . chr(10)); -exit(1); diff --git a/cli/myisam_to_innodb.php b/cli/myisam_to_innodb.php deleted file mode 100755 index 85dab426a31248113a92e178eba2a31b8c1da470..0000000000000000000000000000000000000000 --- a/cli/myisam_to_innodb.php +++ /dev/null @@ -1,122 +0,0 @@ -#!/usr/bin/env php -<?php -require_once(__DIR__.'/studip_cli_env.inc.php'); - -echo 'Migration starting at '.date('d.m.Y H:i:s').".\n"; -$start = microtime(true); - -global $DB_STUDIP_DATABASE; - -// Check if InnoDB is enabled in database server. -$engines = DBManager::get()->fetchAll("SHOW ENGINES"); -$innodb = false; -foreach ($engines as $e) { - // InnoDB is found and enabled. - if ($e['Engine'] == 'InnoDB' && in_array(mb_strtolower($e['Support']), ['default', 'yes'])) { - $innodb = true; - break; - } -} - -if ($innodb) { - // Get version of database system (MySQL/MariaDB/Percona) - $data = DBManager::get()->fetchFirst("SELECT VERSION() AS version"); - $version = $data[0]; - - // Tables to ignore on engine conversion. - $ignore_tables = []; - - - - - // Fetch all tables that need to be converted. - $tables = DBManager::get()->fetchFirst("SELECT TABLE_NAME - FROM `information_schema`.TABLES - WHERE TABLE_SCHEMA=:database AND ENGINE=:oldengine - ORDER BY TABLE_NAME", - [ - ':database' => $DB_STUDIP_DATABASE, - ':oldengine' => 'MyISAM', - ]); - - /* - * lit_catalog needs fulltext indices which InnoDB doesn't support - * in older versions. - */ - if (version_compare($version, '5.6', '<')) { - $stmt_fulltext = DBManager::get()->prepare("SHOW INDEX FROM :database.:table WHERE Index_type = 'FULLTEXT'"); - foreach ($tables as $k => $t) { - $stmt_fulltext->bindParam(':table', $t, StudipPDO::PARAM_COLUMN); - $stmt_fulltext->bindParam(':database', $DB_STUDIP_DATABASE, StudipPDO::PARAM_COLUMN); - $stmt_fulltext->execute(); - if ($stmt_fulltext->fetch()) { - $ignore_tables[] = $t; - unset($tables[$k]); - } - } - if (count($ignore_tables)) { - echo 'The following tables needs fulltext indices '. - 'which are not supported for InnoDB in your database '. - 'version, so the tables will be left untouched: ' . join(',', $ignore_tables) . "\n"; - } - } - - - // Use Barracuda format if database supports it (5.5 upwards). - if (version_compare($version, '5.5', '>=')) { - echo "\tFound MySQL in version >= 5.5, checking if Barracuda file format is supported..."; - // Get innodb_file_per_table setting - $data = DBManager::get()->fetchOne("SHOW VARIABLES LIKE 'innodb_file_per_table'"); - $file_per_table = $data['Value']; - - // Check if Barracuda file format is enabled - $data = DBManager::get()->fetchOne("SHOW VARIABLES LIKE 'innodb_file_format'"); - $file_format = $data['Value']; - - if (mb_strtolower($file_per_table) == 'on' && mb_strtolower($file_format) == 'barracuda') { - echo " yes.\n"; - $rowformat = 'DYNAMIC'; - } else { - echo " no:\n"; - if (mb_strtolower($file_per_table) != 'on') { - echo "\t- file_per_table not set\n"; - } - if (mb_strtolower($file_format) != 'barracuda') { - echo "\t- file_format not set to Barracuda (but to " . $file_format . ")\n"; - } - $rowformat = 'COMPACT'; - } - } - - // Prepare query for table conversion. - $stmt = DBManager::get()->prepare("ALTER TABLE :database.:table ROW_FORMAT=:rowformat ENGINE=:newengine"); - $stmt->bindParam(':database', $DB_STUDIP_DATABASE, StudipPDO::PARAM_COLUMN); - $stmt->bindParam(':rowformat', $rowformat, StudipPDO::PARAM_COLUMN); - $newengine = 'InnoDB'; - $stmt->bindParam(':newengine', $newengine, StudipPDO::PARAM_COLUMN); - - // Now convert the found tables. - foreach ($tables as $t) { - $local_start = microtime(true); - $stmt->bindParam(':table', $t, StudipPDO::PARAM_COLUMN); - $stmt->execute(); - $local_end = microtime(true); - $local_duration = $local_end - $local_start; - $human_local_duration = sprintf("%02d:%02d:%02d", - ($local_duration / 60 / 60) % 24, ($local_duration / 60) % 60, $local_duration % 60); - - echo "\tConversion of table " . $t . " took " . $human_local_duration . ".\n"; - } - - - $end = microtime(true); - - $duration = $end - $start; - $human_duration = sprintf("%02d:%02d:%02d", - ($duration / 60 / 60) % 24, ($duration / 60) % 60, $duration % 60); - - echo 'Migration finished at ' . date('d.m.Y H:i:s') . ', duration ' . $human_duration . ".\n"; -} else { - echo "The storage engine InnoDB is not enabled in your ". - "database installation, tables cannot be converted.\n"; -} diff --git a/cli/plugin_manager b/cli/plugin_manager deleted file mode 100755 index e566c75a13ef419f74a5b375debda62637c29d2c..0000000000000000000000000000000000000000 --- a/cli/plugin_manager +++ /dev/null @@ -1,310 +0,0 @@ -#!/usr/bin/env php -<?php -/* - * plugin_manager.php - CLI Plugin-Manager for Stud.IP - * - * Detailed documentation of this cli-script can be found at: - * http://docs.studip.de/develop/Entwickler/CLIPluginManager - * - * Copyright (C) 2012 - Till Glöggler <tgloeggl@uos.de> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 3 of - * the License, or (at your option) any later version. - */ - -require_once 'studip_cli_env.inc.php'; -require_once 'cli/getopts.php'; - -$args = $_SERVER['argv']; - -if ($args) { - - $command = $args[1]; - - if (!$command) { - echo 'Usage: '. $args[0] .' {install|register|unregister|migrate|activate|deactivate|info|scan}' . "\n"; - } - - switch ($command) { - case 'install': - $zipfile = $args[2]; - - // show usage - if (!$zipfile) { - echo 'Usage: '. $args[0] .' install PATH/TO/PLUGIN.ZIP' . "\n\n"; - exit(1); - } - - $plugin_admin = new PluginAdministration(); - - try { - if (parse_url($zipfile, PHP_URL_SCHEME)) { - $plugin_admin->installPluginFromURL($zipfile); - } else { - $plugin_admin->installPlugin($zipfile); - } - echo 'Das Plugin wurde erfolgreich installiert.' . "\n"; - } catch (PluginInstallationException $ex) { - echo $ex->getMessage() . "\n"; - } - - exit(0); - break; - - case 'register': - $plugindir = $args[2]; - - // show usage - if (!$plugindir) { - echo 'Usage: '. $args[0] .' register PATH/TO/PLUGIN' . "\n\n"; - # echo 'Options:' . "\n"; - # echo "\t". '-f force installation and try to (re-)execute any sql-scripts associated' ."\n"; - exit(1); - } - - # $options = getopts(':f'); // if f is set, try to execute the plugins sql-scripts (if any) - - $plugin_manager = PluginManager::getInstance(); - $manifest = $plugin_manager->getPluginManifest($plugindir); - - if (!$manifest) { - echo 'Das Plugin-Manifest fehlt!' . "\n"; - exit(1); - } - - // get plugin meta data - $pluginclass = $manifest['pluginclassname']; - $origin = $manifest['origin']; - $min_version = $manifest['studipMinVersion']; - $max_version = $manifest['studipMaxVersion']; - - // check for compatible version - if ((isset($min_version) && StudipVersion::olderThan($min_version)) || - (isset($max_version) && StudipVersion::newerThan($max_version))) { - throw new PluginInstallationException(_('Das Plugin ist mit dieser Stud.IP-Version nicht kompatibel.')); - } - - // determine the plugin path - $basepath = Config::get()->PLUGINS_PATH; - $pluginpath = $origin . '/' . $pluginclass; - - $plugin_manager = PluginManager::getInstance(); - $pluginregistered = $plugin_manager->getPluginInfo($pluginclass); - - // create database schema if needed - if (isset($manifest['dbscheme']) && !$pluginregistered) { - $schemafile = $plugindir . '/' . $manifest['dbscheme']; - $contents = file_get_contents($schemafile); - $statements = preg_split("/;[[:space:]]*\n/", $contents, -1, PREG_SPLIT_NO_EMPTY); - $db = DBManager::get(); - foreach ($statements as $statement) { - $db->exec($statement); - } - } - - // check for migrations - if (is_dir($plugindir . '/migrations')) { - $schema_version = new DBSchemaVersion($manifest['pluginname']); - $migrator = new Migrator($plugindir . '/migrations', $schema_version); - $migrator->migrateTo(null); - } - - // now register the plugin in the database - $pluginid = $plugin_manager->registerPlugin($manifest['pluginname'], $pluginclass, $pluginpath); - - // register additional plugin classes in this package - $additionalclasses = $manifest['additionalclasses']; - - if (is_array($additionalclasses)) { - foreach ($additionalclasses as $class) { - $plugin_manager->registerPlugin($class, $class, $pluginpath, $pluginid); - } - } - - echo 'Das Plugin '. $manifest['pluginname'] .' wurde erfolgreich eingetragen.' . "\n"; - break; - - case 'migrate': - $pluginname = $args[2]; - unset($args[0], $args[1], $args[2]); - - // show usage - if (!$pluginname) { - echo 'Usage: '. $args[0] .' migrate PLUGINNAME [-l] [-v] [-t target] [-b branch]' . "\n"; - exit(1); - } - - // parse options - list($errors, $options, $args) = getopts(array('l' => 'Ss l list', 'v' => 'Ss v verbose', 't' => 'Vs t target', 'b' => 'Vs b branch')); - $list = false; - $verbose = false; - $target = NULL; - $branch = '0'; - - foreach ($options as $option => $value) { - switch ($option) { - case 'b': - $branch = ($value === false) ? '0' : $value; - break; - case 'l': - $list = $value; - break; - case 't': - $target = ($value === false) ? null : (int) $value; - break; - case 'v': - $verbose = $value; - break; - } - } - - // create plugin-manager and search for plugin by name - $plugin_manager = PluginManager::getInstance(); - $plugins = $plugin_manager->getPluginInfos(); - - foreach ($plugins as $plugin) { - if (mb_strtolower($pluginname) === mb_strtolower($plugin['name'])) { - $plugindir = Config::get()->PLUGINS_PATH . '/' . $plugin['path']; - - if (is_dir($plugindir . '/migrations')) { - // if there are migrations, migrate - $schema_version = new DBSchemaVersion($plugin['name'], $branch); - $migrator = new Migrator($plugindir . '/migrations', $schema_version, $verbose); - - if ($list) { - $migrations = $migrator->relevantMigrations($target); - - foreach ($migrations as $number => $migration) { - $description = $migration->description() ?: '(no description)'; - - printf("%6s %-20s %s\n", $number, get_class($migration), $description); - } - } else { - $migrator->migrateTo($target); - } - - exit(0); - } else { - echo 'Konnte keine Migrationen für das Plugin '. $plugin['name'] .' finden.' . "\n"; - exit(1); - } - } - } - - echo 'Konnte kein Plugin mit dem Namen ' . $pluginname . ' finden.' . "\n"; - echo 'Ãœberprüfen sie bitte den Namen (auch auf Groß-/Kleinschreibung!)' ."\n"; - exit(1); - break; - - case 'unregister': - $pluginname = $args[2]; - - // show usage - if (!$pluginname) { - echo 'Usage: '. $args[0] .' unregister PLUGINNAME' . "\n"; - exit(1); - } - - $plugin_manager = PluginManager::getInstance(); - $plugins = $plugin_manager->getPluginInfos(); - foreach ($plugins as $plugin) { - if (mb_strtolower($pluginname) == mb_strtolower($plugin['name'])) { - $plugindir = Config::get()->PLUGINS_PATH .'/'. $plugin['path']; - - $plugin_manager->unregisterPlugin($plugin['id']); - - if (is_dir($plugindir . '/migrations')) { - $schema_version = new DBSchemaVersion($plugin['name']); - $migrator = new Migrator($plugindir . '/migrations', $schema_version); - $migrator->migrateTo(0); - } - - echo 'Das Plugin '. $plugin['name'] .' wurde ausgetragen.' . "\n"; - exit(0); - } - } - - echo 'Konnte kein Plugin mit dem Namen '. $pluginname .' finden.' . "\n"; - echo 'Ãœberprüfen sie bitte den Namen (auch auf Groß-/Kleinschreibung!)' ."\n"; - exit(1); - break; - - case 'activate': - case 'deactivate': - $pluginname = $args[2]; - - // show usage - if (!$pluginname) { - echo 'Usage: '. $args[0] .' '. $command .' PLUGINNAME' . "\n"; - exit(1); - } - - $plugin_manager = PluginManager::getInstance(); - $plugins = $plugin_manager->getPluginInfos(); - foreach ($plugins as $plugin) { - if (mb_strtolower($pluginname) == mb_strtolower($plugin['name'])) { - $plugin_manager->setPluginEnabled($plugin['id'], ($command == 'activate')); - echo 'Das Plugin '. $plugin['name'] .' wurde ' . ($command == 'activate' ? 'aktiviert' : 'deaktiviert') . '.' . "\n"; - exit(0); - } - } - - echo 'Konnte kein Plugin mit dem Namen '. $pluginname .' finden.' . "\n"; - echo 'Ãœberprüfen sie bitte den Namen (auch auf Groß-/Kleinschreibung!)' ."\n"; - exit(1); - break; - - case 'info': - $pluginname = $args[2]; - - $plugin_manager = PluginManager::getInstance(); - $plugins = $plugin_manager->getPluginInfos(); - if ($pluginname) { - $plugins = array_filter($plugins, function($p) use ($pluginname) {return mb_stripos($p['name'], $pluginname) !== false;}); - } - $basepath = Config::get()->PLUGINS_PATH; - foreach ($plugins as $plugin) { - $plugindir = $basepath . '/' . $plugin['path'] . '/'; - $plugin['class_exists'] = 0; - $pluginfile = $plugindir . $plugin['class'] . '.class.php'; - if (file_exists($pluginfile)) { - $plugin['class_exists'] = 1; - } else { - $pluginfile = $plugindir . $plugin['class'] . '.php'; - if (file_exists($pluginfile)) { - $plugin['class_exists'] = 1; - } - } - if (is_dir($plugindir . '/migrations')) { - $schema_version = new DBSchemaVersion($plugin['name']); - $migrator = new Migrator($plugindir .'/migrations', $schema_version); - $plugin['migration_top_version'] = $migrator->topVersion(); - $plugin['schema_version'] = $schema_version->get(); - } - echo "\n"; - $plugin['type'] = join(',' , $plugin['type']); - echo join("\n", array_filter(array_map(function($p){if ($p[0] == ' ') return trim($p);},explode("\n", print_r($plugin,1))))); - echo "\n"; - } - exit(0); - break; - - case 'scan': - $plugin_admin = new PluginAdministration(); - $plugin_manager = PluginManager::getInstance(); - foreach ($plugin_admin->scanPluginDirectory() as $manifest) { - if (!$plugin_manager->getPluginInfo($manifest['pluginclassname'])) { - echo "\n"; - echo join("\n", array_filter(array_map(function($p){if ($p[0] == ' ') return trim($p);},explode("\n", print_r($manifest,1))))); - echo "\n"; - } - } - exit(0); - break; - } - -} - -exit(0); diff --git a/cli/studip b/cli/studip new file mode 100755 index 0000000000000000000000000000000000000000..b723ad58ae62e73d09bb19524a41902920143280 --- /dev/null +++ b/cli/studip @@ -0,0 +1,62 @@ +#!/usr/bin/env php +<?php + +namespace Studip\Cli; + +use Symfony\Component\Console\Application; + +require __DIR__.'/studip_cli_env.inc.php'; +require __DIR__.'/../composer/autoload.php'; + +\StudipAutoloader::addAutoloadPath('cli', 'Studip\\Cli'); + +$application = new Application(); +$commands = [ + Commands\Base\Dump::class, + Commands\Checks\Compatibility::class, + Commands\Checks\GlobalizedConfig::class, + Commands\Checks\HelpTours::class, + Commands\Checks\HelpTours::class, + Commands\CleanupAdmissionRules::class, + Commands\Cronjobs\CronjobExecute::class, + Commands\Cronjobs\CronjobExecute::class, + Commands\Cronjobs\CronjobList::class, + Commands\Cronjobs\CronjobList::class, + Commands\Cronjobs\CronjobWorker::class, + Commands\Cronjobs\CronjobWorker::class, + Commands\DB\Dump::class, + Commands\DB\MigrateEngine::class, + Commands\DB\MigrateFileFormat::class, + Commands\Files\Dump::class, + Commands\Fix\Biest7789::class, + Commands\Fix\Biest7866::class, + Commands\Fix\Biest8136::class, + Commands\Fix\EndTimeWeeklyRecurredEvents::class, + Commands\Fix\IconDimensions::class, + Commands\HelpContent\Migrate::class, + Commands\Migrate\MigrateList::class, + Commands\Migrate\MigrateStatus::class, + Commands\Migrate\Migrate::class, + Commands\Plugins\PluginDeactivate::class, + Commands\Plugins\PluginInfo::class, + Commands\Plugins\PluginInstall::class, + Commands\Plugins\PluginListMigrations::class, + Commands\Plugins\PluginStatusMigrations::class, + Commands\Plugins\PluginMigrate::class, + Commands\Plugins\PluginRegister::class, + Commands\Plugins\PluginScan::class, + Commands\Plugins\PluginUnregister::class, + Commands\Plugins\I18N\I18NDetect::class, + Commands\Plugins\I18N\I18NExtract::class, + Commands\Plugins\I18N\I18NCompile::class, + Commands\Resources\UpdateBookingIntervals::class, + Commands\SORM\DescribeModels::class, + Commands\Translations\VueGettextSplitTranslations::class, + Commands\Users\UserDelete::class, + Commands\Users\UserDelete::class, +]; +$creator = function ($command) { + return new $command(); +}; +$application->addCommands(array_map($creator, $commands)); +$application->run(); diff --git a/cli/studip-compat.php b/cli/studip-compat.php deleted file mode 100755 index 18b18c20d45181364681f3cace4b989bd007eed3..0000000000000000000000000000000000000000 --- a/cli/studip-compat.php +++ /dev/null @@ -1,203 +0,0 @@ -#!/usr/bin/env php -<?php -require_once 'studip_cli_env.inc.php'; - -$opts = getopt('fhnvc', ['filenames', 'help', 'non-recursive', 'verbose', 'no-color']); - -if (isset($opts['h']) || isset($opts['help'])) { - fwrite(STDOUT, 'Stud.IP compatibility scanner - Checks plugins for common issues' . PHP_EOL); - fwrite(STDOUT, '================================================================' . PHP_EOL); - fwrite(STDOUT, 'Usage: ' . basename(__FILE__) . ' [OPTION] [VERSION] [FOLDER] ..' . PHP_EOL); - fwrite(STDOUT, PHP_EOL); - fwrite(STDOUT, '[VERSION] is optional, if not given all checks are applied.' . PHP_EOL); - fwrite(STDOUT, '[FOLDER] will default to the plugins_packages folder.' . PHP_EOL); - fwrite(STDOUT, 'Supply as many folders as you need.' . PHP_EOL); - fwrite(STDOUT, PHP_EOL); - fwrite(STDOUT, 'Options:' . PHP_EOL); - fwrite(STDOUT, ' -h, --help Display this help' . PHP_EOL); - fwrite(STDOUT, ' -f, --filenames Display only filenames' . PHP_EOL); - fwrite(STDOUT, ' -n, --non-recursive Do not scan recursively into subfolders' . PHP_EOL); - fwrite(STDOUT, ' -c, --no-color Do not use colors for output' . PHP_EOL); - fwrite(STDOUT, ' -v, --verbose Print additional information' . PHP_EOL); - fwrite(STDOUT, PHP_EOL); - exit(0); -} - -// Reduce arguments by options (this is far from perfect) -$args = $_SERVER['argv']; -$arg_stop = array_search('--', $args); -if ($arg_stop !== false) { - $args = array_slice($args, $arg_stop + 1); -} elseif (count($opts)) { - $args = array_slice($args, 1 + count($opts)); -} else { - $args = array_slice($args, 1); -} - -$verbose = isset($opts['v']) || isset($opts['verbose']); -$only_filenames = isset($opts['f']) || isset($opts['filenames']); -$recursive = !(isset($opts['n']) || isset($opts['non-recursive'])); -$no_colors = isset($opts['c']) || isset($opts['no-color']) || !stream_isatty(STDOUT); -$version = null; -$folders = array_values($args) ?: []; - -if (count($folders) > 0 && preg_match('/^\d+\.\d+$/', $folders[0])) { - $version = array_shift($folders); -} - -// Prepare logging mechanism -$log = function ($message) use ($no_colors) { - $ansi = [ - 'off' => 0, - 'bold' => 1, - 'italic' => 3, - 'underline' => 4, - 'blink' => 5, - 'inverse' => 7, - 'hidden' => 8, - 'black' => 30, - 'red' => 31, - 'green' => 32, - 'yellow' => 33, - 'blue' => 34, - 'magenta' => 35, - 'cyan' => 36, - 'white' => 37, - 'black_bg' => 40, - 'red_bg' => 41, - 'green_bg' => 42, - 'yellow_bg' => 43, - 'blue_bg' => 44, - 'magenta_bg' => 45, - 'cyan_bg' => 46, - 'white_bg' => 47 - ]; - - $message = trim($message); - - if ($message) { - $args = array_slice(func_get_args(), 1); - $message = vsprintf($message . "\n", $args); - - $ansi_codes = implode('|', array_keys($ansi)); - if (preg_match_all('/#\{((?:(?:' . $ansi_codes . '),?)+):(.+?)\}/s', $message, $matches, PREG_SET_ORDER)) { - foreach ($matches as $match) { - $chunk = ''; - if (!$no_colors) { - $codes = explode(',', $match[1]); - foreach ($codes as $code) { - $chunk .= "\033[{$ansi[$code]}m"; - } - } - $chunk .= $match[2]; - if (!$no_colors) { - $chunk .= "\033[{$ansi[off]}m"; - } - - $message = str_replace($match[0], $chunk, $message); - } - } - - print $message; - } -}; -$log_if = function ($condition, $message) use ($log) { - if ($condition) { - call_user_func_array($log, array_slice(func_get_args(), 1)); - } -}; - -// Reduces filename by base path and plugin folder -$reduce = function ($folder) { - $folder = str_replace($GLOBALS['STUDIP_BASE_PATH'] . '/', '', $folder); - $folder = str_replace('public/plugins_packages/', '', $folder); - return $folder; -}; - -// Get rules -if (!$version) { - $rules = []; - foreach (glob(__DIR__ . '/compatibility-rules/*.php') as $file) { - $version_rules = require $file; - $rules = array_merge($rules, $version_rules); - } -} elseif (!file_exists(__DIR__ . "/compatibility-rules/studip-{$version}.php")) { - $log('#{red:No rules defined for Stud.IP version %s}', $version); - die; -} else { - $rules = require __DIR__ . "/compatibility-rules/studip-{$version}.php"; -} - -// Prepare folders -if (count($folders) === 0) { - $folders = rtrim($GLOBALS['STUDIP_BASE_PATH'], '/') . '/public/plugins_packages'; - $folders = glob($folders . '/*/*'); -} -$folders = array_unique($folders); - -$checkRule = function ($rule, $contents) { - if ($rule[0] === '/' && $rule[strlen($rule) - 1] === '/') { - return (bool) preg_match("{$rule}s", $contents); - } - - return strpos($contents, strtolower($rule)) > 0; -}; - -// Main checker -$check = function ($filename) use ($checkRule, $rules) { - $errors = []; - - $contents = strtolower(file_get_contents($filename)); - foreach ($rules as $needle => $suggestion) { - if ($checkRule($needle, $contents)) { - $errors[$needle] = $suggestion; - } - } - return $errors; -}; - -// Engage -foreach ($folders as $folder) { - if (!file_exists($folder) || !is_dir($folder)) { - $log_if($verbose, 'Skipping non-folder arg #{red:%s}', $folder); - continue; - } - - $log_if($verbose && !$only_filenames, '#{green:Scanning} %s', $reduce($folder)); - if ($recursive) { - $iterator = new RecursiveDirectoryIterator($folder, FilesystemIterator::FOLLOW_SYMLINKS | FilesystemIterator::UNIX_PATHS); - $iterator = new RecursiveIteratorIterator($iterator); - } else { - $iterator = new DirectoryIterator($folder); - } - $regexp_iterator = new RegexIterator($iterator, '/.*\.(?:php|tpl|inc|js)$/', RecursiveRegexIterator::MATCH); - - $issues = []; - - foreach ($regexp_iterator as $file) { - $filename = $file->getPathName(); - $log_if($verbose, "Checking #{magenta:%s}", $filename); - if ($errors = $check($filename)) { - $issues[$filename] = $errors; - } - } - - if (count($issues) > 0) { - $issue_count = array_sum(array_map('count', $issues)); - $message = count($issues) === 1 - ? '#{red:%u issue found in} #{red,bold:%s}' - : '#{red:%u issues found in} #{red,bold:%s}'; - $log_if(!$only_filenames, $message, $issue_count, $reduce($folder)); - - foreach ($issues as $filename => $errors) { - if ($only_filenames) { - $log($filename); - } else { - $log('> File #{green,bold:%s}', $reduce($filename)); - foreach ($errors as $needle => $suggestion) { - $log('- #{cyan:%s} -> %s', $needle, $suggestion ?: '#{red:No suggestion available}'); - } - } - } - } -} diff --git a/cli/studip_cli_env.inc.php b/cli/studip_cli_env.inc.php index a0e33cdede50803803c00f2387750377fbf32a14..a5ef8f46dd6a10e9978f82e362226446a8eb3ef1 100644 --- a/cli/studip_cli_env.inc.php +++ b/cli/studip_cli_env.inc.php @@ -48,7 +48,7 @@ function parse_msg_to_clean_text($long_msg,$separator="§") { return join("\n", $ret); } -$STUDIP_BASE_PATH = realpath( dirname(__FILE__) . '/..'); +$STUDIP_BASE_PATH = realpath( __DIR__ . '/..'); $include_path = get_include_path(); $include_path .= PATH_SEPARATOR . $STUDIP_BASE_PATH . DIRECTORY_SEPARATOR . 'public'; set_include_path($include_path); diff --git a/cli/tic_5671_scan.php b/cli/tic_5671_scan.php deleted file mode 100755 index 07fa5c2b943caa5ab91cd7f2bb5f2fc3b34769ce..0000000000000000000000000000000000000000 --- a/cli/tic_5671_scan.php +++ /dev/null @@ -1,172 +0,0 @@ -#!/usr/bin/env php -<?php -require_once 'studip_cli_env.inc.php'; - -$opts = getopt('fhnosv', ['filenames', 'help', 'non-recursive', 'occurences', 'matches', 'verbose']); - -if (isset($opts['h']) || isset($opts['help'])) { - fwrite(STDOUT, 'TIC 5671 Scanner - Scans files for occurences of globalized config items' . PHP_EOL); - fwrite(STDOUT, '========================================================================' . PHP_EOL); - fwrite(STDOUT, 'Usage: ' . basename(__FILE__) . ' [OPTION] [FOLDER] [FOLDER2] ..' . PHP_EOL); - fwrite(STDOUT, PHP_EOL); - fwrite(STDOUT, '[FOLDER] will default to Stud.IP base folder.' . PHP_EOL); - fwrite(STDOUT, 'Supply many folders if you need to.' . PHP_EOL); - fwrite(STDOUT, 'You may pass the special value of "plugins" to scan the plugin folder.' . PHP_EOL); - fwrite(STDOUT, PHP_EOL); - fwrite(STDOUT, 'Options:' . PHP_EOL); - fwrite(STDOUT, ' -h, --help Display this help' . PHP_EOL); - fwrite(STDOUT, ' -f, --filenames Display only filenames (excludes -m and -o)' . PHP_EOL); - fwrite(STDOUT, ' -n, --non-recursive Do not scan recursively into subfolders' . PHP_EOL); - fwrite(STDOUT, ' -m, --matches Show matched config variables' . PHP_EOL); - fwrite(STDOUT, ' -o, --occurences Display occurences in files (implies -s)' . PHP_EOL); - fwrite(STDOUT, ' -v, --verbose Print additional information' . PHP_EOL); - fwrite(STDOUT, PHP_EOL); - exit(0); -} - -// Reduce arguments by options (this is far from perfect) -$args = $_SERVER['argv']; -$arg_stop = array_search('--', $args); -if ($arg_stop !== false) { - $args = array_slice($args, $arg_stop + 1); -} elseif (count($opts)) { - $args = array_slice($args, 1 + count($opts)); -} else { - $args = array_slice($args, 1); -} - -$verbose = isset($opts['v']) || isset($opts['verbose']); -$only_filenames = isset($opts['f']) || isset($opts['filenames']); -$show_occurences = $verbose || isset($opts['o']) || isset($opts['occurences']); -$show_matches = $show_occurences || isset($opts['m']) || isset($opts['matches']); -$recursive = !(isset($opts['n']) || isset($opts['recursive'])); -$folders = $args ?: [$GLOBALS['STUDIP_BASE_PATH']]; - -// Prepare logging mechanism -$log = function ($message) { - $ansi = [ - 'off' => 0, - 'bold' => 1, - 'italic' => 3, - 'underline' => 4, - 'blink' => 5, - 'inverse' => 7, - 'hidden' => 8, - 'black' => 30, - 'red' => 31, - 'green' => 32, - 'yellow' => 33, - 'blue' => 34, - 'magenta' => 35, - 'cyan' => 36, - 'white' => 37, - 'black_bg' => 40, - 'red_bg' => 41, - 'green_bg' => 42, - 'yellow_bg' => 43, - 'blue_bg' => 44, - 'magenta_bg' => 45, - 'cyan_bg' => 46, - 'white_bg' => 47 - ]; - - $message = trim($message); - - if ($message) { - $ansi_codes = implode('|', array_keys($ansi)); - if (preg_match_all('/#\{((?:(?:' . $ansi_codes . '),?)+):(.+?)\}/s', $message, $matches, PREG_SET_ORDER)) { - foreach ($matches as $match) { - $chunk = ''; - $codes = explode(',', $match[1]); - foreach ($codes as $code) { - $chunk .= "\033[{$ansi[$code]}m"; - } - $chunk .= $match[2] . "\033[{$ansi[off]}m"; - - $message = str_replace($match[0], $chunk, $message); - } - } - - $args = array_slice(func_get_args(), 1); - vprintf($message . "\n", $args); - } -}; -$log_if = function ($condition, $message) use ($log) { - if ($condition) { - call_user_func_array($log, array_slice(func_get_args(), 1)); - } -}; - -// Prepare line highlighter -$highlight = function ($content, $variable) { - $lines = explode("\n", $content); - - $result = []; - foreach ($lines as $index => $line) { - if (mb_strpos($line, $variable) === false) { - continue; - } - $result[$index + 1] = $line; - } - - if (!$result) { - return ''; - } - - $max = max(array_map('mb_strlen', array_keys($result))); - - foreach ($result as $index => $line) { - $result[$index] = sprintf('#{yellow:%0' . $max . 'u}: %s', $index, str_replace($variable, "#{yellow_bg,black:$variable}", $line)); - } - - return implode("\n", $result); -}; - -// Prepare folders -foreach ($folders as $index => $folder) { - if ($folder === 'plugins') { - $folders[$index] = $GLOBALS['STUDIP_BASE_PATH'] . '/public/plugins_packages/'; - } -} -$folders = array_unique($folders); - -// Prepare regexp from regexp -$config = Config::get()->getFields('global'); -$quoted = array_map(function ($item) { return preg_quote($item, '/'); }, $config); -$regexp = '/\$(?:GLOBALS\[["\']?)?(' . implode('|', $quoted) . ')\b/S'; - -// Engage -foreach ($folders as $folder) { - if (!file_exists($folder) || !is_dir($folder)) { - $log_if($verbose, 'Skipping non-folder arg #{red:%s}', $folder); - continue; - } - $log_if($verbose, 'Scanning "%s"', $folder); - if ($recursive) { - $iterator = new RecursiveDirectoryIterator($folder, FilesystemIterator::FOLLOW_SYMLINKS | FilesystemIterator::UNIX_PATHS); - $iterator = new RecursiveIteratorIterator($iterator); - } else { - $iterator = new DirectoryIterator($folder); - } - $regexp_iterator = new RegexIterator($iterator, '/.*\.(?:php|tpl|inc)$/', RecursiveRegexIterator::MATCH); - - foreach ($regexp_iterator as $file) { - $filename = $file->getPathName(); - $contents = file_get_contents($filename); - $log_if($verbose, "Checking #{magenta:%s}", $filename); - if ($matched = preg_match_all($regexp, $contents, $matches)) { - if ($only_filenames) { - $log($filename); - } else { - $log('%u matched variable(s) in #{green,bold:%s}', $matched, $filename); - if ($show_matches) { - $variables = array_unique($matches[1]); - foreach ($variables as $variable) { - $log('>> #{cyan:%s}', $variable); - $log_if($show_occurences, $highlight($contents, $variable)); - } - } - } - } - } -} diff --git a/cli/update-resource-booking-intervals.php b/cli/update-resource-booking-intervals.php deleted file mode 100644 index 50c6e4b59277613b0e539ace9a2d18fb8e19a104..0000000000000000000000000000000000000000 --- a/cli/update-resource-booking-intervals.php +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env php -<?php - - -require_once(__DIR__ . '/studip_cli_env.inc.php'); - - -$keep_exceptions = true; - -$options = getopt('h', ['remove-exceptions']); - -if (array_key_exists('h', $options)) { - echo("Usage:\tupdate-resource-booking-intervals.php [--remove-exceptions]\n"); - echo("\tIf --remove-exceptions is set, exceptions for a booking with repetitions\n"); - echo("\twill be removed. By default, they are kept.\n"); - exit(0); -} - -if (array_key_exists('remove-exceptions', $options)) { - $keep_exceptions = false; - echo("Exceptions in bookings with repetitions will be removed!\n"); -} - -$bookings = ResourceBooking::findBySql('TRUE'); -if (!$bookings) { - echo("There are no bookings in your database! Nothing to do!\n"); - exit(0); -} -foreach ($bookings as $booking) { - $booking->updateIntervals($keep_exceptions); -} - -echo("End of script. The resource_booking_intervals table is up to date again!\n"); diff --git a/cli/vue-gettext-split-translations.php b/cli/vue-gettext-split-translations.php deleted file mode 100755 index 0ff09d10cf4cc365d9fd66ffc09d9bfc9afa45e3..0000000000000000000000000000000000000000 --- a/cli/vue-gettext-split-translations.php +++ /dev/null @@ -1,15 +0,0 @@ -<?php - -$translationsFile = realpath(__DIR__ . '/../resources/locales/translations.json'); -if (!file_exists($translationsFile)) { - fwrite(STDERR, "Could not find translations in '" . $translationsFile . "'.\n"); - exit(1); -} - -$file = file_get_contents($translationsFile); -$json = json_decode($file, true); - -foreach ($json as $lang => $content) { - $langFile = realpath(__DIR__ . '/../resources/locales/') . '/' . $lang . '.json'; - file_put_contents($langFile, json_encode($content)); -} diff --git a/composer.json b/composer.json index 50b3f8a6ec937cd12206e702991703900f633589..ba9f55ed8f91f27725ee503617571e8e73839e02 100644 --- a/composer.json +++ b/composer.json @@ -45,6 +45,8 @@ "opis/json-schema": "^1.0", "slim/psr7": "1.4", "slim/slim": "4.7.1", - "php-di/php-di": "6.3.4" + "php-di/php-di": "6.3.4", + "symfony/console": "5.3.6", + "symfony/process": "^5.4" } } diff --git a/composer.lock b/composer.lock index 440bb705a837ef291243ddaeb13a2e385cdf13cf..83fc9e51262501f07a3686237b78328422552b95 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "98af1effd6c2da72cc51ac552e851451", + "content-hash": "59006c71d43d6f32f0445636c803208d", "packages": [ { "name": "algo26-matthias/idna-convert", @@ -2254,41 +2254,61 @@ "time": "2018-09-13T19:25:26+00:00" }, { - "name": "symfony/polyfill-ctype", - "version": "v1.18.1", + "name": "symfony/console", + "version": "v5.3.6", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "1c302646f6efc070cd46856e600e5e0684d6b454" + "url": "https://github.com/symfony/console.git", + "reference": "51b71afd6d2dc8f5063199357b9880cea8d8bfe2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/1c302646f6efc070cd46856e600e5e0684d6b454", - "reference": "1c302646f6efc070cd46856e600e5e0684d6b454", + "url": "https://api.github.com/repos/symfony/console/zipball/51b71afd6d2dc8f5063199357b9880cea8d8bfe2", + "reference": "51b71afd6d2dc8f5063199357b9880cea8d8bfe2", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php73": "^1.8", + "symfony/polyfill-php80": "^1.16", + "symfony/service-contracts": "^1.1|^2", + "symfony/string": "^5.1" + }, + "conflict": { + "psr/log": ">=3", + "symfony/dependency-injection": "<4.4", + "symfony/dotenv": "<5.1", + "symfony/event-dispatcher": "<4.4", + "symfony/lock": "<4.4", + "symfony/process": "<4.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0" + }, + "require-dev": { + "psr/log": "^1|^2", + "symfony/config": "^4.4|^5.0", + "symfony/dependency-injection": "^4.4|^5.0", + "symfony/event-dispatcher": "^4.4|^5.0", + "symfony/lock": "^4.4|^5.0", + "symfony/process": "^4.4|^5.0", + "symfony/var-dumper": "^4.4|^5.0" }, "suggest": { - "ext-ctype": "For best performance" + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.18-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, "autoload": { "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" + "Symfony\\Component\\Console\\": "" }, - "files": [ - "bootstrap.php" + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -2297,24 +2317,24 @@ ], "authors": [ { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for ctype functions", + "description": "Eases the creation of beautiful and testable command line interfaces", "homepage": "https://symfony.com", "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" + "cli", + "command line", + "console", + "terminal" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.18.0" + "source": "https://github.com/symfony/console/tree/v5.3.6" }, "funding": [ { @@ -2330,40 +2350,38 @@ "type": "tidelift" } ], - "time": "2020-07-14T12:35:20+00:00" + "time": "2021-07-27T19:10:22+00:00" }, { - "name": "symfony/polyfill-mbstring", - "version": "v1.10.0", + "name": "symfony/deprecation-contracts", + "version": "v2.4.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "c79c051f5b3a46be09205c73b80b346e4153e494" + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/c79c051f5b3a46be09205c73b80b346e4153e494", - "reference": "c79c051f5b3a46be09205c73b80b346e4153e494", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5f38c8804a9e97d23e0c8d63341088cd8a22d627", + "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627", "shasum": "" }, "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-mbstring": "For best performance" + "php": ">=7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.9-dev" + "dev-main": "2.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - }, "files": [ - "bootstrap.php" + "function.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -2380,37 +2398,46 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for the Mbstring extension", + "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/master" + "source": "https://github.com/symfony/deprecation-contracts/tree/v2.4.0" }, - "time": "2018-09-21T13:07:52+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-03-23T23:28:01+00:00" }, { - "name": "symfony/polyfill-php56", + "name": "symfony/polyfill-ctype", "version": "v1.18.1", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php56.git", - "reference": "13df84e91cd168f247c2f2ec82cc0fa24901c011" + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "1c302646f6efc070cd46856e600e5e0684d6b454" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php56/zipball/13df84e91cd168f247c2f2ec82cc0fa24901c011", - "reference": "13df84e91cd168f247c2f2ec82cc0fa24901c011", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/1c302646f6efc070cd46856e600e5e0684d6b454", + "reference": "1c302646f6efc070cd46856e600e5e0684d6b454", "shasum": "" }, "require": { - "php": ">=5.3.3", - "symfony/polyfill-util": "~1.0" + "php": ">=5.3.3" + }, + "suggest": { + "ext-ctype": "For best performance" }, "type": "library", "extra": { @@ -2424,7 +2451,7 @@ }, "autoload": { "psr-4": { - "Symfony\\Polyfill\\Php56\\": "" + "Symfony\\Polyfill\\Ctype\\": "" }, "files": [ "bootstrap.php" @@ -2436,24 +2463,24 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 5.6+ features to lower PHP versions", + "description": "Symfony polyfill for ctype functions", "homepage": "https://symfony.com", "keywords": [ "compatibility", + "ctype", "polyfill", - "portable", - "shim" + "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-php56/tree/v1.18.1" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.18.0" }, "funding": [ { @@ -2472,22 +2499,25 @@ "time": "2020-07-14T12:35:20+00:00" }, { - "name": "symfony/polyfill-php80", + "name": "symfony/polyfill-intl-grapheme", "version": "v1.23.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "eca0bf41ed421bed1b57c4958bab16aa86b757d0" + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "24b72c6baa32c746a4d0840147c9715e42bb68ab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/eca0bf41ed421bed1b57c4958bab16aa86b757d0", - "reference": "eca0bf41ed421bed1b57c4958bab16aa86b757d0", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/24b72c6baa32c746a4d0840147c9715e42bb68ab", + "reference": "24b72c6baa32c746a4d0840147c9715e42bb68ab", "shasum": "" }, "require": { "php": ">=7.1" }, + "suggest": { + "ext-intl": "For best performance" + }, "type": "library", "extra": { "branch-alias": { @@ -2500,13 +2530,10 @@ }, "autoload": { "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" }, "files": [ "bootstrap.php" - ], - "classmap": [ - "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -2514,10 +2541,6 @@ "MIT" ], "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, { "name": "Nicolas Grekas", "email": "p@tchwork.com" @@ -2527,16 +2550,18 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "description": "Symfony polyfill for intl's grapheme_* functions", "homepage": "https://symfony.com", "keywords": [ "compatibility", + "grapheme", + "intl", "polyfill", "portable", "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.23.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.23.0" }, "funding": [ { @@ -2552,29 +2577,32 @@ "type": "tidelift" } ], - "time": "2021-02-19T12:13:01+00:00" + "time": "2021-05-27T09:17:38+00:00" }, { - "name": "symfony/polyfill-util", - "version": "v1.18.1", + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.23.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-util.git", - "reference": "46b910c71e9828f8ec2aa7a0314de1130d9b295a" + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-util/zipball/46b910c71e9828f8ec2aa7a0314de1130d9b295a", - "reference": "46b910c71e9828f8ec2aa7a0314de1130d9b295a", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8", + "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.18-dev" + "dev-main": "1.23-dev" }, "thanks": { "name": "symfony/polyfill", @@ -2583,8 +2611,14 @@ }, "autoload": { "psr-4": { - "Symfony\\Polyfill\\Util\\": "" - } + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2600,16 +2634,18 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony utilities for portability of PHP codes", + "description": "Symfony polyfill for intl's Normalizer class and related functions", "homepage": "https://symfony.com", "keywords": [ - "compat", "compatibility", + "intl", + "normalizer", "polyfill", + "portable", "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-util/tree/v1.18.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.23.0" }, "funding": [ { @@ -2625,42 +2661,40 @@ "type": "tidelift" } ], - "time": "2020-07-14T12:35:20+00:00" + "time": "2021-02-19T12:13:01+00:00" }, { - "name": "symfony/yaml", - "version": "v3.4.46", + "name": "symfony/polyfill-mbstring", + "version": "v1.10.0", "source": { "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "88289caa3c166321883f67fe5130188ebbb47094" + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "c79c051f5b3a46be09205c73b80b346e4153e494" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/88289caa3c166321883f67fe5130188ebbb47094", - "reference": "88289caa3c166321883f67fe5130188ebbb47094", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/c79c051f5b3a46be09205c73b80b346e4153e494", + "reference": "c79c051f5b3a46be09205c73b80b346e4153e494", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-ctype": "~1.8" - }, - "conflict": { - "symfony/console": "<3.4" - }, - "require-dev": { - "symfony/console": "~3.4|~4.0" + "php": ">=5.3.3" }, "suggest": { - "symfony/console": "For validating YAML files using the lint command" + "ext-mbstring": "For best performance" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.9-dev" + } + }, "autoload": { "psr-4": { - "Symfony\\Component\\Yaml\\": "" + "Symfony\\Polyfill\\Mbstring\\": "" }, - "exclude-from-classmap": [ - "/Tests/" + "files": [ + "bootstrap.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -2669,18 +2703,88 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Yaml Component", + "description": "Symfony polyfill for the Mbstring extension", "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], "support": { - "source": "https://github.com/symfony/yaml/tree/v3.4.46" + "source": "https://github.com/symfony/polyfill-mbstring/tree/master" + }, + "time": "2018-09-21T13:07:52+00:00" + }, + { + "name": "symfony/polyfill-php56", + "version": "v1.18.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php56.git", + "reference": "13df84e91cd168f247c2f2ec82cc0fa24901c011" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php56/zipball/13df84e91cd168f247c2f2ec82cc0fa24901c011", + "reference": "13df84e91cd168f247c2f2ec82cc0fa24901c011", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/polyfill-util": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.18-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php56\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 5.6+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php56/tree/v1.18.1" }, "funding": [ { @@ -2696,104 +2800,124 @@ "type": "tidelift" } ], - "time": "2020-10-24T10:57:07+00:00" + "time": "2020-07-14T12:35:20+00:00" }, { - "name": "tecnickcom/tcpdf", - "version": "6.3.5", + "name": "symfony/polyfill-php73", + "version": "v1.23.0", "source": { "type": "git", - "url": "https://github.com/tecnickcom/TCPDF.git", - "reference": "19a535eaa7fb1c1cac499109deeb1a7a201b4549" + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/19a535eaa7fb1c1cac499109deeb1a7a201b4549", - "reference": "19a535eaa7fb1c1cac499109deeb1a7a201b4549", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fba8933c384d6476ab14fb7b8526e5287ca7e010", + "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=7.1" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php73\\": "" + }, + "files": [ + "bootstrap.php" + ], "classmap": [ - "config", - "include", - "tcpdf.php", - "tcpdf_parser.php", - "tcpdf_import.php", - "tcpdf_barcodes_1d.php", - "tcpdf_barcodes_2d.php", - "include/tcpdf_colors.php", - "include/tcpdf_filters.php", - "include/tcpdf_font_data.php", - "include/tcpdf_fonts.php", - "include/tcpdf_images.php", - "include/tcpdf_static.php", - "include/barcodes/datamatrix.php", - "include/barcodes/pdf417.php", - "include/barcodes/qrcode.php" + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "LGPL-3.0-only" + "MIT" ], "authors": [ { - "name": "Nicola Asuni", - "email": "info@tecnick.com", - "role": "lead" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "TCPDF is a PHP class for generating PDF documents and barcodes.", - "homepage": "http://www.tcpdf.org/", + "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "homepage": "https://symfony.com", "keywords": [ - "PDFD32000-2008", - "TCPDF", - "barcodes", - "datamatrix", - "pdf", - "pdf417", - "qrcode" + "compatibility", + "polyfill", + "portable", + "shim" ], "support": { - "issues": "https://github.com/tecnickcom/TCPDF/issues", - "source": "https://github.com/tecnickcom/TCPDF/tree/6.3.5" + "source": "https://github.com/symfony/polyfill-php73/tree/v1.23.0" }, - "time": "2020-02-14T14:20:12+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-02-19T12:13:01+00:00" }, { - "name": "tuupola/callable-handler", - "version": "1.1.0", + "name": "symfony/polyfill-php80", + "version": "v1.23.0", "source": { "type": "git", - "url": "https://github.com/tuupola/callable-handler.git", - "reference": "0bc7b88630ca753de9aba8f411046856f5ca6f8c" + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "eca0bf41ed421bed1b57c4958bab16aa86b757d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tuupola/callable-handler/zipball/0bc7b88630ca753de9aba8f411046856f5ca6f8c", - "reference": "0bc7b88630ca753de9aba8f411046856f5ca6f8c", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/eca0bf41ed421bed1b57c4958bab16aa86b757d0", + "reference": "eca0bf41ed421bed1b57c4958bab16aa86b757d0", "shasum": "" }, "require": { - "php": "^7.1|^8.0", - "psr/http-server-middleware": "^1.0" - }, - "require-dev": { - "overtrue/phplint": "^1.0", - "phpunit/phpunit": "^7.0|^8.0|^9.0", - "squizlabs/php_codesniffer": "^3.2", - "tuupola/http-factory": "^0.4.0|^1.0", - "zendframework/zend-diactoros": "^1.6.0|^2.0" + "php": ">=7.1" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, "autoload": { "psr-4": { - "Tuupola\\Middleware\\": "src" - } + "Symfony\\Polyfill\\Php80\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2801,65 +2925,75 @@ ], "authors": [ { - "name": "Mika Tuupola", - "email": "tuupola@appelsiini.net", - "homepage": "https://appelsiini.net/", - "role": "Developer" + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Compatibility layer for PSR-7 double pass and PSR-15 middlewares.", - "homepage": "https://github.com/tuupola/callable-handler", + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", "keywords": [ - "middleware", - "psr-15", - "psr-7" + "compatibility", + "polyfill", + "portable", + "shim" ], "support": { - "issues": "https://github.com/tuupola/callable-handler/issues", - "source": "https://github.com/tuupola/callable-handler/tree/1.1.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.23.0" }, "funding": [ { - "url": "https://github.com/tuupola", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2020-09-09T08:31:54+00:00" + "time": "2021-02-19T12:13:01+00:00" }, { - "name": "tuupola/cors-middleware", - "version": "1.2.1", + "name": "symfony/polyfill-util", + "version": "v1.18.1", "source": { "type": "git", - "url": "https://github.com/tuupola/cors-middleware.git", - "reference": "4f085d11f349e83d18f1eb5802551353b2b093a3" + "url": "https://github.com/symfony/polyfill-util.git", + "reference": "46b910c71e9828f8ec2aa7a0314de1130d9b295a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tuupola/cors-middleware/zipball/4f085d11f349e83d18f1eb5802551353b2b093a3", - "reference": "4f085d11f349e83d18f1eb5802551353b2b093a3", + "url": "https://api.github.com/repos/symfony/polyfill-util/zipball/46b910c71e9828f8ec2aa7a0314de1130d9b295a", + "reference": "46b910c71e9828f8ec2aa7a0314de1130d9b295a", "shasum": "" }, "require": { - "neomerx/cors-psr7": "^1.0.4", - "php": "^7.1|^8.0", - "psr/http-message": "^1.0.1", - "psr/http-server-middleware": "^1.0", - "tuupola/callable-handler": "^1.0", - "tuupola/http-factory": "^1.0.2" - }, - "require-dev": { - "equip/dispatch": "^2.0", - "overtrue/phplint": "^1.0", - "phpstan/phpstan": "^0.12.42", - "phpunit/phpunit": "^7.0|^8.0|^9.0", - "squizlabs/php_codesniffer": "^3.5", - "zendframework/zend-diactoros": "^1.0|^2.0" + "php": ">=5.3.3" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.18-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, "autoload": { "psr-4": { - "Tuupola\\Middleware\\": "src" + "Symfony\\Polyfill\\Util\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -2868,67 +3002,67 @@ ], "authors": [ { - "name": "Mika Tuupola", - "email": "tuupola@appelsiini.net", - "homepage": "https://appelsiini.net/", - "role": "Developer" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "PSR-7 and PSR-15 CORS middleware", - "homepage": "https://github.com/tuupola/cors-middleware", + "description": "Symfony utilities for portability of PHP codes", + "homepage": "https://symfony.com", "keywords": [ - "cors", - "middleware", - "psr-15", - "psr-7" + "compat", + "compatibility", + "polyfill", + "shim" ], "support": { - "issues": "https://github.com/tuupola/cors-middleware/issues", - "source": "https://github.com/tuupola/cors-middleware/tree/1.2.1" + "source": "https://github.com/symfony/polyfill-util/tree/v1.18.0" }, "funding": [ { - "url": "https://github.com/tuupola", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2020-10-29T11:01:06+00:00" + "time": "2020-07-14T12:35:20+00:00" }, { - "name": "tuupola/http-factory", - "version": "1.3.0", + "name": "symfony/process", + "version": "v5.4.0", "source": { "type": "git", - "url": "https://github.com/tuupola/http-factory.git", - "reference": "aa48841a9f572b9cebe9d3ac5d5d3362a83f57ac" + "url": "https://github.com/symfony/process.git", + "reference": "5be20b3830f726e019162b26223110c8f47cf274" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tuupola/http-factory/zipball/aa48841a9f572b9cebe9d3ac5d5d3362a83f57ac", - "reference": "aa48841a9f572b9cebe9d3ac5d5d3362a83f57ac", + "url": "https://api.github.com/repos/symfony/process/zipball/5be20b3830f726e019162b26223110c8f47cf274", + "reference": "5be20b3830f726e019162b26223110c8f47cf274", "shasum": "" }, "require": { - "php": "^7.1|^8.0", - "psr/http-factory": "^1.0" - }, - "conflict": { - "nyholm/psr7": "<1.0" - }, - "provide": { - "psr/http-factory-implementation": "^1.0" - }, - "require-dev": { - "http-interop/http-factory-tests": "^0.7.0", - "overtrue/phplint": "^1.0", - "phpunit/phpunit": "^7.0|^8.0|^9.0", - "squizlabs/php_codesniffer": "^3.0" + "php": ">=7.2.5", + "symfony/polyfill-php80": "^1.16" }, "type": "library", "autoload": { "psr-4": { - "Tuupola\\Http\\Factory\\": "src" - } + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2936,61 +3070,69 @@ ], "authors": [ { - "name": "Mika Tuupola", - "email": "tuupola@appelsiini.net", - "homepage": "https://appelsiini.net/", - "role": "Developer" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Lightweight autodiscovering PSR-17 HTTP factories", - "homepage": "https://github.com/tuupola/http-factory", - "keywords": [ - "http", - "psr-17", - "psr-7" - ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", "support": { - "issues": "https://github.com/tuupola/http-factory/issues", - "source": "https://github.com/tuupola/http-factory/tree/1.3.0" + "source": "https://github.com/symfony/process/tree/v5.4.0" }, "funding": [ { - "url": "https://github.com/tuupola", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2020-10-01T07:46:32+00:00" - } - ], - "packages-dev": [ + "time": "2021-11-28T15:25:38+00:00" + }, { - "name": "adlawson/vfs", - "version": "0.12.1", + "name": "symfony/service-contracts", + "version": "v2.2.0", "source": { "type": "git", - "url": "https://github.com/adlawson/php-vfs.git", - "reference": "e955034419d6a8f92c9a8ea2e626eeed96b41095" + "url": "https://github.com/symfony/service-contracts.git", + "reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/adlawson/php-vfs/zipball/e955034419d6a8f92c9a8ea2e626eeed96b41095", - "reference": "e955034419d6a8f92c9a8ea2e626eeed96b41095", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d15da7ba4957ffb8f1747218be9e1a121fd298a1", + "reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1", "shasum": "" }, "require": { - "php": ">=5.5", - "psr/log": "^1.0" + "php": ">=7.2.5", + "psr/container": "^1.0" }, - "require-dev": { - "adlawson/timezone": "^1.0", - "fabpot/php-cs-fixer": "^1.9", - "mockery/mockery": "^0.9", - "phpunit/phpunit": "^4.7" + "suggest": { + "symfony/service-implementation": "" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, "autoload": { "psr-4": { - "Vfs\\": "src/" + "Symfony\\Contracts\\Service\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -2999,66 +3141,82 @@ ], "authors": [ { - "name": "Andrew Lawson", - "homepage": "http://adlawson.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Virtual file system", - "homepage": "https://github.com/adlawson/php-vfs", + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", "keywords": [ - "dir", - "directory", - "file", - "fs", - "read", - "stream", - "system", - "virtual", - "wrapper", - "write" + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" ], "support": { - "issues": "https://github.com/adlawson/php-vfs/issues", - "source": "https://github.com/adlawson/php-vfs/tree/develop" + "source": "https://github.com/symfony/service-contracts/tree/master" }, - "time": "2016-02-20T12:46:01+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-09-07T11:33:47+00:00" }, { - "name": "behat/gherkin", - "version": "v4.8.0", + "name": "symfony/string", + "version": "v5.3.2", "source": { "type": "git", - "url": "https://github.com/Behat/Gherkin.git", - "reference": "2391482cd003dfdc36b679b27e9f5326bd656acd" + "url": "https://github.com/symfony/string.git", + "reference": "0732e97e41c0a590f77e231afc16a327375d50b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Gherkin/zipball/2391482cd003dfdc36b679b27e9f5326bd656acd", - "reference": "2391482cd003dfdc36b679b27e9f5326bd656acd", + "url": "https://api.github.com/repos/symfony/string/zipball/0732e97e41c0a590f77e231afc16a327375d50b0", + "reference": "0732e97e41c0a590f77e231afc16a327375d50b0", "shasum": "" }, "require": { - "php": "~7.2|~8.0" + "php": ">=7.2.5", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php80": "~1.15" }, "require-dev": { - "cucumber/cucumber": "dev-gherkin-16.0.0", - "phpunit/phpunit": "~8|~9", - "symfony/phpunit-bridge": "~3|~4|~5", - "symfony/yaml": "~3|~4|~5" - }, - "suggest": { - "symfony/yaml": "If you want to parse features, represented in YAML files" + "symfony/error-handler": "^4.4|^5.0", + "symfony/http-client": "^4.4|^5.0", + "symfony/translation-contracts": "^1.1|^2", + "symfony/var-exporter": "^4.4|^5.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.4-dev" - } - }, "autoload": { - "psr-0": { - "Behat\\Gherkin": "src/" - } + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "files": [ + "Resources/functions.php" + ], + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3066,49 +3224,78 @@ ], "authors": [ { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Gherkin DSL parser for PHP", - "homepage": "http://behat.org/", + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", "keywords": [ - "BDD", - "Behat", - "Cucumber", - "DSL", - "gherkin", - "parser" + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" ], "support": { - "issues": "https://github.com/Behat/Gherkin/issues", - "source": "https://github.com/Behat/Gherkin/tree/v4.8.0" + "source": "https://github.com/symfony/string/tree/v5.3.2" }, - "time": "2021-02-04T12:44:21+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-06-06T09:51:56+00:00" }, { - "name": "camspiers/json-pretty", - "version": "1.0.2", + "name": "symfony/yaml", + "version": "v3.4.46", "source": { "type": "git", - "url": "https://github.com/camspiers/json-pretty.git", - "reference": "17be37cb83af8014658da48fa0012604179039a7" + "url": "https://github.com/symfony/yaml.git", + "reference": "88289caa3c166321883f67fe5130188ebbb47094" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/camspiers/json-pretty/zipball/17be37cb83af8014658da48fa0012604179039a7", - "reference": "17be37cb83af8014658da48fa0012604179039a7", + "url": "https://api.github.com/repos/symfony/yaml/zipball/88289caa3c166321883f67fe5130188ebbb47094", + "reference": "88289caa3c166321883f67fe5130188ebbb47094", "shasum": "" }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/console": "<3.4" + }, "require-dev": { - "phpunit/phpunit": "~4.0" + "symfony/console": "~3.4|~4.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" }, "type": "library", "autoload": { - "psr-0": { - "Camspiers": "src/" - } + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3116,144 +3303,130 @@ ], "authors": [ { - "name": "Cam Spiers", - "email": "cameron@heyday.co.nz" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Provides support for json pretty printing", + "description": "Symfony Yaml Component", + "homepage": "https://symfony.com", "support": { - "issues": "https://github.com/camspiers/json-pretty/issues", - "source": "https://github.com/camspiers/json-pretty/tree/master" + "source": "https://github.com/symfony/yaml/tree/v3.4.46" }, - "time": "2016-02-06T01:25:58+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-10-24T10:57:07+00:00" }, { - "name": "clue/stream-filter", - "version": "v1.4.1", + "name": "tecnickcom/tcpdf", + "version": "6.3.5", "source": { "type": "git", - "url": "https://github.com/clue/stream-filter.git", - "reference": "5a58cc30a8bd6a4eb8f856adf61dd3e013f53f71" + "url": "https://github.com/tecnickcom/TCPDF.git", + "reference": "19a535eaa7fb1c1cac499109deeb1a7a201b4549" }, "dist": { - "type": "zip", - "url": "https://api.github.com/repos/clue/stream-filter/zipball/5a58cc30a8bd6a4eb8f856adf61dd3e013f53f71", - "reference": "5a58cc30a8bd6a4eb8f856adf61dd3e013f53f71", + "type": "zip", + "url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/19a535eaa7fb1c1cac499109deeb1a7a201b4549", + "reference": "19a535eaa7fb1c1cac499109deeb1a7a201b4549", "shasum": "" }, "require": { - "php": ">=5.3" - }, - "require-dev": { - "phpunit/phpunit": "^5.0 || ^4.8" + "php": ">=5.3.0" }, "type": "library", "autoload": { - "psr-4": { - "Clue\\StreamFilter\\": "src/" - }, - "files": [ - "src/functions_include.php" + "classmap": [ + "config", + "include", + "tcpdf.php", + "tcpdf_parser.php", + "tcpdf_import.php", + "tcpdf_barcodes_1d.php", + "tcpdf_barcodes_2d.php", + "include/tcpdf_colors.php", + "include/tcpdf_filters.php", + "include/tcpdf_font_data.php", + "include/tcpdf_fonts.php", + "include/tcpdf_images.php", + "include/tcpdf_static.php", + "include/barcodes/datamatrix.php", + "include/barcodes/pdf417.php", + "include/barcodes/qrcode.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "LGPL-3.0-only" ], "authors": [ { - "name": "Christian Lück", - "email": "christian@lueck.tv" + "name": "Nicola Asuni", + "email": "info@tecnick.com", + "role": "lead" } ], - "description": "A simple and modern approach to stream filtering in PHP", - "homepage": "https://github.com/clue/php-stream-filter", + "description": "TCPDF is a PHP class for generating PDF documents and barcodes.", + "homepage": "http://www.tcpdf.org/", "keywords": [ - "bucket brigade", - "callback", - "filter", - "php_user_filter", - "stream", - "stream_filter_append", - "stream_filter_register" + "PDFD32000-2008", + "TCPDF", + "barcodes", + "datamatrix", + "pdf", + "pdf417", + "qrcode" ], "support": { - "issues": "https://github.com/clue/stream-filter/issues", - "source": "https://github.com/clue/stream-filter/tree/v1.4.1" + "issues": "https://github.com/tecnickcom/TCPDF/issues", + "source": "https://github.com/tecnickcom/TCPDF/tree/6.3.5" }, - "funding": [ - { - "url": "https://clue.engineering/support", - "type": "custom" - }, - { - "url": "https://github.com/clue", - "type": "github" - } - ], - "time": "2019-04-09T12:31:48+00:00" + "time": "2020-02-14T14:20:12+00:00" }, { - "name": "codeception/codeception", - "version": "4.1.21", + "name": "tuupola/callable-handler", + "version": "1.1.0", "source": { "type": "git", - "url": "https://github.com/Codeception/Codeception.git", - "reference": "c25f20d842a7e3fa0a8e6abf0828f102c914d419" + "url": "https://github.com/tuupola/callable-handler.git", + "reference": "0bc7b88630ca753de9aba8f411046856f5ca6f8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Codeception/zipball/c25f20d842a7e3fa0a8e6abf0828f102c914d419", - "reference": "c25f20d842a7e3fa0a8e6abf0828f102c914d419", + "url": "https://api.github.com/repos/tuupola/callable-handler/zipball/0bc7b88630ca753de9aba8f411046856f5ca6f8c", + "reference": "0bc7b88630ca753de9aba8f411046856f5ca6f8c", "shasum": "" }, "require": { - "behat/gherkin": "^4.4.0", - "codeception/lib-asserts": "^1.0", - "codeception/phpunit-wrapper": ">6.0.15 <6.1.0 | ^6.6.1 | ^7.7.1 | ^8.1.1 | ^9.0", - "codeception/stub": "^2.0 | ^3.0", - "ext-curl": "*", - "ext-json": "*", - "ext-mbstring": "*", - "guzzlehttp/psr7": "~1.4", - "php": ">=5.6.0 <9.0", - "symfony/console": ">=2.7 <6.0", - "symfony/css-selector": ">=2.7 <6.0", - "symfony/event-dispatcher": ">=2.7 <6.0", - "symfony/finder": ">=2.7 <6.0", - "symfony/yaml": ">=2.7 <6.0" + "php": "^7.1|^8.0", + "psr/http-server-middleware": "^1.0" }, "require-dev": { - "codeception/module-asserts": "1.*@dev", - "codeception/module-cli": "1.*@dev", - "codeception/module-db": "1.*@dev", - "codeception/module-filesystem": "1.*@dev", - "codeception/module-phpbrowser": "1.*@dev", - "codeception/specify": "~0.3", - "codeception/util-universalframework": "*@dev", - "monolog/monolog": "~1.8", - "squizlabs/php_codesniffer": "~2.0", - "symfony/process": ">=2.7 <6.0", - "vlucas/phpdotenv": "^2.0 | ^3.0 | ^4.0 | ^5.0" - }, - "suggest": { - "codeception/specify": "BDD-style code blocks", - "codeception/verify": "BDD-style assertions", - "hoa/console": "For interactive console functionality", - "stecman/symfony-console-completion": "For BASH autocompletion", - "symfony/phpunit-bridge": "For phpunit-bridge support" + "overtrue/phplint": "^1.0", + "phpunit/phpunit": "^7.0|^8.0|^9.0", + "squizlabs/php_codesniffer": "^3.2", + "tuupola/http-factory": "^0.4.0|^1.0", + "zendframework/zend-diactoros": "^1.6.0|^2.0" }, - "bin": [ - "codecept" - ], "type": "library", - "extra": { - "branch-alias": [] - }, "autoload": { "psr-4": { - "Codeception\\": "src/Codeception", - "Codeception\\Extension\\": "ext" + "Tuupola\\Middleware\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -3262,56 +3435,66 @@ ], "authors": [ { - "name": "Michael Bodnarchuk", - "email": "davert@mail.ua", - "homepage": "http://codegyre.com" + "name": "Mika Tuupola", + "email": "tuupola@appelsiini.net", + "homepage": "https://appelsiini.net/", + "role": "Developer" } ], - "description": "BDD-style testing framework", - "homepage": "http://codeception.com/", + "description": "Compatibility layer for PSR-7 double pass and PSR-15 middlewares.", + "homepage": "https://github.com/tuupola/callable-handler", "keywords": [ - "BDD", - "TDD", - "acceptance testing", - "functional testing", - "unit testing" + "middleware", + "psr-15", + "psr-7" ], "support": { - "issues": "https://github.com/Codeception/Codeception/issues", - "source": "https://github.com/Codeception/Codeception/tree/4.1.21" + "issues": "https://github.com/tuupola/callable-handler/issues", + "source": "https://github.com/tuupola/callable-handler/tree/1.1.0" }, "funding": [ { - "url": "https://opencollective.com/codeception", - "type": "open_collective" + "url": "https://github.com/tuupola", + "type": "github" } ], - "time": "2021-05-28T17:43:39+00:00" + "time": "2020-09-09T08:31:54+00:00" }, { - "name": "codeception/lib-asserts", - "version": "1.13.2", + "name": "tuupola/cors-middleware", + "version": "1.2.1", "source": { "type": "git", - "url": "https://github.com/Codeception/lib-asserts.git", - "reference": "184231d5eab66bc69afd6b9429344d80c67a33b6" + "url": "https://github.com/tuupola/cors-middleware.git", + "reference": "4f085d11f349e83d18f1eb5802551353b2b093a3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/lib-asserts/zipball/184231d5eab66bc69afd6b9429344d80c67a33b6", - "reference": "184231d5eab66bc69afd6b9429344d80c67a33b6", + "url": "https://api.github.com/repos/tuupola/cors-middleware/zipball/4f085d11f349e83d18f1eb5802551353b2b093a3", + "reference": "4f085d11f349e83d18f1eb5802551353b2b093a3", "shasum": "" }, "require": { - "codeception/phpunit-wrapper": ">6.0.15 <6.1.0 | ^6.6.1 | ^7.7.1 | ^8.0.3 | ^9.0", - "ext-dom": "*", - "php": ">=5.6.0 <9.0" + "neomerx/cors-psr7": "^1.0.4", + "php": "^7.1|^8.0", + "psr/http-message": "^1.0.1", + "psr/http-server-middleware": "^1.0", + "tuupola/callable-handler": "^1.0", + "tuupola/http-factory": "^1.0.2" + }, + "require-dev": { + "equip/dispatch": "^2.0", + "overtrue/phplint": "^1.0", + "phpstan/phpstan": "^0.12.42", + "phpunit/phpunit": "^7.0|^8.0|^9.0", + "squizlabs/php_codesniffer": "^3.5", + "zendframework/zend-diactoros": "^1.0|^2.0" }, "type": "library", "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Tuupola\\Middleware\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3319,56 +3502,67 @@ ], "authors": [ { - "name": "Michael Bodnarchuk", - "email": "davert@mail.ua", - "homepage": "http://codegyre.com" - }, - { - "name": "Gintautas Miselis" - }, - { - "name": "Gustavo Nieves", - "homepage": "https://medium.com/@ganieves" + "name": "Mika Tuupola", + "email": "tuupola@appelsiini.net", + "homepage": "https://appelsiini.net/", + "role": "Developer" } ], - "description": "Assertion methods used by Codeception core and Asserts module", - "homepage": "https://codeception.com/", + "description": "PSR-7 and PSR-15 CORS middleware", + "homepage": "https://github.com/tuupola/cors-middleware", "keywords": [ - "codeception" + "cors", + "middleware", + "psr-15", + "psr-7" ], "support": { - "issues": "https://github.com/Codeception/lib-asserts/issues", - "source": "https://github.com/Codeception/lib-asserts/tree/1.13.2" + "issues": "https://github.com/tuupola/cors-middleware/issues", + "source": "https://github.com/tuupola/cors-middleware/tree/1.2.1" }, - "time": "2020-10-21T16:26:20+00:00" + "funding": [ + { + "url": "https://github.com/tuupola", + "type": "github" + } + ], + "time": "2020-10-29T11:01:06+00:00" }, { - "name": "codeception/module-asserts", - "version": "1.3.1", + "name": "tuupola/http-factory", + "version": "1.3.0", "source": { "type": "git", - "url": "https://github.com/Codeception/module-asserts.git", - "reference": "59374f2fef0cabb9e8ddb53277e85cdca74328de" + "url": "https://github.com/tuupola/http-factory.git", + "reference": "aa48841a9f572b9cebe9d3ac5d5d3362a83f57ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/module-asserts/zipball/59374f2fef0cabb9e8ddb53277e85cdca74328de", - "reference": "59374f2fef0cabb9e8ddb53277e85cdca74328de", + "url": "https://api.github.com/repos/tuupola/http-factory/zipball/aa48841a9f572b9cebe9d3ac5d5d3362a83f57ac", + "reference": "aa48841a9f572b9cebe9d3ac5d5d3362a83f57ac", "shasum": "" }, "require": { - "codeception/codeception": "*@dev", - "codeception/lib-asserts": "^1.13.1", - "php": ">=5.6.0 <9.0" + "php": "^7.1|^8.0", + "psr/http-factory": "^1.0" }, "conflict": { - "codeception/codeception": "<4.0" + "nyholm/psr7": "<1.0" + }, + "provide": { + "psr/http-factory-implementation": "^1.0" + }, + "require-dev": { + "http-interop/http-factory-tests": "^0.7.0", + "overtrue/phplint": "^1.0", + "phpunit/phpunit": "^7.0|^8.0|^9.0", + "squizlabs/php_codesniffer": "^3.0" }, "type": "library", "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Tuupola\\Http\\Factory\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3376,58 +3570,61 @@ ], "authors": [ { - "name": "Michael Bodnarchuk" - }, - { - "name": "Gintautas Miselis" - }, - { - "name": "Gustavo Nieves", - "homepage": "https://medium.com/@ganieves" + "name": "Mika Tuupola", + "email": "tuupola@appelsiini.net", + "homepage": "https://appelsiini.net/", + "role": "Developer" } ], - "description": "Codeception module containing various assertions", - "homepage": "https://codeception.com/", + "description": "Lightweight autodiscovering PSR-17 HTTP factories", + "homepage": "https://github.com/tuupola/http-factory", "keywords": [ - "assertions", - "asserts", - "codeception" + "http", + "psr-17", + "psr-7" ], "support": { - "issues": "https://github.com/Codeception/module-asserts/issues", - "source": "https://github.com/Codeception/module-asserts/tree/1.3.1" + "issues": "https://github.com/tuupola/http-factory/issues", + "source": "https://github.com/tuupola/http-factory/tree/1.3.0" }, - "time": "2020-10-21T16:48:15+00:00" - }, + "funding": [ + { + "url": "https://github.com/tuupola", + "type": "github" + } + ], + "time": "2020-10-01T07:46:32+00:00" + } + ], + "packages-dev": [ { - "name": "codeception/phpunit-wrapper", - "version": "8.1.4", + "name": "adlawson/vfs", + "version": "0.12.1", "source": { "type": "git", - "url": "https://github.com/Codeception/phpunit-wrapper.git", - "reference": "f41335f0b4dd17cf7bbc63e87943b3ae72a8bbc3" + "url": "https://github.com/adlawson/php-vfs.git", + "reference": "e955034419d6a8f92c9a8ea2e626eeed96b41095" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/f41335f0b4dd17cf7bbc63e87943b3ae72a8bbc3", - "reference": "f41335f0b4dd17cf7bbc63e87943b3ae72a8bbc3", + "url": "https://api.github.com/repos/adlawson/php-vfs/zipball/e955034419d6a8f92c9a8ea2e626eeed96b41095", + "reference": "e955034419d6a8f92c9a8ea2e626eeed96b41095", "shasum": "" }, "require": { - "php": ">=7.2", - "phpunit/php-code-coverage": "^7.0", - "phpunit/phpunit": "^8.0", - "sebastian/comparator": "^3.0", - "sebastian/diff": "^3.0" + "php": ">=5.5", + "psr/log": "^1.0" }, "require-dev": { - "codeception/specify": "*", - "vlucas/phpdotenv": "^3.0" + "adlawson/timezone": "^1.0", + "fabpot/php-cs-fixer": "^1.9", + "mockery/mockery": "^0.9", + "phpunit/phpunit": "^4.7" }, "type": "library", "autoload": { "psr-4": { - "Codeception\\PHPUnit\\": "src/" + "Vfs\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3436,81 +3633,115 @@ ], "authors": [ { - "name": "Davert", - "email": "davert.php@resend.cc" + "name": "Andrew Lawson", + "homepage": "http://adlawson.com" } ], - "description": "PHPUnit classes used by Codeception", + "description": "Virtual file system", + "homepage": "https://github.com/adlawson/php-vfs", + "keywords": [ + "dir", + "directory", + "file", + "fs", + "read", + "stream", + "system", + "virtual", + "wrapper", + "write" + ], "support": { - "issues": "https://github.com/Codeception/phpunit-wrapper/issues", - "source": "https://github.com/Codeception/phpunit-wrapper/tree/8.1.4" + "issues": "https://github.com/adlawson/php-vfs/issues", + "source": "https://github.com/adlawson/php-vfs/tree/develop" }, - "time": "2020-12-28T14:00:08+00:00" + "time": "2016-02-20T12:46:01+00:00" }, { - "name": "codeception/stub", - "version": "3.7.0", + "name": "behat/gherkin", + "version": "v4.8.0", "source": { "type": "git", - "url": "https://github.com/Codeception/Stub.git", - "reference": "468dd5fe659f131fc997f5196aad87512f9b1304" + "url": "https://github.com/Behat/Gherkin.git", + "reference": "2391482cd003dfdc36b679b27e9f5326bd656acd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Stub/zipball/468dd5fe659f131fc997f5196aad87512f9b1304", - "reference": "468dd5fe659f131fc997f5196aad87512f9b1304", + "url": "https://api.github.com/repos/Behat/Gherkin/zipball/2391482cd003dfdc36b679b27e9f5326bd656acd", + "reference": "2391482cd003dfdc36b679b27e9f5326bd656acd", "shasum": "" }, "require": { - "phpunit/phpunit": "^8.4 | ^9.0" + "php": "~7.2|~8.0" + }, + "require-dev": { + "cucumber/cucumber": "dev-gherkin-16.0.0", + "phpunit/phpunit": "~8|~9", + "symfony/phpunit-bridge": "~3|~4|~5", + "symfony/yaml": "~3|~4|~5" + }, + "suggest": { + "symfony/yaml": "If you want to parse features, represented in YAML files" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.4-dev" + } + }, "autoload": { - "psr-4": { - "Codeception\\": "src/" + "psr-0": { + "Behat\\Gherkin": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "Flexible Stub wrapper for PHPUnit's Mock Builder", + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + } + ], + "description": "Gherkin DSL parser for PHP", + "homepage": "http://behat.org/", + "keywords": [ + "BDD", + "Behat", + "Cucumber", + "DSL", + "gherkin", + "parser" + ], "support": { - "issues": "https://github.com/Codeception/Stub/issues", - "source": "https://github.com/Codeception/Stub/tree/3.7.0" + "issues": "https://github.com/Behat/Gherkin/issues", + "source": "https://github.com/Behat/Gherkin/tree/v4.8.0" }, - "time": "2020-07-03T15:54:43+00:00" + "time": "2021-02-04T12:44:21+00:00" }, { - "name": "doctrine/instantiator", - "version": "1.4.0", + "name": "camspiers/json-pretty", + "version": "1.0.2", "source": { "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b" + "url": "https://github.com/camspiers/json-pretty.git", + "reference": "17be37cb83af8014658da48fa0012604179039a7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/d56bf6102915de5702778fe20f2de3b2fe570b5b", - "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b", + "url": "https://api.github.com/repos/camspiers/json-pretty/zipball/17be37cb83af8014658da48fa0012604179039a7", + "reference": "17be37cb83af8014658da48fa0012604179039a7", "shasum": "" }, - "require": { - "php": "^7.1 || ^8.0" - }, "require-dev": { - "doctrine/coding-standard": "^8.0", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^0.13 || 1.0.0-alpha2", - "phpstan/phpstan": "^0.12", - "phpstan/phpstan-phpunit": "^0.12", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "~4.0" }, "type": "library", "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + "psr-0": { + "Camspiers": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3519,94 +3750,45 @@ ], "authors": [ { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "https://ocramius.github.io/" + "name": "Cam Spiers", + "email": "cameron@heyday.co.nz" } ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", - "keywords": [ - "constructor", - "instantiate" - ], + "description": "Provides support for json pretty printing", "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.4.0" + "issues": "https://github.com/camspiers/json-pretty/issues", + "source": "https://github.com/camspiers/json-pretty/tree/master" }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", - "type": "tidelift" - } - ], - "time": "2020-11-10T18:47:58+00:00" + "time": "2016-02-06T01:25:58+00:00" }, { - "name": "monolog/monolog", - "version": "1.21.0", + "name": "clue/stream-filter", + "version": "v1.4.1", "source": { "type": "git", - "url": "https://github.com/Seldaek/monolog.git", - "reference": "f42fbdfd53e306bda545845e4dbfd3e72edb4952" + "url": "https://github.com/clue/stream-filter.git", + "reference": "5a58cc30a8bd6a4eb8f856adf61dd3e013f53f71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/f42fbdfd53e306bda545845e4dbfd3e72edb4952", - "reference": "f42fbdfd53e306bda545845e4dbfd3e72edb4952", + "url": "https://api.github.com/repos/clue/stream-filter/zipball/5a58cc30a8bd6a4eb8f856adf61dd3e013f53f71", + "reference": "5a58cc30a8bd6a4eb8f856adf61dd3e013f53f71", "shasum": "" }, "require": { - "php": ">=5.3.0", - "psr/log": "~1.0" - }, - "provide": { - "psr/log-implementation": "1.0.0" - }, - "require-dev": { - "aws/aws-sdk-php": "^2.4.9", - "doctrine/couchdb": "~1.0@dev", - "graylog2/gelf-php": "~1.0", - "jakub-onderka/php-parallel-lint": "0.9", - "php-amqplib/php-amqplib": "~2.4", - "php-console/php-console": "^3.1.3", - "phpunit/phpunit": "~4.5", - "phpunit/phpunit-mock-objects": "2.3.0", - "ruflin/elastica": ">=0.90 <3.0", - "sentry/sentry": "^0.13", - "swiftmailer/swiftmailer": "~5.3" - }, - "suggest": { - "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", - "doctrine/couchdb": "Allow sending log messages to a CouchDB server", - "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", - "ext-mongo": "Allow sending log messages to a MongoDB server", - "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", - "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", - "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", - "php-console/php-console": "Allow sending log messages to Google Chrome", - "rollbar/rollbar": "Allow sending log messages to Rollbar", - "ruflin/elastica": "Allow sending log messages to an Elastic Search server", - "sentry/sentry": "Allow sending log messages to a Sentry server" + "php": ">=5.3" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } + "require-dev": { + "phpunit/phpunit": "^5.0 || ^4.8" }, + "type": "library", "autoload": { "psr-4": { - "Monolog\\": "src/Monolog" - } + "Clue\\StreamFilter\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3614,109 +3796,152 @@ ], "authors": [ { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" + "name": "Christian Lück", + "email": "christian@lueck.tv" } ], - "description": "Sends your logs to files, sockets, inboxes, databases and various web services", - "homepage": "http://github.com/Seldaek/monolog", + "description": "A simple and modern approach to stream filtering in PHP", + "homepage": "https://github.com/clue/php-stream-filter", "keywords": [ - "log", - "logging", - "psr-3" + "bucket brigade", + "callback", + "filter", + "php_user_filter", + "stream", + "stream_filter_append", + "stream_filter_register" ], "support": { - "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/1.x" + "issues": "https://github.com/clue/stream-filter/issues", + "source": "https://github.com/clue/stream-filter/tree/v1.4.1" }, - "time": "2016-07-29T03:23:52+00:00" + "funding": [ + { + "url": "https://clue.engineering/support", + "type": "custom" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2019-04-09T12:31:48+00:00" }, { - "name": "myclabs/deep-copy", - "version": "1.10.2", + "name": "codeception/codeception", + "version": "4.1.21", "source": { "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220" + "url": "https://github.com/Codeception/Codeception.git", + "reference": "c25f20d842a7e3fa0a8e6abf0828f102c914d419" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/776f831124e9c62e1a2c601ecc52e776d8bb7220", - "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220", + "url": "https://api.github.com/repos/Codeception/Codeception/zipball/c25f20d842a7e3fa0a8e6abf0828f102c914d419", + "reference": "c25f20d842a7e3fa0a8e6abf0828f102c914d419", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" - }, - "replace": { - "myclabs/deep-copy": "self.version" + "behat/gherkin": "^4.4.0", + "codeception/lib-asserts": "^1.0", + "codeception/phpunit-wrapper": ">6.0.15 <6.1.0 | ^6.6.1 | ^7.7.1 | ^8.1.1 | ^9.0", + "codeception/stub": "^2.0 | ^3.0", + "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "guzzlehttp/psr7": "~1.4", + "php": ">=5.6.0 <9.0", + "symfony/console": ">=2.7 <6.0", + "symfony/css-selector": ">=2.7 <6.0", + "symfony/event-dispatcher": ">=2.7 <6.0", + "symfony/finder": ">=2.7 <6.0", + "symfony/yaml": ">=2.7 <6.0" }, "require-dev": { - "doctrine/collections": "^1.0", - "doctrine/common": "^2.6", - "phpunit/phpunit": "^7.1" + "codeception/module-asserts": "1.*@dev", + "codeception/module-cli": "1.*@dev", + "codeception/module-db": "1.*@dev", + "codeception/module-filesystem": "1.*@dev", + "codeception/module-phpbrowser": "1.*@dev", + "codeception/specify": "~0.3", + "codeception/util-universalframework": "*@dev", + "monolog/monolog": "~1.8", + "squizlabs/php_codesniffer": "~2.0", + "symfony/process": ">=2.7 <6.0", + "vlucas/phpdotenv": "^2.0 | ^3.0 | ^4.0 | ^5.0" + }, + "suggest": { + "codeception/specify": "BDD-style code blocks", + "codeception/verify": "BDD-style assertions", + "hoa/console": "For interactive console functionality", + "stecman/symfony-console-completion": "For BASH autocompletion", + "symfony/phpunit-bridge": "For phpunit-bridge support" }, + "bin": [ + "codecept" + ], "type": "library", + "extra": { + "branch-alias": [] + }, "autoload": { "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - }, - "files": [ - "src/DeepCopy/deep_copy.php" - ] + "Codeception\\": "src/Codeception", + "Codeception\\Extension\\": "ext" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "Create deep copies (clones) of your objects", + "authors": [ + { + "name": "Michael Bodnarchuk", + "email": "davert@mail.ua", + "homepage": "http://codegyre.com" + } + ], + "description": "BDD-style testing framework", + "homepage": "http://codeception.com/", "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" + "BDD", + "TDD", + "acceptance testing", + "functional testing", + "unit testing" ], "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.10.2" + "issues": "https://github.com/Codeception/Codeception/issues", + "source": "https://github.com/Codeception/Codeception/tree/4.1.21" }, "funding": [ { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" + "url": "https://opencollective.com/codeception", + "type": "open_collective" } ], - "time": "2020-11-13T09:40:50+00:00" + "time": "2021-05-28T17:43:39+00:00" }, { - "name": "phar-io/manifest", - "version": "2.0.1", + "name": "codeception/lib-asserts", + "version": "1.13.2", "source": { "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133" + "url": "https://github.com/Codeception/lib-asserts.git", + "reference": "184231d5eab66bc69afd6b9429344d80c67a33b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/85265efd3af7ba3ca4b2a2c34dbfc5788dd29133", - "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133", + "url": "https://api.github.com/repos/Codeception/lib-asserts/zipball/184231d5eab66bc69afd6b9429344d80c67a33b6", + "reference": "184231d5eab66bc69afd6b9429344d80c67a33b6", "shasum": "" }, "require": { + "codeception/phpunit-wrapper": ">6.0.15 <6.1.0 | ^6.6.1 | ^7.7.1 | ^8.0.3 | ^9.0", "ext-dom": "*", - "ext-phar": "*", - "ext-xmlwriter": "*", - "phar-io/version": "^3.0.1", - "php": "^7.2 || ^8.0" + "php": ">=5.6.0 <9.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, "autoload": { "classmap": [ "src/" @@ -3724,48 +3949,54 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" + "name": "Michael Bodnarchuk", + "email": "davert@mail.ua", + "homepage": "http://codegyre.com" }, { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" + "name": "Gintautas Miselis" }, { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" + "name": "Gustavo Nieves", + "homepage": "https://medium.com/@ganieves" } ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "description": "Assertion methods used by Codeception core and Asserts module", + "homepage": "https://codeception.com/", + "keywords": [ + "codeception" + ], "support": { - "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/master" + "issues": "https://github.com/Codeception/lib-asserts/issues", + "source": "https://github.com/Codeception/lib-asserts/tree/1.13.2" }, - "time": "2020-06-27T14:33:11+00:00" + "time": "2020-10-21T16:26:20+00:00" }, { - "name": "phar-io/version", - "version": "3.1.0", + "name": "codeception/module-asserts", + "version": "1.3.1", "source": { "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "bae7c545bef187884426f042434e561ab1ddb182" + "url": "https://github.com/Codeception/module-asserts.git", + "reference": "59374f2fef0cabb9e8ddb53277e85cdca74328de" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/bae7c545bef187884426f042434e561ab1ddb182", - "reference": "bae7c545bef187884426f042434e561ab1ddb182", + "url": "https://api.github.com/repos/Codeception/module-asserts/zipball/59374f2fef0cabb9e8ddb53277e85cdca74328de", + "reference": "59374f2fef0cabb9e8ddb53277e85cdca74328de", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "codeception/codeception": "*@dev", + "codeception/lib-asserts": "^1.13.1", + "php": ">=5.6.0 <9.0" + }, + "conflict": { + "codeception/codeception": "<4.0" }, "type": "library", "autoload": { @@ -3775,68 +4006,62 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" + "name": "Michael Bodnarchuk" }, { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" + "name": "Gintautas Miselis" }, { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" + "name": "Gustavo Nieves", + "homepage": "https://medium.com/@ganieves" } ], - "description": "Library for handling version information and constraints", + "description": "Codeception module containing various assertions", + "homepage": "https://codeception.com/", + "keywords": [ + "assertions", + "asserts", + "codeception" + ], "support": { - "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.1.0" + "issues": "https://github.com/Codeception/module-asserts/issues", + "source": "https://github.com/Codeception/module-asserts/tree/1.3.1" }, - "time": "2021-02-23T14:00:09+00:00" + "time": "2020-10-21T16:48:15+00:00" }, { - "name": "php-http/curl-client", - "version": "v1.7.1", + "name": "codeception/phpunit-wrapper", + "version": "8.1.4", "source": { "type": "git", - "url": "https://github.com/php-http/curl-client.git", - "reference": "6341a93d00e5d953fc868a3928b5167e6513f2b6" + "url": "https://github.com/Codeception/phpunit-wrapper.git", + "reference": "f41335f0b4dd17cf7bbc63e87943b3ae72a8bbc3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-http/curl-client/zipball/6341a93d00e5d953fc868a3928b5167e6513f2b6", - "reference": "6341a93d00e5d953fc868a3928b5167e6513f2b6", + "url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/f41335f0b4dd17cf7bbc63e87943b3ae72a8bbc3", + "reference": "f41335f0b4dd17cf7bbc63e87943b3ae72a8bbc3", "shasum": "" }, "require": { - "ext-curl": "*", - "php": "^5.5 || ^7.0", - "php-http/discovery": "^1.0", - "php-http/httplug": "^1.0", - "php-http/message": "^1.2", - "php-http/message-factory": "^1.0.2" - }, - "provide": { - "php-http/async-client-implementation": "1.0", - "php-http/client-implementation": "1.0" + "php": ">=7.2", + "phpunit/php-code-coverage": "^7.0", + "phpunit/phpunit": "^8.0", + "sebastian/comparator": "^3.0", + "sebastian/diff": "^3.0" }, "require-dev": { - "guzzlehttp/psr7": "^1.0", - "php-http/client-integration-tests": "^0.6", - "phpunit/phpunit": "^4.8.27", - "zendframework/zend-diactoros": "^1.0" + "codeception/specify": "*", + "vlucas/phpdotenv": "^3.0" }, "type": "library", "autoload": { "psr-4": { - "Http\\Client\\Curl\\": "src/" + "Codeception\\PHPUnit\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3845,122 +4070,81 @@ ], "authors": [ { - "name": "Михаил КраÑильников", - "email": "m.krasilnikov@yandex.ru" + "name": "Davert", + "email": "davert.php@resend.cc" } ], - "description": "cURL client for PHP-HTTP", - "homepage": "http://php-http.org", - "keywords": [ - "curl", - "http" - ], + "description": "PHPUnit classes used by Codeception", "support": { - "issues": "https://github.com/php-http/curl-client/issues", - "source": "https://github.com/php-http/curl-client/tree/v1.7.1" + "issues": "https://github.com/Codeception/phpunit-wrapper/issues", + "source": "https://github.com/Codeception/phpunit-wrapper/tree/8.1.4" }, - "time": "2018-03-26T19:21:48+00:00" + "time": "2020-12-28T14:00:08+00:00" }, { - "name": "php-http/discovery", - "version": "1.6.1", + "name": "codeception/stub", + "version": "3.7.0", "source": { "type": "git", - "url": "https://github.com/php-http/discovery.git", - "reference": "684855f2c2e9d0a61868b8f8d6bd0295c8a4b651" + "url": "https://github.com/Codeception/Stub.git", + "reference": "468dd5fe659f131fc997f5196aad87512f9b1304" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-http/discovery/zipball/684855f2c2e9d0a61868b8f8d6bd0295c8a4b651", - "reference": "684855f2c2e9d0a61868b8f8d6bd0295c8a4b651", + "url": "https://api.github.com/repos/Codeception/Stub/zipball/468dd5fe659f131fc997f5196aad87512f9b1304", + "reference": "468dd5fe659f131fc997f5196aad87512f9b1304", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0" - }, - "conflict": { - "nyholm/psr7": "<1.0" - }, - "require-dev": { - "php-http/httplug": "^1.0 || ^2.0", - "php-http/message-factory": "^1.0", - "phpspec/phpspec": "^2.4", - "puli/composer-plugin": "1.0.0-beta10" - }, - "suggest": { - "php-http/message": "Allow to use Guzzle, Diactoros or Slim Framework factories", - "puli/composer-plugin": "Sets up Puli which is recommended for Discovery to work. Check http://docs.php-http.org/en/latest/discovery.html for more details." + "phpunit/phpunit": "^8.4 | ^9.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.5-dev" - } - }, "autoload": { "psr-4": { - "Http\\Discovery\\": "src/" + "Codeception\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com" - } - ], - "description": "Finds installed HTTPlug implementations and PSR-7 message factories", - "homepage": "http://php-http.org", - "keywords": [ - "adapter", - "client", - "discovery", - "factory", - "http", - "message", - "psr7" - ], + "description": "Flexible Stub wrapper for PHPUnit's Mock Builder", "support": { - "issues": "https://github.com/php-http/discovery/issues", - "source": "https://github.com/php-http/discovery/tree/master" + "issues": "https://github.com/Codeception/Stub/issues", + "source": "https://github.com/Codeception/Stub/tree/3.7.0" }, - "time": "2019-02-23T07:42:53+00:00" + "time": "2020-07-03T15:54:43+00:00" }, { - "name": "php-http/httplug", - "version": "v1.1.0", + "name": "doctrine/instantiator", + "version": "1.4.0", "source": { "type": "git", - "url": "https://github.com/php-http/httplug.git", - "reference": "1c6381726c18579c4ca2ef1ec1498fdae8bdf018" + "url": "https://github.com/doctrine/instantiator.git", + "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-http/httplug/zipball/1c6381726c18579c4ca2ef1ec1498fdae8bdf018", - "reference": "1c6381726c18579c4ca2ef1ec1498fdae8bdf018", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/d56bf6102915de5702778fe20f2de3b2fe570b5b", + "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b", "shasum": "" }, "require": { - "php": ">=5.4", - "php-http/promise": "^1.0", - "psr/http-message": "^1.0" + "php": "^7.1 || ^8.0" }, "require-dev": { - "henrikbjorn/phpspec-code-coverage": "^1.0", - "phpspec/phpspec": "^2.4" + "doctrine/coding-standard": "^8.0", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^0.13 || 1.0.0-alpha2", + "phpstan/phpstan": "^0.12", + "phpstan/phpstan-phpunit": "^0.12", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, "autoload": { "psr-4": { - "Http\\Client\\": "src/" + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3969,78 +4153,94 @@ ], "authors": [ { - "name": "Eric GELOEN", - "email": "geloen.eric@gmail.com" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com" + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" } ], - "description": "HTTPlug, the HTTP client abstraction for PHP", - "homepage": "http://httplug.io", + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", "keywords": [ - "client", - "http" + "constructor", + "instantiate" ], "support": { - "issues": "https://github.com/php-http/httplug/issues", - "source": "https://github.com/php-http/httplug/tree/master" + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/1.4.0" }, - "time": "2016-08-31T08:30:17+00:00" + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2020-11-10T18:47:58+00:00" }, { - "name": "php-http/message", - "version": "1.7.2", + "name": "monolog/monolog", + "version": "1.21.0", "source": { "type": "git", - "url": "https://github.com/php-http/message.git", - "reference": "b159ffe570dffd335e22ef0b91a946eacb182fa1" + "url": "https://github.com/Seldaek/monolog.git", + "reference": "f42fbdfd53e306bda545845e4dbfd3e72edb4952" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-http/message/zipball/b159ffe570dffd335e22ef0b91a946eacb182fa1", - "reference": "b159ffe570dffd335e22ef0b91a946eacb182fa1", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/f42fbdfd53e306bda545845e4dbfd3e72edb4952", + "reference": "f42fbdfd53e306bda545845e4dbfd3e72edb4952", "shasum": "" }, "require": { - "clue/stream-filter": "^1.4", - "php": "^5.4 || ^7.0", - "php-http/message-factory": "^1.0.2", - "psr/http-message": "^1.0" + "php": ">=5.3.0", + "psr/log": "~1.0" }, "provide": { - "php-http/message-factory-implementation": "1.0" + "psr/log-implementation": "1.0.0" }, "require-dev": { - "akeneo/phpspec-skip-example-extension": "^1.0", - "coduo/phpspec-data-provider-extension": "^1.0", - "ext-zlib": "*", - "guzzlehttp/psr7": "^1.0", - "henrikbjorn/phpspec-code-coverage": "^1.0", - "phpspec/phpspec": "^2.4", - "slim/slim": "^3.0", - "zendframework/zend-diactoros": "^1.0" + "aws/aws-sdk-php": "^2.4.9", + "doctrine/couchdb": "~1.0@dev", + "graylog2/gelf-php": "~1.0", + "jakub-onderka/php-parallel-lint": "0.9", + "php-amqplib/php-amqplib": "~2.4", + "php-console/php-console": "^3.1.3", + "phpunit/phpunit": "~4.5", + "phpunit/phpunit-mock-objects": "2.3.0", + "ruflin/elastica": ">=0.90 <3.0", + "sentry/sentry": "^0.13", + "swiftmailer/swiftmailer": "~5.3" }, "suggest": { - "ext-zlib": "Used with compressor/decompressor streams", - "guzzlehttp/psr7": "Used with Guzzle PSR-7 Factories", - "slim/slim": "Used with Slim Framework PSR-7 implementation", - "zendframework/zend-diactoros": "Used with Diactoros Factories" + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-mongo": "Allow sending log messages to a MongoDB server", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "php-console/php-console": "Allow sending log messages to Google Chrome", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server", + "sentry/sentry": "Allow sending log messages to a Sentry server" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.6-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { "psr-4": { - "Http\\Message\\": "src/" - }, - "files": [ - "src/filters.php" - ] + "Monolog\\": "src/Monolog" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -4048,217 +4248,229 @@ ], "authors": [ { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com" + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" } ], - "description": "HTTP Message related tools", - "homepage": "http://php-http.org", + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "http://github.com/Seldaek/monolog", "keywords": [ - "http", - "message", - "psr-7" + "log", + "logging", + "psr-3" ], "support": { - "issues": "https://github.com/php-http/message/issues", - "source": "https://github.com/php-http/message/tree/master" + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/1.x" }, - "time": "2018-11-01T09:32:41+00:00" + "time": "2016-07-29T03:23:52+00:00" }, - { - "name": "php-http/message-factory", - "version": "v1.0.2", + { + "name": "myclabs/deep-copy", + "version": "1.10.2", "source": { "type": "git", - "url": "https://github.com/php-http/message-factory.git", - "reference": "a478cb11f66a6ac48d8954216cfed9aa06a501a1" + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-http/message-factory/zipball/a478cb11f66a6ac48d8954216cfed9aa06a501a1", - "reference": "a478cb11f66a6ac48d8954216cfed9aa06a501a1", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/776f831124e9c62e1a2c601ecc52e776d8bb7220", + "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220", "shasum": "" }, "require": { - "php": ">=5.4", - "psr/http-message": "^1.0" + "php": "^7.1 || ^8.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } + "replace": { + "myclabs/deep-copy": "self.version" + }, + "require-dev": { + "doctrine/collections": "^1.0", + "doctrine/common": "^2.6", + "phpunit/phpunit": "^7.1" }, + "type": "library", "autoload": { "psr-4": { - "Http\\Message\\": "src/" - } + "DeepCopy\\": "src/DeepCopy/" + }, + "files": [ + "src/DeepCopy/deep_copy.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com" - } - ], - "description": "Factory interfaces for PSR-7 HTTP Message", - "homepage": "http://php-http.org", + "description": "Create deep copies (clones) of your objects", "keywords": [ - "factory", - "http", - "message", - "stream", - "uri" + "clone", + "copy", + "duplicate", + "object", + "object graph" ], "support": { - "issues": "https://github.com/php-http/message-factory/issues", - "source": "https://github.com/php-http/message-factory/tree/master" + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.10.2" }, - "time": "2015-12-19T14:08:53+00:00" + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2020-11-13T09:40:50+00:00" }, { - "name": "php-http/promise", - "version": "v1.0.0", + "name": "phar-io/manifest", + "version": "2.0.1", "source": { "type": "git", - "url": "https://github.com/php-http/promise.git", - "reference": "dc494cdc9d7160b9a09bd5573272195242ce7980" + "url": "https://github.com/phar-io/manifest.git", + "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-http/promise/zipball/dc494cdc9d7160b9a09bd5573272195242ce7980", - "reference": "dc494cdc9d7160b9a09bd5573272195242ce7980", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/85265efd3af7ba3ca4b2a2c34dbfc5788dd29133", + "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133", "shasum": "" }, - "require-dev": { - "henrikbjorn/phpspec-code-coverage": "^1.0", - "phpspec/phpspec": "^2.4" + "require": { + "ext-dom": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { - "psr-4": { - "Http\\Promise\\": "src/" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com" + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" }, { - "name": "Joel Wurtz", - "email": "joel.wurtz@gmail.com" + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" } ], - "description": "Promise used for asynchronous HTTP requests", - "homepage": "http://httplug.io", - "keywords": [ - "promise" - ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", "support": { - "issues": "https://github.com/php-http/promise/issues", - "source": "https://github.com/php-http/promise/tree/master" + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/master" }, - "time": "2016-01-26T13:27:02+00:00" + "time": "2020-06-27T14:33:11+00:00" }, { - "name": "phpdocumentor/reflection-common", - "version": "2.2.0", + "name": "phar-io/version", + "version": "3.1.0", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + "url": "https://github.com/phar-io/version.git", + "reference": "bae7c545bef187884426f042434e561ab1ddb182" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "url": "https://api.github.com/repos/phar-io/version/zipball/bae7c545bef187884426f042434e561ab1ddb182", + "reference": "bae7c545bef187884426f042434e561ab1ddb182", "shasum": "" }, "require": { "php": "^7.2 || ^8.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.x-dev" - } - }, "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src/" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" } ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], + "description": "Library for handling version information and constraints", "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.1.0" }, - "time": "2020-06-27T09:03:43+00:00" + "time": "2021-02-23T14:00:09+00:00" }, { - "name": "phpdocumentor/reflection-docblock", - "version": "5.2.2", + "name": "php-http/curl-client", + "version": "v1.7.1", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556" + "url": "https://github.com/php-http/curl-client.git", + "reference": "6341a93d00e5d953fc868a3928b5167e6513f2b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/069a785b2141f5bcf49f3e353548dc1cce6df556", - "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556", + "url": "https://api.github.com/repos/php-http/curl-client/zipball/6341a93d00e5d953fc868a3928b5167e6513f2b6", + "reference": "6341a93d00e5d953fc868a3928b5167e6513f2b6", "shasum": "" }, "require": { - "ext-filter": "*", - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.3", - "webmozart/assert": "^1.9.1" + "ext-curl": "*", + "php": "^5.5 || ^7.0", + "php-http/discovery": "^1.0", + "php-http/httplug": "^1.0", + "php-http/message": "^1.2", + "php-http/message-factory": "^1.0.2" + }, + "provide": { + "php-http/async-client-implementation": "1.0", + "php-http/client-implementation": "1.0" }, "require-dev": { - "mockery/mockery": "~1.3.2" + "guzzlehttp/psr7": "^1.0", + "php-http/client-integration-tests": "^0.6", + "phpunit/phpunit": "^4.8.27", + "zendframework/zend-diactoros": "^1.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - } - }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": "src" + "Http\\Client\\Curl\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4267,51 +4479,61 @@ ], "authors": [ { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - }, - { - "name": "Jaap van Otterdijk", - "email": "account@ijaap.nl" + "name": "Михаил КраÑильников", + "email": "m.krasilnikov@yandex.ru" } ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "description": "cURL client for PHP-HTTP", + "homepage": "http://php-http.org", + "keywords": [ + "curl", + "http" + ], "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/master" + "issues": "https://github.com/php-http/curl-client/issues", + "source": "https://github.com/php-http/curl-client/tree/v1.7.1" }, - "time": "2020-09-03T19:13:55+00:00" + "time": "2018-03-26T19:21:48+00:00" }, { - "name": "phpdocumentor/type-resolver", - "version": "1.4.0", + "name": "php-http/discovery", + "version": "1.6.1", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0" + "url": "https://github.com/php-http/discovery.git", + "reference": "684855f2c2e9d0a61868b8f8d6bd0295c8a4b651" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0", - "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0", + "url": "https://api.github.com/repos/php-http/discovery/zipball/684855f2c2e9d0a61868b8f8d6bd0295c8a4b651", + "reference": "684855f2c2e9d0a61868b8f8d6bd0295c8a4b651", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.0" + "php": "^5.5 || ^7.0" + }, + "conflict": { + "nyholm/psr7": "<1.0" }, "require-dev": { - "ext-tokenizer": "*" + "php-http/httplug": "^1.0 || ^2.0", + "php-http/message-factory": "^1.0", + "phpspec/phpspec": "^2.4", + "puli/composer-plugin": "1.0.0-beta10" + }, + "suggest": { + "php-http/message": "Allow to use Guzzle, Diactoros or Slim Framework factories", + "puli/composer-plugin": "Sets up Puli which is recommended for Discovery to work. Check http://docs.php-http.org/en/latest/discovery.html for more details." }, "type": "library", "extra": { "branch-alias": { - "dev-1.x": "1.x-dev" + "dev-master": "1.5-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": "src" + "Http\\Discovery\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4320,51 +4542,59 @@ ], "authors": [ { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" } ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "description": "Finds installed HTTPlug implementations and PSR-7 message factories", + "homepage": "http://php-http.org", + "keywords": [ + "adapter", + "client", + "discovery", + "factory", + "http", + "message", + "psr7" + ], "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.4.0" + "issues": "https://github.com/php-http/discovery/issues", + "source": "https://github.com/php-http/discovery/tree/master" }, - "time": "2020-09-17T18:55:26+00:00" + "time": "2019-02-23T07:42:53+00:00" }, { - "name": "phpspec/prophecy", - "version": "1.13.0", + "name": "php-http/httplug", + "version": "v1.1.0", "source": { "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "be1996ed8adc35c3fd795488a653f4b518be70ea" + "url": "https://github.com/php-http/httplug.git", + "reference": "1c6381726c18579c4ca2ef1ec1498fdae8bdf018" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/be1996ed8adc35c3fd795488a653f4b518be70ea", - "reference": "be1996ed8adc35c3fd795488a653f4b518be70ea", + "url": "https://api.github.com/repos/php-http/httplug/zipball/1c6381726c18579c4ca2ef1ec1498fdae8bdf018", + "reference": "1c6381726c18579c4ca2ef1ec1498fdae8bdf018", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.2", - "php": "^7.2 || ~8.0, <8.1", - "phpdocumentor/reflection-docblock": "^5.2", - "sebastian/comparator": "^3.0 || ^4.0", - "sebastian/recursion-context": "^3.0 || ^4.0" + "php": ">=5.4", + "php-http/promise": "^1.0", + "psr/http-message": "^1.0" }, "require-dev": { - "phpspec/phpspec": "^6.0", - "phpunit/phpunit": "^8.0 || ^9.0" + "henrikbjorn/phpspec-code-coverage": "^1.0", + "phpspec/phpspec": "^2.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.11.x-dev" + "dev-master": "1.1-dev" } }, "autoload": { "psr-4": { - "Prophecy\\": "src/Prophecy" + "Http\\Client\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4373,451 +4603,402 @@ ], "authors": [ { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" + "name": "Eric GELOEN", + "email": "geloen.eric@gmail.com" }, { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" } ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", + "description": "HTTPlug, the HTTP client abstraction for PHP", + "homepage": "http://httplug.io", "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" + "client", + "http" ], "support": { - "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/1.13.0" + "issues": "https://github.com/php-http/httplug/issues", + "source": "https://github.com/php-http/httplug/tree/master" }, - "time": "2021-03-17T13:42:18+00:00" + "time": "2016-08-31T08:30:17+00:00" }, { - "name": "phpunit/php-code-coverage", - "version": "7.0.14", + "name": "php-http/message", + "version": "1.7.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "bb7c9a210c72e4709cdde67f8b7362f672f2225c" + "url": "https://github.com/php-http/message.git", + "reference": "b159ffe570dffd335e22ef0b91a946eacb182fa1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/bb7c9a210c72e4709cdde67f8b7362f672f2225c", - "reference": "bb7c9a210c72e4709cdde67f8b7362f672f2225c", + "url": "https://api.github.com/repos/php-http/message/zipball/b159ffe570dffd335e22ef0b91a946eacb182fa1", + "reference": "b159ffe570dffd335e22ef0b91a946eacb182fa1", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-xmlwriter": "*", - "php": ">=7.2", - "phpunit/php-file-iterator": "^2.0.2", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^3.1.1 || ^4.0", - "sebastian/code-unit-reverse-lookup": "^1.0.1", - "sebastian/environment": "^4.2.2", - "sebastian/version": "^2.0.1", - "theseer/tokenizer": "^1.1.3" + "clue/stream-filter": "^1.4", + "php": "^5.4 || ^7.0", + "php-http/message-factory": "^1.0.2", + "psr/http-message": "^1.0" + }, + "provide": { + "php-http/message-factory-implementation": "1.0" }, "require-dev": { - "phpunit/phpunit": "^8.2.2" + "akeneo/phpspec-skip-example-extension": "^1.0", + "coduo/phpspec-data-provider-extension": "^1.0", + "ext-zlib": "*", + "guzzlehttp/psr7": "^1.0", + "henrikbjorn/phpspec-code-coverage": "^1.0", + "phpspec/phpspec": "^2.4", + "slim/slim": "^3.0", + "zendframework/zend-diactoros": "^1.0" }, "suggest": { - "ext-xdebug": "^2.7.2" + "ext-zlib": "Used with compressor/decompressor streams", + "guzzlehttp/psr7": "Used with Guzzle PSR-7 Factories", + "slim/slim": "Used with Slim Framework PSR-7 implementation", + "zendframework/zend-diactoros": "Used with Diactoros Factories" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "7.0-dev" + "dev-master": "1.6-dev" } }, "autoload": { - "classmap": [ - "src/" + "psr-4": { + "Http\\Message\\": "src/" + }, + "files": [ + "src/filters.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" } ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "description": "HTTP Message related tools", + "homepage": "http://php-http.org", "keywords": [ - "coverage", - "testing", - "xunit" + "http", + "message", + "psr-7" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/7.0.14" + "issues": "https://github.com/php-http/message/issues", + "source": "https://github.com/php-http/message/tree/master" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-12-02T13:39:03+00:00" + "time": "2018-11-01T09:32:41+00:00" }, { - "name": "phpunit/php-file-iterator", - "version": "2.0.3", + "name": "php-http/message-factory", + "version": "v1.0.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "4b49fb70f067272b659ef0174ff9ca40fdaa6357" + "url": "https://github.com/php-http/message-factory.git", + "reference": "a478cb11f66a6ac48d8954216cfed9aa06a501a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/4b49fb70f067272b659ef0174ff9ca40fdaa6357", - "reference": "4b49fb70f067272b659ef0174ff9ca40fdaa6357", + "url": "https://api.github.com/repos/php-http/message-factory/zipball/a478cb11f66a6ac48d8954216cfed9aa06a501a1", + "reference": "a478cb11f66a6ac48d8954216cfed9aa06a501a1", "shasum": "" }, "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^8.5" + "php": ">=5.4", + "psr/http-message": "^1.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "1.0-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Http\\Message\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" } ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "description": "Factory interfaces for PSR-7 HTTP Message", + "homepage": "http://php-http.org", "keywords": [ - "filesystem", - "iterator" + "factory", + "http", + "message", + "stream", + "uri" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/2.0.3" + "issues": "https://github.com/php-http/message-factory/issues", + "source": "https://github.com/php-http/message-factory/tree/master" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T08:25:21+00:00" + "time": "2015-12-19T14:08:53+00:00" }, { - "name": "phpunit/php-text-template", - "version": "1.2.1", + "name": "php-http/promise", + "version": "v1.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" + "url": "https://github.com/php-http/promise.git", + "reference": "dc494cdc9d7160b9a09bd5573272195242ce7980" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "url": "https://api.github.com/repos/php-http/promise/zipball/dc494cdc9d7160b9a09bd5573272195242ce7980", + "reference": "dc494cdc9d7160b9a09bd5573272195242ce7980", "shasum": "" }, - "require": { - "php": ">=5.3.3" + "require-dev": { + "henrikbjorn/phpspec-code-coverage": "^1.0", + "phpspec/phpspec": "^2.4" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Http\\Promise\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + }, + { + "name": "Joel Wurtz", + "email": "joel.wurtz@gmail.com" } ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "description": "Promise used for asynchronous HTTP requests", + "homepage": "http://httplug.io", "keywords": [ - "template" + "promise" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/1.2.1" + "issues": "https://github.com/php-http/promise/issues", + "source": "https://github.com/php-http/promise/tree/master" }, - "time": "2015-06-21T13:50:34+00:00" + "time": "2016-01-26T13:27:02+00:00" }, { - "name": "phpunit/php-timer", - "version": "2.1.3", + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662" + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/2454ae1765516d20c4ffe103d85a58a9a3bd5662", - "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", "shasum": "" }, "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^8.5" + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1-dev" + "dev-2.x": "2.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" } ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", "keywords": [ - "timer" + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/2.1.3" + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T08:20:02+00:00" + "time": "2020-06-27T09:03:43+00:00" }, { - "name": "phpunit/php-token-stream", - "version": "3.1.2", + "name": "phpdocumentor/reflection-docblock", + "version": "5.2.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "472b687829041c24b25f475e14c2f38a09edf1c2" + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/472b687829041c24b25f475e14c2f38a09edf1c2", - "reference": "472b687829041c24b25f475e14c2f38a09edf1c2", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/069a785b2141f5bcf49f3e353548dc1cce6df556", + "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556", "shasum": "" }, "require": { - "ext-tokenizer": "*", - "php": ">=7.1" + "ext-filter": "*", + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.3", + "webmozart/assert": "^1.9.1" }, "require-dev": { - "phpunit/phpunit": "^7.0" + "mockery/mockery": "~1.3.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-master": "5.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "account@ijaap.nl" } ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", - "keywords": [ - "tokenizer" - ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { - "issues": "https://github.com/sebastianbergmann/php-token-stream/issues", - "source": "https://github.com/sebastianbergmann/php-token-stream/tree/3.1.2" + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/master" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "abandoned": true, - "time": "2020-11-30T08:38:46+00:00" + "time": "2020-09-03T19:13:55+00:00" }, { - "name": "phpunit/phpunit", - "version": "8.5.17", + "name": "phpdocumentor/type-resolver", + "version": "1.4.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "79067856d85421c56d413bd238d4e2cd6b0e54da" + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/79067856d85421c56d413bd238d4e2cd6b0e54da", - "reference": "79067856d85421c56d413bd238d4e2cd6b0e54da", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0", + "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.3.1", - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.0", - "phar-io/manifest": "^2.0.1", - "phar-io/version": "^3.0.2", - "php": ">=7.2", - "phpspec/prophecy": "^1.10.3", - "phpunit/php-code-coverage": "^7.0.12", - "phpunit/php-file-iterator": "^2.0.2", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^2.1.2", - "sebastian/comparator": "^3.0.2", - "sebastian/diff": "^3.0.2", - "sebastian/environment": "^4.2.3", - "sebastian/exporter": "^3.1.2", - "sebastian/global-state": "^3.0.0", - "sebastian/object-enumerator": "^3.0.3", - "sebastian/resource-operations": "^2.0.1", - "sebastian/type": "^1.1.3", - "sebastian/version": "^2.0.1" + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.0" }, "require-dev": { - "ext-pdo": "*" - }, - "suggest": { - "ext-soap": "*", - "ext-xdebug": "*", - "phpunit/php-invoker": "^2.0.0" + "ext-tokenizer": "*" }, - "bin": [ - "phpunit" - ], "type": "library", "extra": { "branch-alias": { - "dev-master": "8.5-dev" + "dev-1.x": "1.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Mike van Riel", + "email": "me@mikevanriel.com" } ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/8.5.17" + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.4.0" }, - "funding": [ - { - "url": "https://phpunit.de/donate.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2021-06-23T05:12:43+00:00" + "time": "2020-09-17T18:55:26+00:00" }, { - "name": "psr/event-dispatcher", - "version": "1.0.0", + "name": "phpspec/prophecy", + "version": "1.13.0", "source": { "type": "git", - "url": "https://github.com/php-fig/event-dispatcher.git", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + "url": "https://github.com/phpspec/prophecy.git", + "reference": "be1996ed8adc35c3fd795488a653f4b518be70ea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/be1996ed8adc35c3fd795488a653f4b518be70ea", + "reference": "be1996ed8adc35c3fd795488a653f4b518be70ea", "shasum": "" }, "require": { - "php": ">=7.2.0" + "doctrine/instantiator": "^1.2", + "php": "^7.2 || ~8.0, <8.1", + "phpdocumentor/reflection-docblock": "^5.2", + "sebastian/comparator": "^3.0 || ^4.0", + "sebastian/recursion-context": "^3.0 || ^4.0" + }, + "require-dev": { + "phpspec/phpspec": "^6.0", + "phpunit/phpunit": "^8.0 || ^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.11.x-dev" } }, "autoload": { "psr-4": { - "Psr\\EventDispatcher\\": "src/" + "Prophecy\\": "src/Prophecy" } }, "notification-url": "https://packagist.org/downloads/", @@ -4826,46 +5007,67 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" } ], - "description": "Standard interfaces for event handling.", + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", "keywords": [ - "events", - "psr", - "psr-14" + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" ], "support": { - "issues": "https://github.com/php-fig/event-dispatcher/issues", - "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + "issues": "https://github.com/phpspec/prophecy/issues", + "source": "https://github.com/phpspec/prophecy/tree/1.13.0" }, - "time": "2019-01-08T18:20:26+00:00" + "time": "2021-03-17T13:42:18+00:00" }, { - "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.2", + "name": "phpunit/php-code-coverage", + "version": "7.0.14", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619" + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "bb7c9a210c72e4709cdde67f8b7362f672f2225c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/1de8cd5c010cb153fcd68b8d0f64606f523f7619", - "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/bb7c9a210c72e4709cdde67f8b7362f672f2225c", + "reference": "bb7c9a210c72e4709cdde67f8b7362f672f2225c", "shasum": "" }, "require": { - "php": ">=5.6" + "ext-dom": "*", + "ext-xmlwriter": "*", + "php": ">=7.2", + "phpunit/php-file-iterator": "^2.0.2", + "phpunit/php-text-template": "^1.2.1", + "phpunit/php-token-stream": "^3.1.1 || ^4.0", + "sebastian/code-unit-reverse-lookup": "^1.0.1", + "sebastian/environment": "^4.2.2", + "sebastian/version": "^2.0.1", + "theseer/tokenizer": "^1.1.3" }, "require-dev": { - "phpunit/phpunit": "^8.5" + "phpunit/phpunit": "^8.2.2" + }, + "suggest": { + "ext-xdebug": "^2.7.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "7.0-dev" } }, "autoload": { @@ -4880,14 +5082,20 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.2" + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/7.0.14" }, "funding": [ { @@ -4895,26 +5103,24 @@ "type": "github" } ], - "time": "2020-11-30T08:15:22+00:00" + "time": "2020-12-02T13:39:03+00:00" }, { - "name": "sebastian/comparator", - "version": "3.0.3", + "name": "phpunit/php-file-iterator", + "version": "2.0.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "1071dfcef776a57013124ff35e1fc41ccd294758" + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "4b49fb70f067272b659ef0174ff9ca40fdaa6357" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1071dfcef776a57013124ff35e1fc41ccd294758", - "reference": "1071dfcef776a57013124ff35e1fc41ccd294758", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/4b49fb70f067272b659ef0174ff9ca40fdaa6357", + "reference": "4b49fb70f067272b659ef0174ff9ca40fdaa6357", "shasum": "" }, "require": { - "php": ">=7.1", - "sebastian/diff": "^3.0", - "sebastian/exporter": "^3.1" + "php": ">=7.1" }, "require-dev": { "phpunit/phpunit": "^8.5" @@ -4922,7 +5128,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -4937,31 +5143,19 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", "keywords": [ - "comparator", - "compare", - "equality" + "filesystem", + "iterator" ], "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/3.0.3" + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/2.0.3" }, "funding": [ { @@ -4969,35 +5163,26 @@ "type": "github" } ], - "time": "2020-11-30T08:04:30+00:00" + "time": "2020-11-30T08:25:21+00:00" }, { - "name": "sebastian/diff", - "version": "3.0.3", + "name": "phpunit/php-text-template", + "version": "1.2.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211" + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/14f72dd46eaf2f2293cbe79c93cc0bc43161a211", - "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", "shasum": "" }, "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.5 || ^8.0", - "symfony/process": "^2 || ^3.3 || ^4" + "php": ">=5.3.3" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, "autoload": { "classmap": [ "src/" @@ -5010,60 +5195,45 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" + "template" ], "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/3.0.3" + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/1.2.1" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T07:59:04+00:00" + "time": "2015-06-21T13:50:34+00:00" }, { - "name": "sebastian/environment", - "version": "4.2.4", + "name": "phpunit/php-timer", + "version": "2.1.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0" + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", - "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/2454ae1765516d20c4ffe103d85a58a9a3bd5662", + "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662", "shasum": "" }, "require": { "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "^7.5" - }, - "suggest": { - "ext-posix": "*" + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.2-dev" + "dev-master": "2.1-dev" } }, "autoload": { @@ -5078,19 +5248,18 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", "keywords": [ - "Xdebug", - "environment", - "hhvm" + "timer" ], "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/4.2.4" + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/2.1.3" }, "funding": [ { @@ -5098,34 +5267,33 @@ "type": "github" } ], - "time": "2020-11-30T07:53:42+00:00" + "time": "2020-11-30T08:20:02+00:00" }, { - "name": "sebastian/exporter", - "version": "3.1.3", + "name": "phpunit/php-token-stream", + "version": "3.1.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "6b853149eab67d4da22291d36f5b0631c0fd856e" + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "472b687829041c24b25f475e14c2f38a09edf1c2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/6b853149eab67d4da22291d36f5b0631c0fd856e", - "reference": "6b853149eab67d4da22291d36f5b0631c0fd856e", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/472b687829041c24b25f475e14c2f38a09edf1c2", + "reference": "472b687829041c24b25f475e14c2f38a09edf1c2", "shasum": "" }, "require": { - "php": ">=7.0", - "sebastian/recursion-context": "^3.0" + "ext-tokenizer": "*", + "php": ">=7.1" }, "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^7.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1.x-dev" + "dev-master": "3.1-dev" } }, "autoload": { @@ -5141,33 +5309,16 @@ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" } ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "http://www.github.com/sebastianbergmann/exporter", + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "https://github.com/sebastianbergmann/php-token-stream/", "keywords": [ - "export", - "exporter" + "tokenizer" ], "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/3.1.3" + "issues": "https://github.com/sebastianbergmann/php-token-stream/issues", + "source": "https://github.com/sebastianbergmann/php-token-stream/tree/3.1.2" }, "funding": [ { @@ -5175,38 +5326,65 @@ "type": "github" } ], - "time": "2020-11-30T07:47:53+00:00" + "abandoned": true, + "time": "2020-11-30T08:38:46+00:00" }, { - "name": "sebastian/global-state", - "version": "3.0.1", + "name": "phpunit/phpunit", + "version": "8.5.17", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "474fb9edb7ab891665d3bfc6317f42a0a150454b" + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "79067856d85421c56d413bd238d4e2cd6b0e54da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/474fb9edb7ab891665d3bfc6317f42a0a150454b", - "reference": "474fb9edb7ab891665d3bfc6317f42a0a150454b", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/79067856d85421c56d413bd238d4e2cd6b0e54da", + "reference": "79067856d85421c56d413bd238d4e2cd6b0e54da", "shasum": "" }, "require": { + "doctrine/instantiator": "^1.3.1", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.0", + "phar-io/manifest": "^2.0.1", + "phar-io/version": "^3.0.2", "php": ">=7.2", - "sebastian/object-reflector": "^1.1.1", - "sebastian/recursion-context": "^3.0" + "phpspec/prophecy": "^1.10.3", + "phpunit/php-code-coverage": "^7.0.12", + "phpunit/php-file-iterator": "^2.0.2", + "phpunit/php-text-template": "^1.2.1", + "phpunit/php-timer": "^2.1.2", + "sebastian/comparator": "^3.0.2", + "sebastian/diff": "^3.0.2", + "sebastian/environment": "^4.2.3", + "sebastian/exporter": "^3.1.2", + "sebastian/global-state": "^3.0.0", + "sebastian/object-enumerator": "^3.0.3", + "sebastian/resource-operations": "^2.0.1", + "sebastian/type": "^1.1.3", + "sebastian/version": "^2.0.1" }, "require-dev": { - "ext-dom": "*", - "phpunit/phpunit": "^8.0" + "ext-pdo": "*" }, "suggest": { - "ext-uopz": "*" + "ext-soap": "*", + "ext-xdebug": "*", + "phpunit/php-invoker": "^2.0.0" }, + "bin": [ + "phpunit" + ], "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "8.5-dev" } }, "autoload": { @@ -5221,107 +5399,107 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", "keywords": [ - "global state" + "phpunit", + "testing", + "xunit" ], "support": { - "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/3.0.1" + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/8.5.17" }, "funding": [ + { + "url": "https://phpunit.de/donate.html", + "type": "custom" + }, { "url": "https://github.com/sebastianbergmann", "type": "github" } ], - "time": "2020-11-30T07:43:24+00:00" + "time": "2021-06-23T05:12:43+00:00" }, { - "name": "sebastian/object-enumerator", - "version": "3.0.4", + "name": "psr/event-dispatcher", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2" + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", - "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", "shasum": "" }, "require": { - "php": ">=7.0", - "sebastian/object-reflector": "^1.1.1", - "sebastian/recursion-context": "^3.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" + "php": ">=7.2.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" } ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], "support": { - "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/3.0.4" + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-11-30T07:40:27+00:00" + "time": "2019-01-08T18:20:26+00:00" }, { - "name": "sebastian/object-reflector", - "version": "1.1.2", + "name": "sebastian/code-unit-reverse-lookup", + "version": "1.0.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d" + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", - "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/1de8cd5c010cb153fcd68b8d0f64606f523f7619", + "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619", "shasum": "" }, "require": { - "php": ">=7.0" + "php": ">=5.6" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { @@ -5339,11 +5517,11 @@ "email": "sebastian@phpunit.de" } ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", "support": { - "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/1.1.2" + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.2" }, "funding": [ { @@ -5351,32 +5529,34 @@ "type": "github" } ], - "time": "2020-11-30T07:37:18+00:00" + "time": "2020-11-30T08:15:22+00:00" }, { - "name": "sebastian/recursion-context", - "version": "3.0.1", + "name": "sebastian/comparator", + "version": "3.0.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb" + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "1071dfcef776a57013124ff35e1fc41ccd294758" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/367dcba38d6e1977be014dc4b22f47a484dac7fb", - "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1071dfcef776a57013124ff35e1fc41ccd294758", + "reference": "1071dfcef776a57013124ff35e1fc41ccd294758", "shasum": "" }, "require": { - "php": ">=7.0" + "php": ">=7.1", + "sebastian/diff": "^3.0", + "sebastian/exporter": "^3.1" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -5398,15 +5578,24 @@ "email": "whatthejeff@gmail.com" }, { - "name": "Adam Harvey", - "email": "aharvey@php.net" + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" } ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/3.0.1" + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/3.0.3" }, "funding": [ { @@ -5414,29 +5603,33 @@ "type": "github" } ], - "time": "2020-11-30T07:34:24+00:00" + "time": "2020-11-30T08:04:30+00:00" }, { - "name": "sebastian/resource-operations", - "version": "2.0.2", + "name": "sebastian/diff", + "version": "3.0.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3" + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/31d35ca87926450c44eae7e2611d45a7a65ea8b3", - "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/14f72dd46eaf2f2293cbe79c93cc0bc43161a211", + "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211", "shasum": "" }, "require": { "php": ">=7.1" }, + "require-dev": { + "phpunit/phpunit": "^7.5 || ^8.0", + "symfony/process": "^2 || ^3.3 || ^4" + }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -5452,13 +5645,23 @@ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" } ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/2.0.2" + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/3.0.3" }, "funding": [ { @@ -5466,32 +5669,35 @@ "type": "github" } ], - "time": "2020-11-30T07:30:19+00:00" + "time": "2020-11-30T07:59:04+00:00" }, { - "name": "sebastian/type", - "version": "1.1.4", + "name": "sebastian/environment", + "version": "4.2.4", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4" + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/0150cfbc4495ed2df3872fb31b26781e4e077eb4", - "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", + "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", "shasum": "" }, "require": { - "php": ">=7.2" + "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "^8.2" + "phpunit/phpunit": "^7.5" + }, + "suggest": { + "ext-posix": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "4.2-dev" } }, "autoload": { @@ -5506,15 +5712,19 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "email": "sebastian@phpunit.de" } ], - "description": "Collection of value objects that represent the types of the PHP type system", - "homepage": "https://github.com/sebastianbergmann/type", + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], "support": { - "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/1.1.4" + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/4.2.4" }, "funding": [ { @@ -5522,29 +5732,34 @@ "type": "github" } ], - "time": "2020-11-30T07:25:11+00:00" + "time": "2020-11-30T07:53:42+00:00" }, { - "name": "sebastian/version", - "version": "2.0.1", + "name": "sebastian/exporter", + "version": "3.1.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "6b853149eab67d4da22291d36f5b0631c0fd856e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/6b853149eab67d4da22291d36f5b0631c0fd856e", + "reference": "6b853149eab67d4da22291d36f5b0631c0fd856e", "shasum": "" }, "require": { - "php": ">=5.6" + "php": ">=7.0", + "sebastian/recursion-context": "^3.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^6.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "3.1.x-dev" } }, "autoload": { @@ -5559,593 +5774,461 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" } ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "http://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/master" + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/3.1.3" }, - "time": "2016-10-03T07:35:21+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T07:47:53+00:00" }, { - "name": "symfony/console", - "version": "v5.3.2", + "name": "sebastian/global-state", + "version": "3.0.1", "source": { "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "649730483885ff2ca99ca0560ef0e5f6b03f2ac1" + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "474fb9edb7ab891665d3bfc6317f42a0a150454b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/649730483885ff2ca99ca0560ef0e5f6b03f2ac1", - "reference": "649730483885ff2ca99ca0560ef0e5f6b03f2ac1", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/474fb9edb7ab891665d3bfc6317f42a0a150454b", + "reference": "474fb9edb7ab891665d3bfc6317f42a0a150454b", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php73": "^1.8", - "symfony/polyfill-php80": "^1.15", - "symfony/service-contracts": "^1.1|^2", - "symfony/string": "^5.1" - }, - "conflict": { - "symfony/dependency-injection": "<4.4", - "symfony/dotenv": "<5.1", - "symfony/event-dispatcher": "<4.4", - "symfony/lock": "<4.4", - "symfony/process": "<4.4" - }, - "provide": { - "psr/log-implementation": "1.0" + "php": ">=7.2", + "sebastian/object-reflector": "^1.1.1", + "sebastian/recursion-context": "^3.0" }, "require-dev": { - "psr/log": "~1.0", - "symfony/config": "^4.4|^5.0", - "symfony/dependency-injection": "^4.4|^5.0", - "symfony/event-dispatcher": "^4.4|^5.0", - "symfony/lock": "^4.4|^5.0", - "symfony/process": "^4.4|^5.0", - "symfony/var-dumper": "^4.4|^5.0" + "ext-dom": "*", + "phpunit/phpunit": "^8.0" }, "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" + "ext-uopz": "*" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], - "description": "Eases the creation of beautiful and testable command line interfaces", - "homepage": "https://symfony.com", + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", "keywords": [ - "cli", - "command line", - "console", - "terminal" + "global state" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.3.2" + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/3.0.1" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2021-06-12T09:42:48+00:00" + "time": "2020-11-30T07:43:24+00:00" }, { - "name": "symfony/css-selector", - "version": "v5.3.0", + "name": "sebastian/object-enumerator", + "version": "3.0.4", "source": { "type": "git", - "url": "https://github.com/symfony/css-selector.git", - "reference": "fcd0b29a7a0b1bb5bfbedc6231583d77fea04814" + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/fcd0b29a7a0b1bb5bfbedc6231583d77fea04814", - "reference": "fcd0b29a7a0b1bb5bfbedc6231583d77fea04814", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", + "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", "shasum": "" }, "require": { - "php": ">=7.2.5" + "php": ">=7.0", + "sebastian/object-reflector": "^1.1.1", + "sebastian/recursion-context": "^3.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, "autoload": { - "psr-4": { - "Symfony\\Component\\CssSelector\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Jean-François Simon", - "email": "jeanfrancois.simon@sensiolabs.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], - "description": "Converts CSS selectors to XPath expressions", - "homepage": "https://symfony.com", + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "support": { - "source": "https://github.com/symfony/css-selector/tree/v5.3.0" + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/3.0.4" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2021-05-26T17:40:38+00:00" + "time": "2020-11-30T07:40:27+00:00" }, { - "name": "symfony/deprecation-contracts", - "version": "v2.4.0", + "name": "sebastian/object-reflector", + "version": "1.1.2", "source": { "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627" + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5f38c8804a9e97d23e0c8d63341088cd8a22d627", - "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", + "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "2.4-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "dev-master": "1.1-dev" } }, "autoload": { - "files": [ - "function.php" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], - "description": "A generic function and convention to trigger deprecation notices", - "homepage": "https://symfony.com", + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v2.4.0" + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/1.1.2" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2021-03-23T23:28:01+00:00" + "time": "2020-11-30T07:37:18+00:00" }, { - "name": "symfony/event-dispatcher", - "version": "v5.3.0", + "name": "sebastian/recursion-context", + "version": "3.0.1", "source": { "type": "git", - "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "67a5f354afa8e2f231081b3fa11a5912f933c3ce" + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/67a5f354afa8e2f231081b3fa11a5912f933c3ce", - "reference": "67a5f354afa8e2f231081b3fa11a5912f933c3ce", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/367dcba38d6e1977be014dc4b22f47a484dac7fb", + "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1", - "symfony/event-dispatcher-contracts": "^2", - "symfony/polyfill-php80": "^1.15" - }, - "conflict": { - "symfony/dependency-injection": "<4.4" - }, - "provide": { - "psr/event-dispatcher-implementation": "1.0", - "symfony/event-dispatcher-implementation": "2.0" + "php": ">=7.0" }, "require-dev": { - "psr/log": "~1.0", - "symfony/config": "^4.4|^5.0", - "symfony/dependency-injection": "^4.4|^5.0", - "symfony/error-handler": "^4.4|^5.0", - "symfony/expression-language": "^4.4|^5.0", - "symfony/http-foundation": "^4.4|^5.0", - "symfony/service-contracts": "^1.1|^2", - "symfony/stopwatch": "^4.4|^5.0" - }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" + "phpunit/phpunit": "^6.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, "autoload": { - "psr-4": { - "Symfony\\Component\\EventDispatcher\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" }, { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" } ], - "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", - "homepage": "https://symfony.com", + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v5.3.0" + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/3.0.1" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2021-05-26T17:43:10+00:00" + "time": "2020-11-30T07:34:24+00:00" }, { - "name": "symfony/event-dispatcher-contracts", - "version": "v2.4.0", + "name": "sebastian/resource-operations", + "version": "2.0.2", "source": { "type": "git", - "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "69fee1ad2332a7cbab3aca13591953da9cdb7a11" + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/69fee1ad2332a7cbab3aca13591953da9cdb7a11", - "reference": "69fee1ad2332a7cbab3aca13591953da9cdb7a11", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/31d35ca87926450c44eae7e2611d45a7a65ea8b3", + "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3", "shasum": "" }, "require": { - "php": ">=7.2.5", - "psr/event-dispatcher": "^1" - }, - "suggest": { - "symfony/event-dispatcher-implementation": "" + "php": ">=7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "2.4-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "dev-master": "2.0-dev" } }, "autoload": { - "psr-4": { - "Symfony\\Contracts\\EventDispatcher\\": "" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], - "description": "Generic abstractions related to dispatching event", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.4.0" + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/2.0.2" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2021-03-23T23:28:01+00:00" + "time": "2020-11-30T07:30:19+00:00" }, { - "name": "symfony/finder", - "version": "v5.3.0", + "name": "sebastian/type", + "version": "1.1.4", "source": { "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "0ae3f047bed4edff6fd35b26a9a6bfdc92c953c6" + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/0ae3f047bed4edff6fd35b26a9a6bfdc92c953c6", - "reference": "0ae3f047bed4edff6fd35b26a9a6bfdc92c953c6", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/0150cfbc4495ed2df3872fb31b26781e4e077eb4", + "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4", "shasum": "" }, "require": { - "php": ">=7.2.5" + "php": ">=7.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.2" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, "autoload": { - "psr-4": { - "Symfony\\Component\\Finder\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Finds files and directories via an intuitive fluent interface", - "homepage": "https://symfony.com", + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", "support": { - "source": "https://github.com/symfony/finder/tree/v5.3.0" + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/1.1.4" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2021-05-26T12:52:38+00:00" + "time": "2020-11-30T07:25:11+00:00" }, { - "name": "symfony/polyfill-intl-grapheme", - "version": "v1.23.0", + "name": "sebastian/version", + "version": "2.0.1", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "24b72c6baa32c746a4d0840147c9715e42bb68ab" + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/24b72c6baa32c746a4d0840147c9715e42bb68ab", - "reference": "24b72c6baa32c746a4d0840147c9715e42bb68ab", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", "shasum": "" }, "require": { - "php": ">=7.1" - }, - "suggest": { - "ext-intl": "For best performance" + "php": ">=5.6" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Intl\\Grapheme\\": "" - }, - "files": [ - "bootstrap.php" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Symfony polyfill for intl's grapheme_* functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "grapheme", - "intl", - "polyfill", - "portable", - "shim" - ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.23.0" + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/master" }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-05-27T09:17:38+00:00" + "time": "2016-10-03T07:35:21+00:00" }, { - "name": "symfony/polyfill-intl-normalizer", - "version": "v1.23.0", + "name": "symfony/css-selector", + "version": "v5.3.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8" + "url": "https://github.com/symfony/css-selector.git", + "reference": "fcd0b29a7a0b1bb5bfbedc6231583d77fea04814" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8", - "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/fcd0b29a7a0b1bb5bfbedc6231583d77fea04814", + "reference": "fcd0b29a7a0b1bb5bfbedc6231583d77fea04814", "shasum": "" }, "require": { - "php": ">=7.1" - }, - "suggest": { - "ext-intl": "For best performance" + "php": ">=7.2.5" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, "autoload": { "psr-4": { - "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + "Symfony\\Component\\CssSelector\\": "" }, - "files": [ - "bootstrap.php" - ], - "classmap": [ - "Resources/stubs" + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -6154,26 +6237,22 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for intl's Normalizer class and related functions", + "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "intl", - "normalizer", - "polyfill", - "portable", - "shim" - ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.23.0" + "source": "https://github.com/symfony/css-selector/tree/v5.3.0" }, "funding": [ { @@ -6189,44 +6268,56 @@ "type": "tidelift" } ], - "time": "2021-02-19T12:13:01+00:00" + "time": "2021-05-26T17:40:38+00:00" }, { - "name": "symfony/polyfill-php73", - "version": "v1.23.0", + "name": "symfony/event-dispatcher", + "version": "v5.3.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010" + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "67a5f354afa8e2f231081b3fa11a5912f933c3ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fba8933c384d6476ab14fb7b8526e5287ca7e010", - "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/67a5f354afa8e2f231081b3fa11a5912f933c3ce", + "reference": "67a5f354afa8e2f231081b3fa11a5912f933c3ce", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1", + "symfony/event-dispatcher-contracts": "^2", + "symfony/polyfill-php80": "^1.15" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } + "conflict": { + "symfony/dependency-injection": "<4.4" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "^4.4|^5.0", + "symfony/dependency-injection": "^4.4|^5.0", + "symfony/error-handler": "^4.4|^5.0", + "symfony/expression-language": "^4.4|^5.0", + "symfony/http-foundation": "^4.4|^5.0", + "symfony/service-contracts": "^1.1|^2", + "symfony/stopwatch": "^4.4|^5.0" }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "type": "library", "autoload": { "psr-4": { - "Symfony\\Polyfill\\Php73\\": "" + "Symfony\\Component\\EventDispatcher\\": "" }, - "files": [ - "bootstrap.php" - ], - "classmap": [ - "Resources/stubs" + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -6235,24 +6326,18 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.23.0" + "source": "https://github.com/symfony/event-dispatcher/tree/v5.3.0" }, "funding": [ { @@ -6268,33 +6353,33 @@ "type": "tidelift" } ], - "time": "2021-02-19T12:13:01+00:00" + "time": "2021-05-26T17:43:10+00:00" }, { - "name": "symfony/service-contracts", - "version": "v2.2.0", + "name": "symfony/event-dispatcher-contracts", + "version": "v2.4.0", "source": { "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1" + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "69fee1ad2332a7cbab3aca13591953da9cdb7a11" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d15da7ba4957ffb8f1747218be9e1a121fd298a1", - "reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/69fee1ad2332a7cbab3aca13591953da9cdb7a11", + "reference": "69fee1ad2332a7cbab3aca13591953da9cdb7a11", "shasum": "" }, "require": { "php": ">=7.2.5", - "psr/container": "^1.0" + "psr/event-dispatcher": "^1" }, "suggest": { - "symfony/service-implementation": "" + "symfony/event-dispatcher-implementation": "" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.2-dev" + "dev-main": "2.4-dev" }, "thanks": { "name": "symfony/contracts", @@ -6303,7 +6388,7 @@ }, "autoload": { "psr-4": { - "Symfony\\Contracts\\Service\\": "" + "Symfony\\Contracts\\EventDispatcher\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -6320,7 +6405,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Generic abstractions related to writing services", + "description": "Generic abstractions related to dispatching event", "homepage": "https://symfony.com", "keywords": [ "abstractions", @@ -6331,7 +6416,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/master" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.4.0" }, "funding": [ { @@ -6347,44 +6432,30 @@ "type": "tidelift" } ], - "time": "2020-09-07T11:33:47+00:00" + "time": "2021-03-23T23:28:01+00:00" }, { - "name": "symfony/string", - "version": "v5.3.2", + "name": "symfony/finder", + "version": "v5.3.0", "source": { "type": "git", - "url": "https://github.com/symfony/string.git", - "reference": "0732e97e41c0a590f77e231afc16a327375d50b0" + "url": "https://github.com/symfony/finder.git", + "reference": "0ae3f047bed4edff6fd35b26a9a6bfdc92c953c6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/0732e97e41c0a590f77e231afc16a327375d50b0", - "reference": "0732e97e41c0a590f77e231afc16a327375d50b0", + "url": "https://api.github.com/repos/symfony/finder/zipball/0ae3f047bed4edff6fd35b26a9a6bfdc92c953c6", + "reference": "0ae3f047bed4edff6fd35b26a9a6bfdc92c953c6", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.0", - "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "~1.15" - }, - "require-dev": { - "symfony/error-handler": "^4.4|^5.0", - "symfony/http-client": "^4.4|^5.0", - "symfony/translation-contracts": "^1.1|^2", - "symfony/var-exporter": "^4.4|^5.0" + "php": ">=7.2.5" }, "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\String\\": "" + "Symfony\\Component\\Finder\\": "" }, - "files": [ - "Resources/functions.php" - ], "exclude-from-classmap": [ "/Tests/" ] @@ -6395,26 +6466,18 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", - "keywords": [ - "grapheme", - "i18n", - "string", - "unicode", - "utf-8", - "utf8" - ], "support": { - "source": "https://github.com/symfony/string/tree/v5.3.2" + "source": "https://github.com/symfony/finder/tree/v5.3.0" }, "funding": [ { @@ -6430,7 +6493,7 @@ "type": "tidelift" } ], - "time": "2021-06-06T09:51:56+00:00" + "time": "2021-05-26T12:52:38+00:00" }, { "name": "theseer/tokenizer", @@ -6623,5 +6686,5 @@ "ext-mbstring": "*" }, "platform-dev": [], - "plugin-api-version": "2.0.0" + "plugin-api-version": "2.1.0" } diff --git a/lib/bootstrap.php b/lib/bootstrap.php index 3230562e9c5d764bd24c8f84fea3dadcd45132ee..a50c4fb3a6e00a34f078b2a128415d1cb4d91d63 100644 --- a/lib/bootstrap.php +++ b/lib/bootstrap.php @@ -97,7 +97,7 @@ if (Studip\ENV === 'development' && !in_array('ASSETS_URL', $added) && function_ } } -if (!file_exists($GLOBALS['STUDIP_BASE_PATH'] . '/config/config_local.inc.php')) { +if (!file_exists($GLOBALS['STUDIP_BASE_PATH'] . '/config/config_local.inc.php') && php_sapi_name() !== 'cli') { require_once __DIR__ . '/classes/URLHelper.php'; URLHelper::setBaseUrl($GLOBALS['ABSOLUTE_URI_STUDIP']); diff --git a/lib/classes/StudipPDO.class.php b/lib/classes/StudipPDO.class.php index 10738a716444e33b77dd9d001177a0804bb86355..6356222aa03a0438448440d7b59efbc00ee94e6d 100644 --- a/lib/classes/StudipPDO.class.php +++ b/lib/classes/StudipPDO.class.php @@ -389,4 +389,14 @@ class StudipPDO extends PDO $st->execute($input_parameters); return $st->fetchColumn($column); } + + /** + * Determine if the connected database is a MariaDB database. + * + * @return bool + */ + public function isMariaDB(): bool + { + return stripos($this->getAttribute(\PDO::ATTR_SERVER_VERSION), 'MariaDB') !== false; + } }