modulesPath = config('modules.paths.modules'); } /** * Execute the console command. */ public function handle() { $this->moduleName = $name = $this->argument('name'); $this->parentModuleId = $this->argument('parentModuleId'); // invalid module name provided if (!is_dir($this->modulesPath . DIRECTORY_SEPARATOR . $name)) { throw new InvalidArgumentException('Invalid module ' . $name . '. No directory under ' . $this->modulesPath . DIRECTORY_SEPARATOR . $name . ' found!'); } // get config $config = $this->loadConfig(); if (empty($config)) { return; } // check if it is valid $this->checkMandatoryFields($config); // all mandatory fields are present, we can write it into database and create modules.json $this->installModule($config); // delete cache Cache::forget('enabled-modules'); Cache::forget('module-handler-to-id-enumeration'); exec('php artisan module:enable '.$name); exec('php artisan cache:clear'); exec('composer dump-autoload'); // inform user $this->line('Module ' . $name . ' successfuly installed.'); } /** * Load config from Config/config.php in module files. We cannot use config() because the file have not published * yet. * @return array * @throws FileNotFoundException */ private function loadConfig() { $basePath = $this->modulesPath . DIRECTORY_SEPARATOR . $this->moduleName . DIRECTORY_SEPARATOR; $path = $basePath . 'Config' . DIRECTORY_SEPARATOR . 'config.php'; if (basename($basePath) == 'Database') { return []; } // check if module has Config/config.php file if (!file_exists($path)) { $path = $basePath . 'info.php'; // does module has info.php? If not, throw error, because we have no info about this module if (!file_exists($path)) { throw new FileNotFoundException('Missing config file. Create /Config/config.php or /info.php (for module with submodules) in ' . $basePath); } } // we cannot rely on config() helper, because this module does not have to be published $config = include($path); return $config; } /** * Checks if config contains all mandatory entries. * * @param $config */ private function checkMandatoryFields($config) { $fields = ['name']; foreach ($fields as $field) { if (!isset($config[$field])) { throw new MissingMandatoryParametersException('Missing *' . $field . '* in config file! Cannot install ' . $this->moduleName); } } } /** * Installs module into database. * This method calls Module::install, which handles specific installation of module. In this way we should be able * to install any module even with some other database structures. * * @param $config * * @return Module * @throws \ErrorException * @throws \Exception */ private function installModule($config) { $name = isset($config['translatedName']) ? $config['translatedName'] : $config['name']; $description = isset($config['translatedDescription']) ? $config['translatedDescription'] : null; $alias = isset($config['alias']) ? $config['alias'] : null; $handler = isset($config['handler']) ? $config['handler'] : null; $providers = isset($config['providers']) ? $config['providers'] : []; // check if module is not installed yet $module = Module::where('name', $name); if ($handler) { $module = $module->where('handler', $handler); } $module = $module->first(); if ($module && (!isset($config['hasSubmodules']) || !$config['hasSubmodules'])) { throw new \Exception('Module ' . $config['name'] . ' already exists in database.'); } if (is_null($module)) { // insert module into database $settings = [ 'providers' => $providers, 'order' => isset($config['order']) ? $config['order'] : 0, 'files' => isset($config['files']) ? $config['files'] : [] ]; $module = Installer::install($name, $alias, $this->moduleName, $description, $handler, $this->parentModuleId, $settings); } // try to install submodules if (isset($config['hasSubmodules']) && $config['hasSubmodules']) { $this->installSubmodules($this->modulesPath . DIRECTORY_SEPARATOR . $this->moduleName, $module); } } /** * Crawl all subdirectories in module and try to install submodules * * @param $path string path to module base directory * @param $parentModule */ private function installSubmodules($path, Module $parentModule) { $submodules = File::directories($path); foreach ($submodules as $submodulePath) { $submodule = str_replace($this->modulesPath . DIRECTORY_SEPARATOR, '', $submodulePath); try { $this->call('module:install', ['name' => $submodule, 'parentModuleId' => $parentModule->id]); } catch (\Exception $e) { $this->error($e->getMessage()); } } } }