addonPath = $addonPath; $this->database = $database; $this->config = $config; $this->cache = $cache; $this->logger = $logger; $this->profiler = $profiler; $this->proxy = new AddonProxy($addonPath); } /** * Returns the absolute path to the addon folder * * e.g. `/var/www/html/addon` */ public function getAddonPath(): string { return $this->addonPath; } /** * Returns the list of available addons. * * This list is made from scanning the addon/ folder. * Unsupported addons are excluded unless they already are enabled or system.show_unsupported_addon is set. * * @return string[] */ public function getAvailableAddons(): array { $dirs = scandir($this->getAddonPath()); if (!is_array($dirs)) { return []; } $files = []; foreach ($dirs as $dirname) { if (in_array($dirname, ['.', '..'])) { continue; } if (!is_dir($this->getAddonPath() . '/' . $dirname)) { continue; } $files[] = $dirname; } $addons = []; foreach ($files as $addonId) { $addonInfo = $this->getAddonInfo($addonId); if ( $this->config->get('system', 'show_unsupported_addons') || strtolower($addonInfo->getStatus()) !== 'unsupported' || $this->isAddonEnabled($addonId) ) { $addons[] = $addonId; } } return $addons; } /** * Installs an addon. * * @param string $addonId name of the addon * * @return bool true on success or false on failure */ public function installAddon(string $addonId): bool { $addonId = Strings::sanitizeFilePathItem($addonId); $addon_file_path = $this->getAddonPath() . '/' . $addonId . '/' . $addonId . '.php'; // silently fail if addon was removed or if $addonId is funky if (!file_exists($addon_file_path)) { return false; } $this->logger->debug("Addon {addon}: {action}", ['action' => 'install', 'addon' => $addonId]); $timestamp = @filemtime($addon_file_path); @include_once($addon_file_path); if (function_exists($addonId . '_install')) { $func = $addonId . '_install'; $func(); } $this->config->set('addons', $addonId, [ 'last_update' => $timestamp, 'admin' => function_exists($addonId . '_addon_admin'), ]); if (!$this->isAddonEnabled($addonId)) { $this->addons[] = $addonId; } return true; } /** * Uninstalls an addon. * * @param string $addonId name of the addon */ public function uninstallAddon(string $addonId): void { $addonId = Strings::sanitizeFilePathItem($addonId); $this->logger->debug("Addon {addon}: {action}", ['action' => 'uninstall', 'addon' => $addonId]); $this->config->delete('addons', $addonId); $addon_file_path = $this->getAddonPath() . '/' . $addonId . '/' . $addonId . '.php'; @include_once($addon_file_path); if (function_exists($addonId . '_uninstall')) { $func = $addonId . '_uninstall'; $func(); } // Remove registered hooks for the addon // Handles both relative and absolute file paths $condition = ['`file` LIKE ?', "%/$addonId/$addonId.php"]; $result = $this->database->delete('hook', $condition); if ($result) { $this->cache->delete('routerDispatchData'); } unset($this->addons[array_search($addonId, $this->addons)]); } /** * Load addons. * * @internal */ public function loadAddons(): void { $this->addons = array_keys(array_filter($this->config->get('addons') ?? [])); } /** * Reload (uninstall and install) all updated addons. */ public function reloadAddons(): void { $this->proxy->reloadAddons(); } /** * Get the comment block of an addon as value object. */ public function getAddonInfo(string $addonId): AddonInfo { $default = [ 'id' => $addonId, 'name' => $addonId, ]; if (!is_file($this->getAddonPath() . "/$addonId/$addonId.php")) { return AddonInfo::fromArray($default); } $this->profiler->startRecording('file'); $raw = file_get_contents($this->getAddonPath() . "/$addonId/$addonId.php"); $this->profiler->stopRecording(); return AddonInfo::fromString($addonId, $raw); } /** * Checks if the provided addon is enabled */ public function isAddonEnabled(string $addonId): bool { return in_array($addonId, $this->addons); } /** * Returns a list with the IDs of the enabled addons * * @return string[] */ public function getEnabledAddons(): array { return $this->addons; } /** * Returns a list with the IDs of the non-hidden enabled addons * * @return string[] */ public function getVisibleEnabledAddons(): array { $visible_addons = []; $addons = array_filter($this->config->get('addons') ?? []); foreach ($addons as $name => $data) { $visible_addons[] = $name; } return $visible_addons; } /** * Returns a list with the IDs of the enabled addons that provides admin settings. * * @return string[] */ public function getEnabledAddonsWithAdminSettings(): array { $addons_admin = []; $addons = array_filter($this->config->get('addons') ?? []); ksort($addons); foreach ($addons as $name => $data) { if (array_key_exists('admin', $data) && $data['admin'] === true) { $addons_admin[] = $name; } } return $addons_admin; } }