diff --git a/Dockerfile.phpdoc b/Dockerfile.phpdoc index d39ad7891f..60470c5ab6 100644 --- a/Dockerfile.phpdoc +++ b/Dockerfile.phpdoc @@ -8,10 +8,7 @@ RUN mkdir /target && \ phpdoc run \ -d 'core/classes' \ -d 'modules/Core/classes' \ - -d 'modules/Discord Integration/classes' \ - -d 'modules/Cookie Consent/classes' \ -d 'modules/Forum/classes' \ - -d 'modules/Members/classes' \ -i vendor \ -t /target diff --git a/composer.json b/composer.json index 465a7f960a..ac1066bff8 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,10 @@ "joypixels/emoji-toolkit": "^7.0", "geoip2/geoip2": "^2.13", "jenssegers/agent": "^2.6", - "php-di/php-di": "^6.4" + "illuminate/container": "^8.0", + "namelessmc/members-module": "dev-main", + "namelessmc/cookie-consent-module": "dev-main", + "namelessmc/discord-integration-module": "dev-main" }, "require-dev": { "phpstan/phpstan": "1.6.9", @@ -44,17 +47,12 @@ "autoload": { "classmap": [ "core/classes", - "modules/Cookie Consent/classes", "modules/Core/classes", "modules/Core/hooks", "modules/Core/widgets", - "modules/Discord Integration/classes", - "modules/Discord Integration/hooks", - "modules/Discord Integration/widgets", "modules/Forum/classes", "modules/Forum/hooks", - "modules/Forum/widgets", - "modules/Members/classes" + "modules/Forum/widgets" ] } } diff --git a/core/classes/Core/Cache.php b/core/classes/Core/Cache.php index f55c2db5f1..4743e7d3cd 100644 --- a/core/classes/Core/Cache.php +++ b/core/classes/Core/Cache.php @@ -295,6 +295,18 @@ public function retrieveAll(bool $meta = false): array return $results; } + public function fetch(string $key, callable $callback, int $expiration = 0) + { + if ($this->isCached($key)) { + return $this->retrieve($key); + } + + $data = $callback(); + $this->store($key, $data, $expiration); + + return $data; + } + /** * Erase cached entry by its key. * diff --git a/core/classes/Core/ComposerModuleDiscovery.php b/core/classes/Core/ComposerModuleDiscovery.php new file mode 100644 index 0000000000..b50b8e6caa --- /dev/null +++ b/core/classes/Core/ComposerModuleDiscovery.php @@ -0,0 +1,65 @@ +getName(), array_column($allEnabledModules, 'name'))) { + continue; + } + + self::bootModule($container, $composerModule); + } + } + + public static function bootModule(\Illuminate\Container\Container $container, ComposerModuleWrapper $composerModule): void + { + /** @var NamelessMC\Framework\Extend\BaseExtender[] $extenders */ + $extenders = require_once ROOT_PATH . '/vendor/' . $composerModule->getPackageName() . '/module.php'; + foreach ($extenders as $extender) { + $extender->setModule($composerModule)->extend($container); + } + } + + public static function fromPackage(array $composerPackage): ComposerModuleWrapper + { + return new ComposerModuleWrapper( + $composerPackage['name'], + $composerPackage['extra']['nameless_module']['name'], + $composerPackage['extra']['nameless_module']['display_name'], + $composerPackage['authors'][0]['name'], + $composerPackage['authors'][0]['homepage'], + $composerPackage['extra']['nameless_module']['version'], + $composerPackage['extra']['nameless_module']['nameless_version'], + $composerPackage['source']['url'], + ); + } +} diff --git a/core/classes/Core/ComposerModuleWrapper.php b/core/classes/Core/ComposerModuleWrapper.php new file mode 100644 index 0000000000..848130936a --- /dev/null +++ b/core/classes/Core/ComposerModuleWrapper.php @@ -0,0 +1,180 @@ +_packageName = $packageName; + $this->_privateName = $privateName; + $this->_authorName = $authorName; + $this->_authorHomepage = $authorHomepage; + $this->_repositoryUrl = $repositoryUrl; + + parent::__construct($this, $displayName, $authorName, $moduleVersion, $namelessVersion); + } + + public function getPackageName(): string + { + return $this->_packageName; + } + + public function getPrivateName(): string + { + return $this->_privateName; + } + + public function getRepositoryUrl(): string + { + return $this->_repositoryUrl; + } + + public function getAuthor(): string + { + return "{$this->_authorName}"; + } + + public function setOnInstall(array $callbacks): void + { + $this->_onInstall = $callbacks; + } + + public function setOnEnable(array $callbacks): void + { + $this->_onEnable = $callbacks; + } + + public function setOnDisable(array $callbacks): void + { + $this->_onDisable = $callbacks; + } + + public function setOnUninstall(array $callbacks): void + { + $this->_onUninstall = $callbacks; + } + + public function setDebugInfoProvider(string $provider): void + { + $this->_debugInfoProvider = $provider; + } + + public function onPageLoad(User $user, Pages $pages, Cache $cache, Smarty $smarty, iterable $navs, Widgets $widgets, ?TemplateBase $template) + { + // ... + } + + public function onInstall() + { + $this->runMigrations(); + $this->callLifecycleHooks($this->_onInstall); + } + + public function onEnable() + { + $this->callLifecycleHooks($this->_onEnable); + } + + public function onDisable() + { + $this->callLifecycleHooks($this->_onDisable); + } + + public function onUninstall() + { + // TODO, should this be before or after + $this->rollbackMigrations(); + $this->callLifecycleHooks($this->_onUninstall); + } + + public function getDebugInfo(): array + { + if (!$this->_debugInfoProvider) { + return []; + } + + /** @var \NamelessMC\Framework\Debugging\DebugInfoProvider */ + $provider = Container::getInstance()->make($this->_debugInfoProvider); + + return $provider->provide(); + } + + public function frontendViewsPath(): string + { + return ROOT_PATH . '/vendor/' . $this->_packageName . '/views'; + } + + public function hasFrontendViews(): bool + { + return file_exists($this->frontendViewsPath()); + } + + public function panelViewsPath(): string + { + return ROOT_PATH . '/vendor/' . $this->_packageName . '/panel_views'; + } + + public function hasPanelViews(): bool + { + return file_exists($this->panelViewsPath()); + } + + private function runMigrations(): void + { + if (!$this->hasMigrations()) { + return; + } + + PhinxAdapter::migrate($this->getPrivateName(), $this->migrationsPath()); + } + + private function rollbackMigrations(): void + { + if (!$this->hasMigrations()) { + return; + } + + PhinxAdapter::rollback($this->getPrivateName(), $this->migrationsPath()); + } + + private function migrationsPath(): string + { + return ROOT_PATH . '/vendor/' . $this->_packageName . '/migrations'; + } + + private function hasMigrations(): bool + { + return file_exists($this->migrationsPath()); + } + + private function callLifecycleHooks(array $hooks): void + { + foreach ($hooks as $callback) { + /** @var \NamelessMC\Framework\ModuleLifecycle\Hook $hook */ + $hook = Container::getInstance()->make($callback); + $hook->execute(); + } + } +} diff --git a/core/classes/Core/Navigation.php b/core/classes/Core/Navigation.php index 185ea56b0e..7138b89be5 100644 --- a/core/classes/Core/Navigation.php +++ b/core/classes/Core/Navigation.php @@ -24,6 +24,8 @@ class Navigation */ private bool $_panel; + private array $_preloaded_dropdowns = []; + public function __construct(bool $panel = false) { $this->_panel = $panel; @@ -49,19 +51,6 @@ public function add( float $order = 10, ?string $icon = '' ): void { - if ($this->_panel && $location == 'top') { - // Discard order - // TODO: only a temporary solution to the link conflict issue in the StaffCP - if (count($this->_topNavbar)) { - $key = array_keys($this->_topNavbar)[count($this->_topNavbar) - 1]; - $previous_order = $this->_topNavbar[$key]['order']; - } else { - $previous_order = 0; - } - - $order = $previous_order + 1; - } - // Add the link to the navigation if ($location === 'top') { // Add to top navbar @@ -143,15 +132,28 @@ public function addDropdown(string $name, string $title, string $location = 'top public function addItemToDropdown(string $dropdown, string $name, string $title, string $link, string $location = 'top', string $target = null, string $icon = '', int $order = 10): void { // Add the item - if ($location == 'top' && isset($this->_topNavbar[$dropdown])) { - // Navbar - $this->_topNavbar[$dropdown]['items'][$name] = [ - 'title' => $title, - 'link' => $link, - 'target' => $target, - 'icon' => $icon, - 'order' => $order, - ]; + if ($location == 'top') { + if (isset($this->_topNavbar[$dropdown])) { + // Navbar + $this->_topNavbar[$dropdown]['items'][$name] = [ + 'title' => $title, + 'link' => $link, + 'target' => $target, + 'icon' => $icon, + 'order' => $order, + ]; + } else { + // Dropdown not found + if (!isset($this->_preloaded_dropdowns[$dropdown])) { + $this->_preloaded_dropdowns[$dropdown]['items'][$name] = [ + 'title' => $title, + 'link' => $link, + 'items' => [], + 'icon' => $icon, + 'order' => $order, + ]; + } + } } elseif (isset($this->_footerNav[$dropdown])) { // Footer $this->_footerNav[$dropdown]['items'][$name] = [ @@ -172,6 +174,15 @@ public function addItemToDropdown(string $dropdown, string $name, string $title, */ public function returnNav(string $location = 'top'): array { + // merge preloaded dropdowns + foreach ($this->_preloaded_dropdowns as $key => $dropdown) { + if ($location == 'top') { + if (isset($this->_topNavbar[$key])) { + $this->_topNavbar[$key]['items'] = array_merge($this->_topNavbar[$key]['items'], $dropdown['items']); + } + } + } + $return = []; // String to return if ($location == 'top') { if (count($this->_topNavbar)) { diff --git a/core/classes/Core/Pages.php b/core/classes/Core/Pages.php index 88e0309ac6..243466627a 100644 --- a/core/classes/Core/Pages.php +++ b/core/classes/Core/Pages.php @@ -42,14 +42,18 @@ class Pages * @param string $file Path (from module folder) to page file. * @param string $name Name of page. * @param bool $widgets Can widgets be used on the page? Default false. + * @param bool $controllerBased Is the page controller based? Default false. */ - public function add(string $module, string $url, string $file, string $name = '', bool $widgets = false): void + public function add(string $module, string $url, string $file, string $name = '', bool $widgets = false, string $moduleSafeName = null, bool $controllerBased = false, string $safeName = ''): void { $this->_pages[$url] = [ 'module' => $module, + 'moduleSafeName' => $moduleSafeName, 'file' => $file, 'name' => $name, + 'safeName' => $safeName, 'widgets' => $widgets, + 'controllerBased' => $controllerBased, 'id' => $this->_id++, ]; } diff --git a/core/classes/Database/PhinxAdapter.php b/core/classes/Database/PhinxAdapter.php index fea7a6f2a6..6ae3662c65 100644 --- a/core/classes/Database/PhinxAdapter.php +++ b/core/classes/Database/PhinxAdapter.php @@ -10,14 +10,12 @@ class PhinxAdapter * * @param string $module Module name * @param ?string $migrationDir Migration directory - * @param bool $returnResults If true the results will be returned - otherwise script execution is ended * * @return array|void */ public static function ensureUpToDate( string $module, - ?string $migrationDir = null, - bool $returnResults = false + ?string $migrationDir = null ) { $module = strtolower($module); @@ -52,13 +50,6 @@ static function ($file_name) { $missing = array_diff($migration_files, $migration_database_entries); $extra = array_diff($migration_database_entries, $migration_files); - if ($returnResults) { - return [ - 'missing' => count($missing), - 'extra' => count($extra), - ]; - } - // Likely a pull from the repo dev branch or migrations // weren't run during an upgrade script. if (($missing_count = count($missing)) > 0) { diff --git a/core/classes/Debugging/DebugInfoProvider.php b/core/classes/Debugging/DebugInfoProvider.php new file mode 100644 index 0000000000..e55cfdb6e8 --- /dev/null +++ b/core/classes/Debugging/DebugInfoProvider.php @@ -0,0 +1,8 @@ +getFilename()); - try { - /** @var EndpointBase $endpoint */ - $endpoint = new $endpoint_class_name(); + $this->loadEndpoint($endpoint_class_name); + } + } - $key = $endpoint->getRoute() . '-' . $endpoint->getMethod(); + public function loadEndpoint(string $endpoint): void + { + try { + /** @var EndpointBase $endpoint */ + $endpoint = new $endpoint(); - if (!isset($this->_endpoints[$key])) { - $this->_endpoints[$key] = $endpoint; - } - } catch (Error $error) { - // Silently ignore errors caused by invalid endpoint files, - // but make a log entry for debugging purposes. - ErrorHandler::logCustomError($error->getMessage()); + $key = $endpoint->getRoute() . '-' . $endpoint->getMethod(); + + if (!isset($this->_endpoints[$key])) { + $this->_endpoints[$key] = $endpoint; } + } catch (Error $error) { + // Silently ignore errors caused by invalid endpoint files, + // but make a log entry for debugging purposes. + ErrorHandler::logCustomError($error->getMessage()); } } } diff --git a/core/classes/Events/EventHandler.php b/core/classes/Events/EventHandler.php index 4281926182..570b176c63 100644 --- a/core/classes/Events/EventHandler.php +++ b/core/classes/Events/EventHandler.php @@ -1,4 +1,5 @@ make($callback), 'handle']; + } } self::$_events[$name]['listeners'][] = [ @@ -243,7 +253,7 @@ public static function getEvents(bool $showInternal = false): array * Not used internally, currently for WebSend. * * @param string $event Name of event to get data for. - * @returns array Event data. + * @return array Event data. */ public static function getEvent(string $event): array { diff --git a/core/classes/Events/Listener.php b/core/classes/Events/Listener.php new file mode 100644 index 0000000000..3a2fb05de3 --- /dev/null +++ b/core/classes/Events/Listener.php @@ -0,0 +1,9 @@ +module = $module; + + $this->moduleName = $module->getPrivateName(); + $this->moduleDisplayName = $module->getName(); + + return $this; + } + + abstract public function extend(\Illuminate\Container\Container $container): void; + +} \ No newline at end of file diff --git a/core/classes/Extend/Container.php b/core/classes/Extend/Container.php new file mode 100644 index 0000000000..66228a6962 --- /dev/null +++ b/core/classes/Extend/Container.php @@ -0,0 +1,22 @@ +singletons as $class) { + $container->singleton($class); + } + } + + public function singleton(string $class): Container { + $this->singletons[] = $class; + + return $this; + } +} \ No newline at end of file diff --git a/core/classes/Extend/DebugInfo.php b/core/classes/Extend/DebugInfo.php new file mode 100644 index 0000000000..585c4dc6c3 --- /dev/null +++ b/core/classes/Extend/DebugInfo.php @@ -0,0 +1,20 @@ +module->setDebugInfoProvider($this->provider); + } + + public function provide(string $provider): DebugInfo { + $this->provider = $provider; + + return $this; + } +} \ No newline at end of file diff --git a/core/classes/Extend/Endpoints.php b/core/classes/Extend/Endpoints.php new file mode 100644 index 0000000000..26f5ec2048 --- /dev/null +++ b/core/classes/Extend/Endpoints.php @@ -0,0 +1,25 @@ +get(\Endpoints::class); + + foreach ($this->endpoints as $endpoint) { + $endpoints->loadEndpoint($endpoint); + } + } + + public function register(string $injector): self { + $this->endpoints[] = $injector; + + return $this; + } +} \ No newline at end of file diff --git a/core/classes/Extend/Events.php b/core/classes/Extend/Events.php new file mode 100644 index 0000000000..3da0a0e8aa --- /dev/null +++ b/core/classes/Extend/Events.php @@ -0,0 +1,39 @@ +events as $event) { + \EventHandler::registerEvent($event); + } + + foreach ($this->listeners as $event => $listeners) { + foreach ($listeners as $listener) { + \EventHandler::registerListener($event, $listener, 10); + } + } + } + + public function register(string $event): Events { + $this->events[] = $event; + + return $this; + } + + public function listen(string $event, string $listener): Events { + if (!isset($this->listeners[$event])) { + $this->listeners[$event] = []; + } + + $this->listeners[$event][] = $listener; + + return $this; + } +} \ No newline at end of file diff --git a/core/classes/Extend/FrontendAssets.php b/core/classes/Extend/FrontendAssets.php new file mode 100644 index 0000000000..cc4051e754 --- /dev/null +++ b/core/classes/Extend/FrontendAssets.php @@ -0,0 +1,81 @@ +get('FrontendTemplate') since these would be called before the template is intialized + public function extend(Container $container): void { + if ($container->has('FrontendAssets')) { + $frontendAssets = $container->get('FrontendAssets'); + } else { + $frontendAssets = $container->instance('FrontendAssets', []); + } + + // merge in global assets + $frontendAssets['globalJsFiles'] = array_merge($frontendAssets['globalJsFiles'] ?? [], $this->globalJsFiles); + $frontendAssets['globalCssFiles'] = array_merge($frontendAssets['globalCssFiles'] ?? [], $this->globalCssFiles); + + // merge in page specific assets + foreach ($this->jsFiles as $page => $files) { + if (!isset($frontendAssets['jsFiles'][$page])) { + $frontendAssets['jsFiles'][$page] = []; + } + + $frontendAssets['jsFiles'][$page] = array_merge($frontendAssets['jsFiles'][$page], $files); + } + + foreach ($this->cssFiles as $page => $files) { + if (!isset($frontendAssets['cssFiles'][$page])) { + $frontendAssets['cssFiles'][$page] = []; + } + + $frontendAssets['cssFiles'][$page] = array_merge($frontendAssets['cssFiles'][$page], $files); + } + + $container->instance('FrontendAssets', $frontendAssets); + } + + public function js(string $path, array $pages = []): self { + if (empty($pages)) { + $this->globalJsFiles[] = $this->trimPath($path); + } else { + foreach ($pages as $page) { + if (!isset($this->jsFiles[$page])) { + $this->jsFiles[$page] = []; + } + + $this->jsFiles[$page][] = $this->trimPath($path); + } + } + + return $this; + } + + public function css(string $path, array $pages = []): self { + if (empty($pages)) { + $this->globalCssFiles[] = $this->trimPath($path); + } else { + foreach ($pages as $page) { + if (!isset($this->cssFiles[$page])) { + $this->cssFiles[$page] = []; + } + + $this->cssFiles[$page][] = $this->trimPath($path); + } + } + + return $this; + } + + private function trimPath(string $path): string { + return substr($path, strpos($path, '/vendor')); + } +} \ No newline at end of file diff --git a/core/classes/Extend/FrontendMiddleware.php b/core/classes/Extend/FrontendMiddleware.php new file mode 100644 index 0000000000..524ddad10f --- /dev/null +++ b/core/classes/Extend/FrontendMiddleware.php @@ -0,0 +1,29 @@ +has('FrontendMiddleware')) { + $middlewares = $container->get('FrontendMiddleware'); + } else { + $middlewares = $container->instance('FrontendMiddleware', []); + } + + $middlewares = array_merge($middlewares, $this->middlewares); + + $container->instance('FrontendMiddleware', $middlewares); + } + + public function register(string $middleware): self + { + $this->middlewares[] = $middleware; + + return $this; + } +} \ No newline at end of file diff --git a/core/classes/Extend/FrontendPages.php b/core/classes/Extend/FrontendPages.php new file mode 100644 index 0000000000..e967c9ea05 --- /dev/null +++ b/core/classes/Extend/FrontendPages.php @@ -0,0 +1,77 @@ +get("{$this->moduleName}Language"); + + /** @var \Pages */ + $pages = $container->get(\Pages::class); + + /** @var \Cache */ + $cache = $container->get(\Cache::class); + + /** @var \Navigation */ + $frontendNavigation = $container->get('FrontendNavigation'); + + foreach ($this->pages as $page) { + $path = $page['path']; + $name = $page['name']; + $title = $moduleLanguage->get($page['title_translation']); + + $cache->setCache('navbar_order'); + $order = $cache->fetch("{$name}_order", fn () => 5); + + $cache->setCache('navbar_icons'); + $icon = $cache->fetch("{$name}_icon", fn () => ''); + + $cache->setCache('nav_location'); + $location = $cache->fetch("{$name}_location", fn () => 1); + + switch ($location) { + case 1: + // Navbar + $frontendNavigation->add($name, $title, \URL::build($path), 'top', null, $order, $icon); + break; + case 2: + // "More" dropdown + $frontendNavigation->addItemToDropdown('more_dropdown', $name, $title, \URL::build($path), 'top', null, $icon, $order); + break; + case 3: + // Footer + $frontendNavigation->add($name, $title, \URL::build($path), 'footer', null, $order, $icon); + break; + } + + $pages->add( + $this->moduleDisplayName, + $path, + $page['handler'], + $title, + $page['allowWidgets'], + $this->moduleName, + true, + $name, + ); + } + } + + public function register(string $path, string $name, string $titleTranslation, string $handler, bool $allowWidgets): FrontendPages { + $this->pages[] = [ + 'path' => $path, + 'name' => $name, + 'title_translation' => $titleTranslation, + 'handler' => $handler, + 'allowWidgets' => $allowWidgets + ]; + + return $this; + } +} \ No newline at end of file diff --git a/core/classes/Extend/GroupSync.php b/core/classes/Extend/GroupSync.php new file mode 100644 index 0000000000..6b391a5b08 --- /dev/null +++ b/core/classes/Extend/GroupSync.php @@ -0,0 +1,22 @@ +injectors as $injector) { + \GroupSyncManager::getInstance()->registerInjector(new $injector); + } + } + + public function register(string $injector): self { + $this->injectors[] = $injector; + + return $this; + } +} \ No newline at end of file diff --git a/core/classes/Extend/Integrations.php b/core/classes/Extend/Integrations.php new file mode 100644 index 0000000000..3491d7a672 --- /dev/null +++ b/core/classes/Extend/Integrations.php @@ -0,0 +1,22 @@ +integrations as $integration) { + \Integrations::getInstance()->registerIntegration($container->make($integration)); + } + } + + public function register(string $injector): self { + $this->integrations[] = $injector; + + return $this; + } +} \ No newline at end of file diff --git a/core/classes/Extend/Language.php b/core/classes/Extend/Language.php new file mode 100644 index 0000000000..2d1abfbeb8 --- /dev/null +++ b/core/classes/Extend/Language.php @@ -0,0 +1,19 @@ +path = $path; + } + + public function extend(Container $container): void { + $containerKey = "{$this->moduleName}Language"; + + $container->bind($containerKey, fn () => new \Language($this->path)); + } +} \ No newline at end of file diff --git a/core/classes/Extend/ModuleLifecycle.php b/core/classes/Extend/ModuleLifecycle.php new file mode 100644 index 0000000000..acf0f0ba2e --- /dev/null +++ b/core/classes/Extend/ModuleLifecycle.php @@ -0,0 +1,43 @@ +module->setOnInstall($this->onInstall); + $this->module->setOnEnable($this->onEnable); + $this->module->setOnDisable($this->onDisable); + $this->module->setOnUninstall($this->onUninstall); + } + + public function onInstall(string $hook): ModuleLifecycle { + $this->onInstall[] = $hook; + + return $this; + } + + public function onEnable(string $hook): ModuleLifecycle { + $this->onEnable[] = $hook; + + return $this; + } + + public function onDisable(string $hook): ModuleLifecycle { + $this->onDisable[] = $hook; + + return $this; + } + + public function onUninstall(string $hook): ModuleLifecycle { + $this->onUninstall[] = $hook; + + return $this; + } +} \ No newline at end of file diff --git a/core/classes/Extend/PanelPages.php b/core/classes/Extend/PanelPages.php new file mode 100644 index 0000000000..4dc2650743 --- /dev/null +++ b/core/classes/Extend/PanelPages.php @@ -0,0 +1,117 @@ +get("{$this->moduleName}Language"); + + /** @var \Pages */ + $pages = $container->get(\Pages::class); + + /** @var \User */ + $user = $container->get(\User::class); + + /** @var \Cache */ + $cache = $container->get(\Cache::class); + $cache->setCache('panel_sidebar'); + + /** @var \Navigation */ + $panelNavigation = $container->get('PanelNavigation'); + + if (!empty($this->pages)) { + $moduleSidebarOrder = array_reduce(array_filter($cache->retrieveAll(), function ($item) { + return str_ends_with($item, '_order'); + }, ARRAY_FILTER_USE_KEY), function ($carry, $item) { + return $item > $carry ? $item : $carry; + }, 0) + 1; + $panelNavigation->add("{$this->moduleName}_divider", mb_strtoupper($this->moduleDisplayName), 'divider', 'top', null, $moduleSidebarOrder); + + $lastSubPageOrder = $moduleSidebarOrder; + + foreach ($this->pages as $page) { + $path = ltrim($page['path'], '/'); + $path = "/panel/{$path}"; + + $order = $lastSubPageOrder + 0.1; + $lastSubPageOrder = $order; + + $title = $moduleLanguage->get($page['title_translation']); + + if ($user->hasPermission($page['permission'])) { + $icon = ""; + $panelNavigation->add($page['name'], $title, \URL::build($path), 'top', null, $order, $icon); + } + + $this->registerInternalPage($pages, $path, $page['handler'], $title); + } + } + + if (!empty($this->dropdownPages)) { + foreach ($this->dropdownPages as $dropdownName => $dropdownPages) { + foreach ($dropdownPages as $page) { + $path = ltrim($page['path'], '/'); + $path = "/panel/{$path}"; + + $title = $moduleLanguage->get($page['title_translation']); + + if ($user->hasPermission($page['permission'])) { + $icon = ""; + $panelNavigation->addItemToDropdown($dropdownName, $page['name'], $title, \URL::build($path), 'top', null, $icon); + } + + $this->registerInternalPage($pages, $path, $page['handler'], $title); + } + } + } + } + + public function register(string $path, string $name, string $titleTranslation, string $handler, string $permission, string $icon): PanelPages { + $this->pages[] = [ + 'path' => $path, + 'name' => $name, + 'title_translation' => $titleTranslation, + 'handler' => $handler, + 'permission' => $permission, + 'icon' => $icon, + ]; + + return $this; + } + + public function registerInDropdown(string $dropdownName, string $path, string $name, string $titleTranslation, string $handler, string $permission, string $icon): PanelPages { + if (!isset($this->dropdownPages[$dropdownName])) { + $this->dropdownPages[$dropdownName] = []; + } + + $this->dropdownPages[$dropdownName][] = [ + 'path' => $path, + 'name' => $name, + 'title_translation' => $titleTranslation, + 'handler' => $handler, + 'permission' => $permission, + 'icon' => $icon, + ]; + + return $this; + } + + private function registerInternalPage(\Pages $pages, string $path, string $handler, string $title): void { + $pages->add( + $this->moduleDisplayName, + $path, + $handler, + $title, + false, + $this->moduleName, + true, + ); + } +} \ No newline at end of file diff --git a/core/classes/Extend/Permissions.php b/core/classes/Extend/Permissions.php new file mode 100644 index 0000000000..48937f32b1 --- /dev/null +++ b/core/classes/Extend/Permissions.php @@ -0,0 +1,29 @@ +get(\Language::class); + $moduleLanguage = $container->get("{$this->moduleName}Language"); + + if (isset($this->permissions['staffcp'])) { + foreach ($this->permissions['staffcp'] as $permission => $name) { + \PermissionHandler::registerPermissions($coreLanguage->get('moderator', 'staff_cp'), [ + $permission => $this->moduleDisplayName . ' » ' . $moduleLanguage->get($name) + ]); + } + } + } + + public function register(array $permissions): Permissions { + $this->permissions = $permissions; + + return $this; + } +} \ No newline at end of file diff --git a/core/classes/Extend/Queries.php b/core/classes/Extend/Queries.php new file mode 100644 index 0000000000..e09daf4e10 --- /dev/null +++ b/core/classes/Extend/Queries.php @@ -0,0 +1,40 @@ +get(\Pages::class); + + foreach ($this->pages as $page) { + $path = ltrim($page['path'], '/'); + $path = "/queries/{$path}"; + $path = rtrim($path, '/'); + + $pages->add( + $this->moduleDisplayName, + $path, + $page['handler'], + '', + false, + $this->moduleName, + true, + ); + } + } + + public function register(string $path, string $handler): Queries { + $this->pages[] = [ + 'path' => $path, + 'handler' => $handler, + ]; + + return $this; + } +} \ No newline at end of file diff --git a/core/classes/Extend/Sitemap.php b/core/classes/Extend/Sitemap.php new file mode 100644 index 0000000000..c7058f14ee --- /dev/null +++ b/core/classes/Extend/Sitemap.php @@ -0,0 +1,30 @@ +get(\Pages::class); + + foreach ($this->paths as $path) { + $pages->registerSitemapMethod(static function (\SitemapPHP\Sitemap $sitemap) use ($path) { + $sitemap->addItem(\URL::build($path['path']), $path['priority']); + }); + } + } + + public function path(string $path, float $priority = \SitemapPHP\Sitemap::DEFAULT_PRIORITY): self { + $this->paths[] = [ + 'path' => $path, + 'priority' => $priority + ]; + + return $this; + } +} \ No newline at end of file diff --git a/core/classes/Extend/Widgets.php b/core/classes/Extend/Widgets.php new file mode 100644 index 0000000000..2563d9ceb9 --- /dev/null +++ b/core/classes/Extend/Widgets.php @@ -0,0 +1,33 @@ +get(\Pages::class); + + // Skip initialization if we don't need to display any widgets + if (!$pages->getActivePage()['widgets'] && (defined('PANEL_PAGE') && !str_contains(PANEL_PAGE, 'widget'))) { + return; + } + + /** @var \Widgets */ + $widgets = $container->get(\Widgets::class); + + foreach ($this->widgets as $widget) { + $widgets->add($container->make($widget)); + } + } + + public function register(string $widget): self { + $this->widgets[] = $widget; + + return $this; + } +} \ No newline at end of file diff --git a/core/classes/Integrations/HasIntegrationSettings.php b/core/classes/Integrations/HasIntegrationSettings.php new file mode 100644 index 0000000000..d81c63a67b --- /dev/null +++ b/core/classes/Integrations/HasIntegrationSettings.php @@ -0,0 +1,10 @@ + $file) { + if (is_int($href)) { + $href = $file; + $file = []; + } + $this->_css[] = ' $file) { + if (is_int($href)) { + $href = $file; + $file = []; + } + $this->_js[] = ' - - - - diff --git a/custom/panel_templates/Default/sidebar.tpl b/custom/panel_templates/Default/sidebar.tpl index 38537ca5ea..a9595f5005 100644 --- a/custom/panel_templates/Default/sidebar.tpl +++ b/custom/panel_templates/Default/sidebar.tpl @@ -12,38 +12,38 @@ {foreach from=$NAV_LINKS key=name item=item} - {if isset($item.link) && $item.link eq "divider"} - - - {else} - {if isset($item.items)} - - {else} - - {/if} - {/if} + {if isset($item.link) && $item.link eq "divider"} + + + {else} + {if isset($item.items)} + + {else} + + {/if} + {/if} {/foreach} diff --git a/custom/panel_templates/Default/template.php b/custom/panel_templates/Default/template.php index 1aff0469f8..1944babcd7 100644 --- a/custom/panel_templates/Default/template.php +++ b/custom/panel_templates/Default/template.php @@ -518,17 +518,6 @@ public function onPageLoad() $this->addJSScript(Input::createTinyEditor($this->_language, 'InputPlaceholder', null, false, true)); } break; - - case 'member_lists': - $this->addJSScript(' - if ($(\'.js-check-change\').length) { - var changeCheckbox = document.querySelector(\'.js-check-change\'); - - changeCheckbox.onchange = function () { - $(\'#toggleHideBannedUsers\').submit(); - }; - } - '); } } } diff --git a/custom/templates/DefaultRevamp/cookies.tpl b/custom/templates/DefaultRevamp/cookies.tpl deleted file mode 100644 index 7fc8c64fdf..0000000000 --- a/custom/templates/DefaultRevamp/cookies.tpl +++ /dev/null @@ -1,15 +0,0 @@ -{include file='header.tpl'} -{include file='navbar.tpl'} - -

- {$COOKIE_NOTICE_HEADER} -

- -
- {$COOKIE_NOTICE} - -
-
{$UPDATE_SETTINGS}
-
- -{include file='footer.tpl'} \ No newline at end of file diff --git a/custom/templates/DefaultRevamp/members/members.tpl b/custom/templates/DefaultRevamp/members/members.tpl deleted file mode 100644 index f52dcd28cc..0000000000 --- a/custom/templates/DefaultRevamp/members/members.tpl +++ /dev/null @@ -1,228 +0,0 @@ -{include file='header.tpl'} -{include file='navbar.tpl'} - -

- {$MEMBERS} -

- -
- -{if isset($ERROR)} -
- -
-
{$ERROR_TITLE}
- {$ERROR} -
-
-{/if} - -
-
-
- -
-
-

{$FIND_MEMBER}

-
- -
-
-
- {if $GROUPS|count} -
-
-

{$VIEW_GROUP}

-
- -
-
-
- {/if} -
-
-

{$NEW_MEMBERS}

-
-
- {foreach from=$NEW_MEMBERS_VALUE item=member} -
- - {$member->getDisplayname()} - -
- {/foreach} -
-
-
-
-
-
- {if $VIEWING_LIST == "group" || $MEMBER_LISTS_VIEWING|count} -
- {if $VIEWING_LIST == "group"} -
-

{$VIEWING_GROUP.name}

-
-
    -
- {$PAGINATION} -
-
- {else} - {foreach from=$MEMBER_LISTS_VIEWING item=list} -
-

{$list->getFriendlyName()}

-
-
    -
- {if $VIEWING_LIST == "overview"} - {$VIEW_ALL} - {else} - {$PAGINATION} - {/if} -
-
- {/foreach} - {/if} -
- {else} -
{$NO_OVERVIEW_LISTS_ENABLED}
- {/if} -
-
-
- - - -{include file='footer.tpl'} diff --git a/dev/scripts/find_unused_language_terms.sh b/dev/scripts/find_unused_language_terms.sh index 8810c135e7..7db4f42524 100644 --- a/dev/scripts/find_unused_language_terms.sh +++ b/dev/scripts/find_unused_language_terms.sh @@ -6,12 +6,10 @@ then fi UNUSED_TERMS_FOUND=false +# TODO: make this a reusable action for the module specific repos as well FILES=( "modules/Core/language/en_UK.json" "modules/Forum/language/en_UK.json" - "modules/Cookie Consent/language/en_UK.json" - "modules/Discord Integration/language/en_UK.json" - "modules/Members/language/en_UK.json" ) # terms which are too tricky to detect, or are used in a different way WHITELISTED_TERMS=( diff --git a/index.php b/index.php index 2237b70994..1437421691 100644 --- a/index.php +++ b/index.php @@ -106,17 +106,74 @@ $all_pages = $pages->returnPages(); if (array_key_exists($route, $all_pages)) { - $pages->setActivePage($all_pages[$route]); - if (isset($all_pages[$route]['custom'])) { + $active_page = $all_pages[$route]; + $pages->setActivePage($active_page); + if (isset($active_page['custom'])) { require(implode(DIRECTORY_SEPARATOR, [ROOT_PATH, 'modules', 'Core', 'pages', 'custom.php'])); die; } - $path = implode(DIRECTORY_SEPARATOR, [ROOT_PATH, 'modules', $all_pages[$route]['module'], $all_pages[$route]['file']]); + if ($active_page['controllerBased']) { + if (str_contains($route, 'queries')) { + /** @var \NamelessMC\Framework\Queries\Query */ + $controller = $container->make($active_page['file']); + $controller->handle(); + } elseif (str_contains($route, 'panel')) { + /** @var \NamelessMC\Framework\Pages\PanelPage */ + $controller = $container->make($active_page['file']); + + if (!$user->handlePanelPageLoad($controller->permission())) { + require_once(ROOT_PATH . '/403.php'); + die(); + } - if (file_exists($path)) { - require($path); - die; + require_once(ROOT_PATH . '/core/templates/backend_init.php'); + + $controller->render(); + Module::loadPage($user, $pages, $cache, $smarty, [$navigation, $cc_nav, $staffcp_nav], $widgets, $template); + define('PAGE', 'panel'); + define('PARENT_PAGE', $controller->parentPage() ?? $active_page['moduleSafeName']); + define('PANEL_PAGE', $controller->pageName()); + $smarty->assign(['TITLE' => $active_page['name'], 'PAGE' => $controller->pageName(), 'PARENT_PAGE' => $controller->parentPage() ?? $active_page['moduleSafeName']]); + $template->onPageLoad(); + require(ROOT_PATH . '/core/templates/panel_navbar.php'); + return $template->displayTemplate($controller->viewFile(), $smarty); + } else { + require_once(ROOT_PATH . '/core/templates/frontend_init.php'); + + $frontendAssets = $container->has('FrontendAssets') ? $container->get('FrontendAssets') : []; + $template->addJSFiles($frontendAssets['globalJsFiles'] ?? []); + $template->addCSSFiles($frontendAssets['globalCssFiles'] ?? []); + $template->addJSFiles($frontendAssets['jsFiles'][$active_page['safeName']] ?? []); + $template->addCSSFiles($frontendAssets['cssFiles'][$active_page['safeName']] ?? []); + + $frontendMiddlewares = $container->has('FrontendMiddleware') ? $container->get('FrontendMiddleware') : []; + foreach ($frontendMiddlewares as $moduleName => $frontendMiddleware) { + /** @var \NamelessMC\Framework\Pages\Middleware $middleware */ + $middleware = $container->make($frontendMiddleware); + $middleware->handle(); + } + + /** @var \NamelessMC\Framework\Pages\Page */ + $controller = $container->make($active_page['file'], [ + 'templatePagination' => $template_pagination, + ]); + $controller->render(); + Module::loadPage($user, $pages, $cache, $smarty, [$navigation, $cc_nav, $staffcp_nav], $widgets, $template); + define('PAGE', $controller->pageName()); + $smarty->assign('TITLE', $active_page['name']); + $template->onPageLoad(); + require(ROOT_PATH . '/core/templates/navbar.php'); + require(ROOT_PATH . '/core/templates/footer.php'); + return $template->displayTemplate($controller->viewFile(), $smarty); + } + } else { + $path = implode(DIRECTORY_SEPARATOR, [ROOT_PATH, 'modules', $active_page['module'], $active_page['file']]); + + if (file_exists($path)) { + require($path); + die; + } } } else { // Use recursion to check - might have URL parameters in path diff --git a/modules/Cookie Consent/assets/css/cookieconsent.min.css b/modules/Cookie Consent/assets/css/cookieconsent.min.css deleted file mode 100644 index d4d439025b..0000000000 --- a/modules/Cookie Consent/assets/css/cookieconsent.min.css +++ /dev/null @@ -1,6 +0,0 @@ -.cc-window{opacity:1;-webkit-transition:opacity 1s ease;transition:opacity 1s ease}.cc-window.cc-invisible{opacity:0}.cc-animate.cc-revoke{-webkit-transition:transform 1s ease;-webkit-transition:-webkit-transform 1s ease;transition:-webkit-transform 1s ease;transition:transform 1s ease;transition:transform 1s ease,-webkit-transform 1s ease}.cc-animate.cc-revoke.cc-top{-webkit-transform:translateY(-2em);transform:translateY(-2em)}.cc-animate.cc-revoke.cc-bottom{-webkit-transform:translateY(2em);transform:translateY(2em)}.cc-animate.cc-revoke.cc-active.cc-top{-webkit-transform:translateY(0);transform:translateY(0)}.cc-animate.cc-revoke.cc-active.cc-bottom{-webkit-transform:translateY(0);transform:translateY(0)}.cc-revoke:hover{-webkit-transform:translateY(0);transform:translateY(0)}.cc-grower{max-height:0;overflow:hidden;-webkit-transition:max-height 1s;transition:max-height 1s} -.cc-revoke,.cc-window{position:fixed;overflow:hidden;-webkit-box-sizing:border-box;box-sizing:border-box;font-family:Helvetica,Calibri,Arial,sans-serif;font-size:16px;line-height:1.5em;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:nowrap;flex-wrap:nowrap;z-index:9999}.cc-window.cc-static{position:static}.cc-window.cc-floating{padding:2em;max-width:24em;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.cc-window.cc-banner{padding:1em 1.8em;width:100%;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.cc-revoke{padding:.5em}.cc-revoke:hover{text-decoration:underline}.cc-header{font-size:18px;font-weight:700}.cc-btn,.cc-close,.cc-link,.cc-revoke{cursor:pointer}.cc-link{opacity:.8;display:inline-block;padding:.2em;text-decoration:underline}.cc-link:hover{opacity:1}.cc-link:active,.cc-link:visited{color:initial}.cc-btn{display:block;padding:.4em .8em;font-size:.9em;font-weight:700;border-width:2px;border-style:solid;text-align:center;white-space:nowrap}.cc-highlight .cc-btn:first-child{background-color:transparent;border-color:transparent}.cc-highlight .cc-btn:first-child:focus,.cc-highlight .cc-btn:first-child:hover{background-color:transparent;text-decoration:underline}.cc-close{display:block;position:absolute;top:.5em;right:.5em;font-size:1.6em;opacity:.9;line-height:.75}.cc-close:focus,.cc-close:hover{opacity:1} -.cc-revoke.cc-top{top:0;left:3em;border-bottom-left-radius:.5em;border-bottom-right-radius:.5em}.cc-revoke.cc-bottom{bottom:0;left:3em;border-top-left-radius:.5em;border-top-right-radius:.5em}.cc-revoke.cc-left{left:3em;right:unset}.cc-revoke.cc-right{right:3em;left:unset}.cc-top{top:1em}.cc-left{left:1em}.cc-right{right:1em}.cc-bottom{bottom:1em}.cc-floating>.cc-link{margin-bottom:1em}.cc-floating .cc-message{display:block;margin-bottom:1em}.cc-window.cc-floating .cc-compliance{-webkit-box-flex:1;-ms-flex:1 0 auto;flex:1 0 auto}.cc-window.cc-banner{-webkit-box-align:center;-ms-flex-align:center;align-items:center}.cc-banner.cc-top{left:0;right:0;top:0}.cc-banner.cc-bottom{left:0;right:0;bottom:0}.cc-banner .cc-message{display:block;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;max-width:100%;margin-right:1em}.cc-compliance{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-ms-flex-line-pack:justify;align-content:space-between}.cc-floating .cc-compliance>.cc-btn{-webkit-box-flex:1;-ms-flex:1;flex:1}.cc-btn+.cc-btn{margin-left:.5em} -@media print{.cc-revoke,.cc-window{display:none}}@media screen and (max-width:900px){.cc-btn{white-space:normal}}@media screen and (max-width:414px) and (orientation:portrait),screen and (max-width:736px) and (orientation:landscape){.cc-window.cc-top{top:0}.cc-window.cc-bottom{bottom:0}.cc-window.cc-banner,.cc-window.cc-floating,.cc-window.cc-left,.cc-window.cc-right{left:0;right:0}.cc-window.cc-banner{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.cc-window.cc-banner .cc-compliance{-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto}.cc-window.cc-floating{max-width:none}.cc-window .cc-message{margin-bottom:1em}.cc-window.cc-banner{-webkit-box-align:unset;-ms-flex-align:unset;align-items:unset}.cc-window.cc-banner .cc-message{margin-right:0}} -.cc-floating.cc-theme-classic{padding:1.2em;border-radius:5px}.cc-floating.cc-type-info.cc-theme-classic .cc-compliance{text-align:center;display:inline;-webkit-box-flex:0;-ms-flex:none;flex:none}.cc-theme-classic .cc-btn{border-radius:5px}.cc-theme-classic .cc-btn:last-child{min-width:140px}.cc-floating.cc-type-info.cc-theme-classic .cc-btn{display:inline-block} -.cc-theme-edgeless.cc-window{padding:0}.cc-floating.cc-theme-edgeless .cc-message{margin:2em;margin-bottom:1.5em}.cc-banner.cc-theme-edgeless .cc-btn{margin:0;padding:.8em 1.8em;height:100%}.cc-banner.cc-theme-edgeless .cc-message{margin-left:1em}.cc-floating.cc-theme-edgeless .cc-btn+.cc-btn{margin-left:0} \ No newline at end of file diff --git a/modules/Cookie Consent/assets/js/configure.js b/modules/Cookie Consent/assets/js/configure.js deleted file mode 100644 index dd6c49c5a8..0000000000 --- a/modules/Cookie Consent/assets/js/configure.js +++ /dev/null @@ -1,4 +0,0 @@ -function configureCookies() { - $.removeCookie('cookieconsent_status', { path: '/' }); - window.location.reload(); -} diff --git a/modules/Cookie Consent/assets/js/cookieconsent.min.js b/modules/Cookie Consent/assets/js/cookieconsent.min.js deleted file mode 100644 index 1e3dccf1a7..0000000000 --- a/modules/Cookie Consent/assets/js/cookieconsent.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(e){if(!e.hasInitialised){var t={escapeRegExp:function(e){return e.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")},hasClass:function(e,t){var i=" ";return 1===e.nodeType&&(i+e.className+i).replace(/[\n\t]/g,i).indexOf(i+t+i)>=0},addClass:function(e,t){e.className+=" "+t},removeClass:function(e,t){var i=new RegExp("\\b"+this.escapeRegExp(t)+"\\b");e.className=e.className.replace(i,"")},interpolateString:function(e,t){return e.replace(/{{([a-z][a-z0-9\-_]*)}}/gi,function(e){return t(arguments[1])||""})},getCookie:function(e){var t=("; "+document.cookie).split("; "+e+"=");return t.length<2?void 0:t.pop().split(";").shift()},setCookie:function(e,t,i,n,o,s){var r=new Date;r.setHours(r.getHours()+24*(i||365));var a=[e+"="+t,"expires="+r.toUTCString(),"path="+(o||"/")];n&&a.push("domain="+n),s&&a.push("secure"),document.cookie=a.join(";")},deepExtend:function(e,t){for(var i in t)t.hasOwnProperty(i)&&(i in e&&this.isPlainObject(e[i])&&this.isPlainObject(t[i])?this.deepExtend(e[i],t[i]):e[i]=t[i]);return e},throttle:function(e,t){var i=!1;return function(){i||(e.apply(this,arguments),i=!0,setTimeout(function(){i=!1},t))}},hash:function(e){var t,i,n=0;if(0===e.length)return n;for(t=0,i=e.length;t=128?"#000":"#fff"},getLuminance:function(e){var t=parseInt(this.normaliseHex(e),16),i=38+(t>>16),n=38+(t>>8&255),o=38+(255&t);return"#"+(16777216+65536*(i<255?i<1?0:i:255)+256*(n<255?n<1?0:n:255)+(o<255?o<1?0:o:255)).toString(16).slice(1)},isMobile:function(){return/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)},isPlainObject:function(e){return"object"==typeof e&&null!==e&&e.constructor==Object},traverseDOMPath:function(e,i){return e&&e.parentNode?t.hasClass(e,i)?e:this.traverseDOMPath(e.parentNode,i):null}};e.status={deny:"deny",allow:"allow",dismiss:"dismiss"},e.transitionEnd=function(){var e=document.createElement("div"),t={t:"transitionend",OT:"oTransitionEnd",msT:"MSTransitionEnd",MozT:"transitionend",WebkitT:"webkitTransitionEnd"};for(var i in t)if(t.hasOwnProperty(i)&&void 0!==e.style[i+"ransition"])return t[i];return""}(),e.hasTransition=!!e.transitionEnd;var i=Object.keys(e.status).map(t.escapeRegExp);e.customStyles={},e.Popup=function(){var n={enabled:!0,container:null,cookie:{name:"cookieconsent_status",path:"/",domain:"",expiryDays:365,secure:!1},onPopupOpen:function(){},onPopupClose:function(){},onInitialise:function(e){},onStatusChange:function(e,t){},onRevokeChoice:function(){},onNoCookieLaw:function(e,t){},content:{header:"Cookies used on the website!",message:"This website uses cookies to ensure you get the best experience on our website.",dismiss:"Got it!",allow:"Allow cookies",deny:"Decline",link:"Learn more",href:"https://www.cookiesandyou.com",close:"❌",target:"_blank",policy:"Cookie Policy"},elements:{header:'{{header}} ',message:'{{message}}',messagelink:'{{message}} {{link}}',dismiss:'{{dismiss}}',allow:'{{allow}}',deny:'{{deny}}',link:'{{link}}',close:'{{close}}'},window:'',revokeBtn:'
{{policy}}
',compliance:{info:'
{{dismiss}}
',"opt-in":'
{{deny}}{{allow}}
',"opt-out":'
{{deny}}{{allow}}
'},type:"info",layouts:{basic:"{{messagelink}}{{compliance}}","basic-close":"{{messagelink}}{{compliance}}{{close}}","basic-header":"{{header}}{{message}}{{link}}{{compliance}}"},layout:"basic",position:"bottom",theme:"block",static:!1,palette:null,revokable:!1,animateRevokable:!0,showLink:!0,dismissOnScroll:!1,dismissOnTimeout:!1,dismissOnWindowClick:!1,ignoreClicksFrom:["cc-revoke","cc-btn"],autoOpen:!0,autoAttach:!0,whitelistPage:[],blacklistPage:[],overrideHTML:null};function o(){this.initialise.apply(this,arguments)}function s(e){this.openingTimeout=null,t.removeClass(e,"cc-invisible")}function r(t){t.style.display="none",t.removeEventListener(e.transitionEnd,this.afterTransition),this.afterTransition=null}function a(){var e=this.options.position.split("-"),t=[];return e.forEach(function(e){t.push("cc-"+e)}),t}function c(n){var o=this.options,s=document.createElement("div"),r=o.container&&1===o.container.nodeType?o.container:document.body;s.innerHTML=n;var a=s.children[0];return a.style.display="none",t.hasClass(a,"cc-window")&&e.hasTransition&&t.addClass(a,"cc-invisible"),this.onButtonClick=function(n){var o=t.traverseDOMPath(n.target,"cc-btn")||n.target;if(t.hasClass(o,"cc-btn")){var s=o.className.match(new RegExp("\\bcc-("+i.join("|")+")\\b")),r=s&&s[1]||!1;r&&(this.setStatus(r),this.close(!0))}t.hasClass(o,"cc-close")&&(this.setStatus(e.status.dismiss),this.close(!0));t.hasClass(o,"cc-revoke")&&this.revokeChoice()}.bind(this),a.addEventListener("click",this.onButtonClick),o.autoAttach&&(r.firstChild?r.insertBefore(a,r.firstChild):r.appendChild(a)),a}function l(e){return"000000"==(e=t.normaliseHex(e))?"#222":t.getLuminance(e)}function u(e,t){for(var i=0,n=e.length;i=0;o&&t(n);return o}.call(this)&&(this.options.enabled=!1),u(this.options.blacklistPage,location.pathname)&&(this.options.enabled=!1),u(this.options.whitelistPage,location.pathname)&&(this.options.enabled=!0);var o=this.options.window.replace("{{classes}}",function(){var i=this.options,n="top"==i.position||"bottom"==i.position?"banner":"floating";t.isMobile()&&(n="floating");var o=["cc-"+n,"cc-type-"+i.type,"cc-theme-"+i.theme];i.static&&o.push("cc-static");o.push.apply(o,a.call(this));(function(i){var n=t.hash(JSON.stringify(i)),o="cc-color-override-"+n,s=t.isPlainObject(i);this.customStyleSelector=s?o:null,s&&function(i,n,o){if(e.customStyles[i])return void++e.customStyles[i].references;var s={},r=n.popup,a=n.button,c=n.highlight;r&&(r.text=r.text?r.text:t.getContrast(r.background),r.link=r.link?r.link:r.text,s[o+".cc-window"]=["color: "+r.text,"background-color: "+r.background],s[o+".cc-revoke"]=["color: "+r.text,"background-color: "+r.background],s[o+" .cc-link,"+o+" .cc-link:active,"+o+" .cc-link:visited"]=["color: "+r.link],a&&(a.text=a.text?a.text:t.getContrast(a.background),a.border=a.border?a.border:"transparent",s[o+" .cc-btn"]=["color: "+a.text,"border-color: "+a.border,"background-color: "+a.background],a.padding&&s[o+" .cc-btn"].push("padding: "+a.padding),"transparent"!=a.background&&(s[o+" .cc-btn:hover, "+o+" .cc-btn:focus"]=["background-color: "+(a.hover||l(a.background))]),c?(c.text=c.text?c.text:t.getContrast(c.background),c.border=c.border?c.border:"transparent",s[o+" .cc-highlight .cc-btn:first-child"]=["color: "+c.text,"border-color: "+c.border,"background-color: "+c.background]):s[o+" .cc-highlight .cc-btn:first-child"]=["color: "+r.text]));var u=document.createElement("style");document.head.appendChild(u),e.customStyles[i]={references:1,element:u.sheet};var h=-1;for(var p in s)s.hasOwnProperty(p)&&u.sheet.insertRule(p+"{"+s[p].join(";")+"}",++h)}(n,i,"."+o);return s}).call(this,this.options.palette);this.customStyleSelector&&o.push(this.customStyleSelector);return o}.call(this).join(" ")).replace("{{children}}",function(){var e={},i=this.options;i.showLink||(i.elements.link="",i.elements.messagelink=i.elements.message);Object.keys(i.elements).forEach(function(n){e[n]=t.interpolateString(i.elements[n],function(e){var t=i.content[e];return e&&"string"==typeof t&&t.length?t:""})});var n=i.compliance[i.type];n||(n=i.compliance.info);e.compliance=t.interpolateString(n,function(t){return e[t]});var o=i.layouts[i.layout];o||(o=i.layouts.basic);return t.interpolateString(o,function(t){return e[t]})}.call(this)),s=this.options.overrideHTML;if("string"==typeof s&&s.length&&(o=s),this.options.static){var r=c.call(this,'
'+o+"
");r.style.display="",this.element=r.firstChild,this.element.style.display="none",t.addClass(this.element,"cc-invisible")}else this.element=c.call(this,o);(function(){var i=this.setStatus.bind(this),n=this.close.bind(this),o=this.options.dismissOnTimeout;"number"==typeof o&&o>=0&&(this.dismissTimeout=window.setTimeout(function(){i(e.status.dismiss),n(!0)},Math.floor(o)));var s=this.options.dismissOnScroll;if("number"==typeof s&&s>=0){var r=function(t){window.pageYOffset>Math.floor(s)&&(i(e.status.dismiss),n(!0),window.removeEventListener("scroll",r),this.onWindowScroll=null)};this.options.enabled&&(this.onWindowScroll=r,window.addEventListener("scroll",r))}var a=this.options.dismissOnWindowClick,c=this.options.ignoreClicksFrom;if(a){var l=function(o){for(var s=!1,r=o.path.length,a=c.length,u=0;uo&&(i=!0),i?t.hasClass(n,"cc-active")||t.addClass(n,"cc-active"):t.hasClass(n,"cc-active")&&t.removeClass(n,"cc-active")},200);this.onMouseMove=o,window.addEventListener("mousemove",o)}}}.call(this),this.options.autoOpen&&this.autoOpen()},o.prototype.destroy=function(){this.onButtonClick&&this.element&&(this.element.removeEventListener("click",this.onButtonClick),this.onButtonClick=null),this.dismissTimeout&&(clearTimeout(this.dismissTimeout),this.dismissTimeout=null),this.onWindowScroll&&(window.removeEventListener("scroll",this.onWindowScroll),this.onWindowScroll=null),this.onWindowClick&&(window.removeEventListener("click",this.onWindowClick),this.onWindowClick=null),this.onMouseMove&&(window.removeEventListener("mousemove",this.onMouseMove),this.onMouseMove=null),this.element&&this.element.parentNode&&this.element.parentNode.removeChild(this.element),this.element=null,this.revokeBtn&&this.revokeBtn.parentNode&&this.revokeBtn.parentNode.removeChild(this.revokeBtn),this.revokeBtn=null,function(i){if(t.isPlainObject(i)){var n=t.hash(JSON.stringify(i)),o=e.customStyles[n];if(o&&!--o.references){var s=o.element.ownerNode;s&&s.parentNode&&s.parentNode.removeChild(s),e.customStyles[n]=null}}}(this.options.palette),this.options=null},o.prototype.open=function(t){if(this.element)return this.isOpen()||(e.hasTransition?this.fadeIn():this.element.style.display="",this.options.revokable&&this.toggleRevokeButton(),this.options.onPopupOpen.call(this)),this},o.prototype.close=function(t){if(this.element)return this.isOpen()&&(e.hasTransition?this.fadeOut():this.element.style.display="none",t&&this.options.revokable&&this.toggleRevokeButton(!0),this.options.onPopupClose.call(this)),this},o.prototype.fadeIn=function(){var i=this.element;if(e.hasTransition&&i&&(this.afterTransition&&r.call(this,i),t.hasClass(i,"cc-invisible"))){if(i.style.display="",this.options.static){var n=this.element.clientHeight;this.element.parentNode.style.maxHeight=n+"px"}this.openingTimeout=setTimeout(s.bind(this,i),20)}},o.prototype.fadeOut=function(){var i=this.element;e.hasTransition&&i&&(this.openingTimeout&&(clearTimeout(this.openingTimeout),s.bind(this,i)),t.hasClass(i,"cc-invisible")||(this.options.static&&(this.element.parentNode.style.maxHeight=""),this.afterTransition=r.bind(this,i),i.addEventListener(e.transitionEnd,this.afterTransition),t.addClass(i,"cc-invisible")))},o.prototype.isOpen=function(){return this.element&&""==this.element.style.display&&(!e.hasTransition||!t.hasClass(this.element,"cc-invisible"))},o.prototype.toggleRevokeButton=function(e){this.revokeBtn&&(this.revokeBtn.style.display=e?"":"none")},o.prototype.revokeChoice=function(e){this.options.enabled=!0,this.clearStatus(),this.options.onRevokeChoice.call(this),e||this.autoOpen()},o.prototype.hasAnswered=function(t){return Object.keys(e.status).indexOf(this.getStatus())>=0},o.prototype.hasConsented=function(t){var i=this.getStatus();return i==e.status.allow||i==e.status.dismiss},o.prototype.autoOpen=function(e){!this.hasAnswered()&&this.options.enabled?this.open():this.hasAnswered()&&this.options.revokable&&this.toggleRevokeButton(!0)},o.prototype.setStatus=function(i){var n=this.options.cookie,o=t.getCookie(n.name),s=Object.keys(e.status).indexOf(o)>=0;Object.keys(e.status).indexOf(i)>=0?(t.setCookie(n.name,i,n.expiryDays,n.domain,n.path,n.secure),this.options.onStatusChange.call(this,i,s)):this.clearStatus()},o.prototype.getStatus=function(){return t.getCookie(this.options.cookie.name)},o.prototype.clearStatus=function(){var e=this.options.cookie;t.setCookie(e.name,"",-1,e.domain,e.path)},o}(),e.Location=function(){var e={timeout:5e3,services:["ipinfo"],serviceDefinitions:{ipinfo:function(){return{url:"//ipinfo.io",headers:["Accept: application/json"],callback:function(e,t){try{var i=JSON.parse(t);return i.error?s(i):{code:i.country}}catch(e){return s({error:"Invalid response ("+e+")"})}}}},ipinfodb:function(e){return{url:"//api.ipinfodb.com/v3/ip-country/?key={api_key}&format=json&callback={callback}",isScript:!0,callback:function(e,t){try{var i=JSON.parse(t);return"ERROR"==i.statusCode?s({error:i.statusMessage}):{code:i.countryCode}}catch(e){return s({error:"Invalid response ("+e+")"})}}}},maxmind:function(){return{url:"//js.maxmind.com/js/apis/geoip2/v2.1/geoip2.js",isScript:!0,callback:function(e){window.geoip2?geoip2.country(function(t){try{e({code:t.country.iso_code})}catch(t){e(s(t))}},function(t){e(s(t))}):e(new Error("Unexpected response format. The downloaded script should have exported `geoip2` to the global scope"))}}}}};function i(i){t.deepExtend(this.options={},e),t.isPlainObject(i)&&t.deepExtend(this.options,i),this.currentServiceIndex=-1}function n(e,t,i){var n,o=document.createElement("script");o.type="text/"+(e.type||"javascript"),o.src=e.src||e,o.async=!1,o.onreadystatechange=o.onload=function(){var e=o.readyState;clearTimeout(n),t.done||e&&!/loaded|complete/.test(e)||(t.done=!0,t(),o.onreadystatechange=o.onload=null)},document.body.appendChild(o),n=setTimeout(function(){t.done=!0,t(),o.onreadystatechange=o.onload=null},i)}function o(e,t,i,n,o){var s=new(window.XMLHttpRequest||window.ActiveXObject)("MSXML2.XMLHTTP.3.0");if(s.open(n?"POST":"GET",e,1),s.setRequestHeader("Content-type","application/x-www-form-urlencoded"),Array.isArray(o))for(var r=0,a=o.length;r3&&t(s)}),s.send(n)}function s(e){return new Error("Error ["+(e.code||"UNKNOWN")+"]: "+e.error)}return i.prototype.getNextService=function(){var e;do{e=this.getServiceByIdx(++this.currentServiceIndex)}while(this.currentServiceIndex=0,revokable:t.revokable.indexOf(e)>=0,explicitAction:t.explicitAction.indexOf(e)>=0}},i.prototype.applyLaw=function(e,t){var i=this.get(t);return i.hasLaw||(e.enabled=!1,"function"==typeof e.onNoCookieLaw&&e.onNoCookieLaw(t,i)),this.options.regionalLaw&&(i.revokable&&(e.revokable=!0),i.explicitAction&&(e.dismissOnScroll=!1,e.dismissOnTimeout=!1)),e},i}(),e.initialise=function(i,n,o){var s=new e.Law(i.law);n||(n=function(){}),o||(o=function(){});var r=Object.keys(e.status),a=t.getCookie("cookieconsent_status");r.indexOf(a)>=0?n(new e.Popup(i)):e.getCountryCode(i,function(t){delete i.law,delete i.location,t.code&&(i=s.applyLaw(i,t.code)),n(new e.Popup(i))},function(t){delete i.law,delete i.location,o(t,new e.Popup(i))})},e.getCountryCode=function(t,i,n){t.law&&t.law.countryCode?i({code:t.law.countryCode}):t.location?new e.Location(t.location).locate(function(e){i(e||{})},n):i({})},e.utils=t,e.hasInitialised=!0,window.cookieconsent=e}}(window.cookieconsent||{}); \ No newline at end of file diff --git a/modules/Cookie Consent/assets/js/template.js b/modules/Cookie Consent/assets/js/template.js deleted file mode 100644 index 92f35db417..0000000000 --- a/modules/Cookie Consent/assets/js/template.js +++ /dev/null @@ -1,10 +0,0 @@ -if (window.cookieconsent === undefined) { - console.warn('Failed to initialise cookie consent, it may be blocked by your browser or a browser extension'); -} else { - window.cookieconsent.initialise({ - onStatusChange: function() { - window.location.reload(); - }, - //"{x}" - }); -} diff --git a/modules/Cookie Consent/classes/CookieConsent.php b/modules/Cookie Consent/classes/CookieConsent.php deleted file mode 100644 index 7c804a5f34..0000000000 --- a/modules/Cookie Consent/classes/CookieConsent.php +++ /dev/null @@ -1,89 +0,0 @@ - ['background' => 'transparent', 'text' => $button_text_colour, 'border' => $border_colour], - 'popup' => ['background' => $background_colour, 'text' => $text_colour], - ]; - - $script_options['content'] = [ - 'policy' => $options['cookies'], - 'message' => $options['message'], - 'deny' => $options['dismiss'], - 'allow' => $options['allow'], - 'link' => $options['link'], - 'href' => $options['href'], - ]; - - $json = json_encode($script_options, JSON_PRETTY_PRINT); - - return str_replace( - '//"{x}"', - substr($json, 1, -1), - file_get_contents(ROOT_PATH . '/modules/Cookie Consent/assets/js/template.js') - ); - } -} diff --git a/modules/Cookie Consent/classes/CookieConsent_Sitemap.php b/modules/Cookie Consent/classes/CookieConsent_Sitemap.php deleted file mode 100644 index 3f786c86d5..0000000000 --- a/modules/Cookie Consent/classes/CookieConsent_Sitemap.php +++ /dev/null @@ -1,23 +0,0 @@ -addItem(URL::build('/cookies'), 0.9); - } -} diff --git a/modules/Cookie Consent/init.php b/modules/Cookie Consent/init.php deleted file mode 100644 index 342024e4e1..0000000000 --- a/modules/Cookie Consent/init.php +++ /dev/null @@ -1,16 +0,0 @@ -_language = $language; - $this->_cookie_language = $cookie_language; - - $name = 'Cookie Consent'; - $author = 'Samerton'; - $module_version = '2.1.2'; - $nameless_version = '2.1.2'; - - parent::__construct($this, $name, $author, $module_version, $nameless_version); - - // Define URLs which belong to this module - $pages->add('Cookie Consent', '/cookies', 'pages/cookies.php'); - - // Panel - $pages->add('Cookie Consent', '/panel/cookies', 'pages/panel/cookies.php'); - - // Cookies - define('COOKIE_CHECK', true); - define('COOKIES_ALLOWED', Cookie::exists('cookieconsent_status') && Cookie::get('cookieconsent_status') == 'allow'); - } - - public function onInstall() { - // Not necessary for CookieConsent - } - - public function onUninstall() { - // Not necessary for CookieConsent - } - - public function onEnable() { - // Not necessary for CookieConsent - } - - public function onDisable() { - // Not necessary for CookieConsent - } - - public function onPageLoad(User $user, Pages $pages, Cache $cache, Smarty $smarty, $navs, Widgets $widgets, ?TemplateBase $template) { - $language = $this->_language; - - // AdminCP - PermissionHandler::registerPermissions($language->get('moderator', 'staff_cp'), [ - 'admincp.cookies' => $this->_cookie_language->get('cookie', 'cookies') - ]); - - // Sitemap - $pages->registerSitemapMethod([CookieConsent_Sitemap::class, 'generateSitemap']); - - if (defined('FRONT_END')) { - // Add cookie page link - $cache->setCache('cookie_consent_module_cache'); - if (!$cache->isCached('options')) { - $options = ['type' => 'opt-in', 'position' => 'bottom-right']; - $cache->store('options', $options); - } else { - $options = $cache->retrieve('options'); - } - - $cookie_url = URL::build('/cookies'); - - // Add JS script - if ($template) { - $template->addCSSFiles([ - (defined('CONFIG_PATH') ? CONFIG_PATH : '') . '/modules/Cookie Consent/assets/css/cookieconsent.min.css' => [], - ]); - $template->addJSFiles([ - (defined('CONFIG_PATH') ? CONFIG_PATH : '') . '/modules/Cookie Consent/assets/js/cookieconsent.min.js' => [], - ]); - $template->addJSScript( - CookieConsent::generateScript( - array_merge($options, [ - 'cookies' => $this->_cookie_language->get('cookie', 'cookies'), - 'message' => $this->_cookie_language->get('cookie', 'cookie_popup'), - 'dismiss' => $this->_cookie_language->get('cookie', 'cookie_popup_disallow'), - 'allow' => $this->_cookie_language->get('cookie', 'cookie_popup_allow'), - 'link' => $this->_cookie_language->get('cookie', 'cookie_popup_more_info'), - 'href' => $cookie_url, - ]) - ) - ); - } - - $smarty->assign([ - 'COOKIE_URL' => $cookie_url, - 'COOKIE_NOTICE_HEADER' => $this->_cookie_language->get('cookie', 'cookie_notice'), - 'COOKIE_NOTICE_BODY' => $this->_cookie_language->get('cookie', 'cookie_notice_info'), - 'COOKIE_NOTICE_CONFIGURE' => $this->_cookie_language->get('cookie', 'configure_cookies'), - 'COOKIE_DECISION_MADE' => (bool)Cookie::get('cookieconsent_status'), - ]); - - $navs[0]->add('cookies', $this->_cookie_language->get('cookie', 'cookie_notice'), $cookie_url, 'footer'); - } - - if (defined('BACK_END')) { - $cache->setCache('panel_sidebar'); - - // StaffCP link - if ($user->hasPermission('admincp.cookies')) { - if (!$cache->isCached('cookie_order')) { - $order = 10; - $cache->store('cookie_order', 10); - } else { - $order = $cache->retrieve('cookie_order'); - } - - if (!$cache->isCached('cookie_icon')) { - $icon = ''; - $cache->store('cookie_icon', $icon); - } else { - $icon = $cache->retrieve('cookie_icon'); - } - - $navs[2]->add('cookie_divider', mb_strtoupper($this->_cookie_language->get('cookie', 'cookies'), 'UTF-8'), 'divider', 'top', null, $order, ''); - $navs[2]->add('cookie_settings', $this->_cookie_language->get('cookie', 'cookies'), URL::build('/panel/cookies'), 'top', null, $order + 0.1, $icon); - } - } - } - - public function getDebugInfo(): array { - return []; - } -} diff --git a/modules/Cookie Consent/pages/cookies.php b/modules/Cookie Consent/pages/cookies.php deleted file mode 100644 index a32e180171..0000000000 --- a/modules/Cookie Consent/pages/cookies.php +++ /dev/null @@ -1,37 +0,0 @@ -get('cookie', 'cookie_notice'); -require_once(ROOT_PATH . '/core/templates/frontend_init.php'); - -// Retrieve cookie notice from database -$cookie_notice = DB::getInstance()->query('SELECT value FROM nl2_privacy_terms WHERE `name` = ?', ['cookies'])->first()->value; - -// Load modules + template -Module::loadPage($user, $pages, $cache, $smarty, [$navigation, $cc_nav, $staffcp_nav], $widgets, $template); - -$smarty->assign([ - 'COOKIE_NOTICE_HEADER' => $cookie_language->get('cookie', 'cookie_notice'), - 'COOKIE_NOTICE' => Output::getPurified($cookie_notice), - 'UPDATE_SETTINGS' => $cookie_language->get('cookie', 'update_settings'), -]); - -$template->addJSScript(file_get_contents(ROOT_PATH . '/modules/Cookie Consent/assets/js/configure.js')); - -$template->onPageLoad(); - -require(ROOT_PATH . '/core/templates/navbar.php'); -require(ROOT_PATH . '/core/templates/footer.php'); - -// Display template -$template->displayTemplate('cookies.tpl', $smarty); diff --git a/modules/Cookie Consent/pages/panel/cookies.php b/modules/Cookie Consent/pages/panel/cookies.php deleted file mode 100644 index c7274f4155..0000000000 --- a/modules/Cookie Consent/pages/panel/cookies.php +++ /dev/null @@ -1,100 +0,0 @@ -handlePanelPageLoad('admincp.cookies')) { - require_once(ROOT_PATH . '/403.php'); - die(); -} - -const PAGE = 'panel'; -const PARENT_PAGE = 'cookie_divider'; -const PANEL_PAGE = 'cookie_settings'; -$page_title = $cookie_language->get('cookie', 'cookies'); -require_once(ROOT_PATH . '/core/templates/backend_init.php'); - -if (Input::exists()) { - $errors = []; - - if (Token::check()) { - $validation = Validate::check($_POST, [ - 'cookies' => [ - Validate::REQUIRED => true, - Validate::MAX => 100000 - ], - ])->messages([ - 'cookies' => $cookie_language->get('cookie', 'cookie_notice_error'), - ]); - - if ($validation->passed()) { - try { - $cookie_id = DB::getInstance()->get('privacy_terms', ['name', 'cookies'])->results(); - if (count($cookie_id)) { - $cookie_id = $cookie_id[0]->id; - - DB::getInstance()->update('privacy_terms', $cookie_id, [ - 'value' => Input::get('cookies') - ]); - } else { - DB::getInstance()->insert('privacy_terms', [ - 'name' => 'cookies', - 'value' => Input::get('cookies') - ]); - } - - $success = $cookie_language->get('cookie', 'cookie_notice_success'); - } catch (Exception $e) { - $errors[] = $e->getMessage(); - } - } else { - $errors = $validation->errors(); - } - } else { - $errors[] = $language->get('general', 'invalid_token'); - } -} - -// Load modules + template -Module::loadPage($user, $pages, $cache, $smarty, [$navigation, $cc_nav, $staffcp_nav], $widgets, $template); - -if (isset($success)) { - $smarty->assign([ - 'SUCCESS' => $success, - 'SUCCESS_TITLE' => $language->get('general', 'success') - ]); -} - -if (isset($errors) && count($errors)) { - $smarty->assign([ - 'ERRORS' => $errors, - 'ERRORS_TITLE' => $language->get('general', 'error') - ]); -} - -// Get cookie notice -$cookies = DB::getInstance()->query('SELECT value FROM nl2_privacy_terms WHERE `name` = ?', ['cookies'])->first()->value; - -$smarty->assign([ - 'PARENT_PAGE' => PARENT_PAGE, - 'DASHBOARD' => $language->get('admin', 'dashboard'), - 'COOKIES' => $cookie_language->get('cookie', 'cookies'), - 'PAGE' => PANEL_PAGE, - 'TOKEN' => Token::get(), - 'SUBMIT' => $language->get('general', 'submit'), - 'COOKIE_NOTICE' => $cookie_language->get('cookie', 'cookie_notice'), - 'COOKIE_NOTICE_VALUE' => Output::getPurified($cookies), -]); - -$template->onPageLoad(); - -require(ROOT_PATH . '/core/templates/panel_navbar.php'); - -// Display template -$template->displayTemplate('cookies/cookies.tpl', $smarty); diff --git a/modules/Core/classes/Misc/RegisteredMembersListProvider.php b/modules/Core/classes/Misc/RegisteredMembersListProvider.php index 8f194caa1a..7e068195a7 100644 --- a/modules/Core/classes/Misc/RegisteredMembersListProvider.php +++ b/modules/Core/classes/Misc/RegisteredMembersListProvider.php @@ -1,4 +1,5 @@ getSitemapMethods(); foreach ($methods as $method) { - if (!class_exists($method[0])) { + if (!is_callable($method)) { $errors[] = $language->get( 'admin', 'unable_to_load_sitemap_file_x', diff --git a/modules/Core/init.php b/modules/Core/init.php index 107afe605b..9cec3e2494 100644 --- a/modules/Core/init.php +++ b/modules/Core/init.php @@ -10,6 +10,7 @@ */ // Ensure module has been installed +$cache->setCache('modulescache'); $module_installed = $cache->retrieve('module_core'); if (!$module_installed) { // Hasn't been installed diff --git a/modules/Core/module.php b/modules/Core/module.php index d31f04aa0d..16cf2b0b88 100644 --- a/modules/Core/module.php +++ b/modules/Core/module.php @@ -1,4 +1,5 @@ $viewing_language->get('emails', $email . '_message')); Email::addPlaceholder('[Thanks]', static fn(Language $viewing_language) => $viewing_language->get('emails', 'thanks')); - if (Util::isModuleEnabled('Members')) { - MemberListManager::getInstance()->registerListProvider(new RegisteredMembersListProvider($language)); - MemberListManager::getInstance()->registerListProvider(new StaffMembersListProvider($language)); + // if (Util::isModuleEnabled('Members')) { + $memberListManager = Container::getInstance()->get('NamelessMC\Members\MemberListManager'); + $memberListManager->registerListProvider(new RegisteredMembersListProvider($language)); + $memberListManager->registerListProvider(new StaffMembersListProvider($language)); - MemberListManager::getInstance()->registerMemberMetadataProvider(function (User $member) use ($language) { + $memberListManager->registerMemberMetadataProvider(function (User $member) use ($language) { return [ $language->get('general', 'joined') => date(DATE_FORMAT, $member->data()->joined), ]; }); - } + // } ReactionContextsManager::getInstance()->provideContext(new ProfilePostReactionContext()); diff --git a/modules/Core/pages/panel/integrations.php b/modules/Core/pages/panel/integrations.php index b751a64624..830cf424a3 100644 --- a/modules/Core/pages/panel/integrations.php +++ b/modules/Core/pages/panel/integrations.php @@ -70,8 +70,9 @@ Redirect::to(URL::build('/panel/core/integrations')); } + $errors = []; + if (Input::exists()) { - $errors = []; if (Token::check()) { if (Input::get('action') === 'general_settings') { @@ -106,7 +107,13 @@ } } - if ($integration->getSettings() !== null) { + if ($integration instanceof HasIntegrationSettings) { + $integration->handleSettingsRequest( + $smarty, + $language, + $errors, + ); + } else if ($integration->getSettings() !== null) { if (file_exists($integration->getSettings())) { require_once($integration->getSettings()); } else { diff --git a/modules/Core/pages/panel/modules.php b/modules/Core/pages/panel/modules.php index a22ec654e2..bbd06f31b3 100644 --- a/modules/Core/pages/panel/modules.php +++ b/modules/Core/pages/panel/modules.php @@ -70,6 +70,7 @@ 'nameless_version' => Output::getClean($module->getNamelessVersion()), 'author' => Output::getPurified($module->getAuthor()), 'author_x' => $language->get('admin', 'author_x', ['author' => Output::getPurified($module->getAuthor())]), + 'repository_url' => $module instanceof ComposerModuleWrapper ? Output::getClean($module->getRepositoryUrl()) : null, 'version_mismatch' => !Util::isCompatible($module->getNamelessVersion(), NAMELESS_VERSION) ? $language->get('admin', 'module_outdated', [ 'intendedVersion' => Text::bold(Output::getClean($module->getNamelessVersion())), 'actualVersion' => Text::bold(NAMELESS_VERSION) @@ -161,14 +162,44 @@ // Get module name $name = DB::getInstance()->get('modules', ['id', $_GET['m']])->results(); if (!count($name)) { - Redirect::to(URL::build('/panel/modules')); + Redirect::to(URL::build('/panel/core/modules')); } $name = Output::getClean($name[0]->name); + // Check if composer module + foreach (ComposerModuleDiscovery::discoverModules() as $composerModule) { + if ($composerModule->getName() === $name) { // compare by ID? + $modules = []; + + $order = Module::determineModuleOrder(); + + foreach ($order['modules'] as $key => $item) { + $modules[] = [ + 'name' => $item, + 'priority' => $key + ]; + } + + // We need to boot it so that the Lifecycle Extenders are loaded + ComposerModuleDiscovery::bootModule($container, $composerModule); + $composerModule->onEnable(); + + DB::getInstance()->update('modules', $_GET['m'], [ + 'enabled' => true, + ]); + + $cache->setCache('modulescache'); + $cache->store('enabled_modules', $modules); + + Session::flash('admin_modules', $language->get('admin', 'module_enabled')); + Redirect::to(URL::build('/panel/core/modules')); + } + } + // Ensure module is valid if (!file_exists(ROOT_PATH . '/modules/' . $name . '/init.php')) { - Redirect::to(URL::build('/panel/modules')); + Redirect::to(URL::build('/panel/core/modules')); } $module = null; @@ -271,6 +302,13 @@ if (file_exists(ROOT_PATH . '/modules/' . $name . '/init.php')) { require_once(ROOT_PATH . '/modules/' . $name . '/init.php'); $module->onDisable(); + } else { + // Check if composer module + foreach (ComposerModuleDiscovery::discoverModules() as $composerModule) { + if ($composerModule->getName() === $name) { // compare by ID? + $composerModule->onDisable(); + } + } } Session::flash('admin_modules', $language->get('admin', 'module_disabled')); @@ -284,7 +322,7 @@ if ($_GET['action'] == 'install') { if (Token::check()) { - // Install any new modules + // Install any new classic modules $directories = glob(ROOT_PATH . '/modules/*', GLOB_ONLYDIR); define('MODULE_INSTALL', true); @@ -325,6 +363,18 @@ } } + // Install any new composer modules + foreach (ComposerModuleDiscovery::discoverModules() as $composerModule) { + $exists = DB::getInstance()->get('modules', ['name', $composerModule->getName()])->results(); + + if (!count($exists)) { + DB::getInstance()->insert('modules', [ + 'name' => $composerModule->getName(), + ]); + $composerModule->onInstall(); + } + } + if (count($errors)) { Session::put('admin_modules_errors', $errors); } else { @@ -381,10 +431,18 @@ /** @var Module $module */ require_once(ROOT_PATH . '/modules/' . $name . '/init.php'); $module->onUninstall(); - } - if (!Util::recursiveRemoveDirectory(ROOT_PATH . '/modules/' . $name)) { - Session::flash('admin_modules_error', $language->get('admin', 'unable_to_delete_module_files')); + if (!Util::recursiveRemoveDirectory(ROOT_PATH . '/modules/' . $name)) { + Session::flash('admin_modules_error', $language->get('admin', 'unable_to_delete_module_files')); + } + } else { + // Check if composer module + // TODO, should we try to run the `composer remove` command? + foreach (ComposerModuleDiscovery::discoverModules() as $composerModule) { + if ($composerModule->getName() === $name) { // compare by ID? + $composerModule->onUninstall(); + } + } } Session::flash('admin_modules', $language->get('admin', 'module_uninstalled')); diff --git a/modules/Core/pages/panel/seo.php b/modules/Core/pages/panel/seo.php index dd7ddd0b2d..9600edc072 100644 --- a/modules/Core/pages/panel/seo.php +++ b/modules/Core/pages/panel/seo.php @@ -46,7 +46,7 @@ $methods = $pages->getSitemapMethods(); foreach ($methods as $method) { - if (!class_exists($method[0])) { + if (!is_callable($method)) { $errors[] = $language->get('admin', 'unable_to_load_sitemap_file_x', ['file' => Output::getClean($method[0])]); continue; } @@ -239,6 +239,7 @@ 'GENERATE' => $language->get('admin', 'generate_sitemap'), 'GOOGLE_ANALYTICS_VALUE' => Settings::get('ga_script'), 'PAGE_TITLE' => $language->get('admin', 'page'), + 'PAGE_MODULE' => $language->get('admin', 'module'), 'PAGE_LIST' => $pages->returnPages(), 'EDIT_LINK' => URL::build('/panel/core/seo/', 'metadata={x}'), 'GOOGLE_ANALYTICS' => $language->get('admin', 'google_analytics'), diff --git a/modules/Core/pages/panel/widgets.php b/modules/Core/pages/panel/widgets.php index fe61daed7a..c499c6d8f2 100644 --- a/modules/Core/pages/panel/widgets.php +++ b/modules/Core/pages/panel/widgets.php @@ -183,7 +183,7 @@ } } - if ($widget_instance->getSettings() !== null) { + if ($widget_instance instanceof HasWidgetSettings || $widget_instance->getSettings() !== null) { $smarty->assign([ 'SETTINGS' => $language->get('admin', 'settings'), 'SETTINGS_LINK' => URL::build('/panel/core/widgets/', 'action=settings&w=' . urlencode($widget->id)), @@ -231,12 +231,16 @@ $widget = $widget[0]; $widget_instance = $widgets->getWidget($widget->name); - if ($widget_instance->getSettings() === null || !file_exists($widget_instance->getSettings())) { - Redirect::to(URL::build('/admin/widgets')); + if ($widget_instance instanceof HasWidgetSettings) { + $widget_instance->handleSettingsRequest($cache, $smarty, $language, $success, $errors); + } else if ($widget_instance->getSettings() !== null) { + if (file_exists($widget_instance->getSettings())) { + require_once($widget_instance->getSettings()); + } else { + Redirect::to(URL::build('/admin/widgets')); + } } - require_once($widget_instance->getSettings()); - $smarty->assign([ 'EDITING_WIDGET' => $language->get('admin', 'editing_widget_x', [ 'widget' => Text::bold(Output::getClean($widget->name)), diff --git a/modules/Core/queries/debug_link.php b/modules/Core/queries/debug_link.php index 0c3411259e..d92149999a 100755 --- a/modules/Core/queries/debug_link.php +++ b/modules/Core/queries/debug_link.php @@ -39,6 +39,7 @@ 'module_version' => $module->getVersion(), 'namelessmc_version' => $module->getNamelessVersion(), 'debug_info' => $module->getDebugInfo(), + 'type' => $module instanceof ComposerModuleWrapper ? 'composer' : 'classic', ]; } diff --git a/modules/Core/queries/queue.php b/modules/Core/queries/queue.php index 8fb06a0b4c..4d4838b4a0 100644 --- a/modules/Core/queries/queue.php +++ b/modules/Core/queries/queue.php @@ -3,7 +3,7 @@ * Queue query runs any pending tasks * * @var Cache $cache - * @var \DI\Container $container + * @var Illuminate\Container\Container $container * @var Navigation $cc_nav * @var Navigation $navigation * @var Navigation $staffcp_nav diff --git a/modules/Discord Integration/classes/Discord.php b/modules/Discord Integration/classes/Discord.php deleted file mode 100644 index 49c87c5a9e..0000000000 --- a/modules/Discord Integration/classes/Discord.php +++ /dev/null @@ -1,186 +0,0 @@ -getIntegration('Discord'); - if ($integrationUser === null || !$integrationUser->isVerified()) { - return false; - } - - // Filter out any `null` values that snuck into $added or $removed - $added = array_filter($added); - $removed = array_filter($removed); - - $user_discord_id = $integrationUser->data()->identifier; - $role_changes = []; - foreach ($added as $role) { - $role_changes[] = [ - 'user_id' => $user_discord_id, - 'role_id' => $role, - 'action' => 'add' - ]; - } - foreach ($removed as $role) { - $role_changes[] = [ - 'user_id' => $user_discord_id, - 'role_id' => $role, - 'action' => 'remove' - ]; - } - - $result = self::botRequest('/applyRoleChanges', json_encode([ - 'guild_id' => self::getGuildId(), - 'api_key' => Settings::get('mc_api_key'), - 'role_changes' => $role_changes - ])); - - // No point to log the HTTP request failure here, `botRequest` already does it - if ($result === false) { - return false; - } - - $result = json_decode($result, true); - - $status = $result['status']; - if ($status !== 'success') { - $meta = $result['meta'] ?? ''; - - switch ($status) { - case 'bad_request': - case 'not_linked': - case 'unauthorized': - case 'invalid_guild': - Log::getInstance()->log(Log::Action('discord/role_set'), "Status: $status, meta: $meta", $user->data()->id); - break; - default: - Log::getInstance()->log(Log::Action('discord/role_set'), "Invalid 'status' response from bot $status", $user->data()->id); - } - - return false; - } - - $role_changes = $result['role_changes']; - return array_map(static fn (array $role_change) => [ - 'group_id' => $role_change['role_id'], - 'status' => $role_change['status'] - ], $role_changes); - } - - /** - * @return bool Whether the Discord bot is set up properly - */ - public static function isBotSetup(): bool { - return self::$_is_bot_setup ??= Settings::get('discord_integration'); - } - - /** - * Get the associated NamelessMC group ID for a Discord role. - * - * @param DB $db Instance of DB class - * @param int $nameless_group_id The ID of the NamelessMC group - * @return null|int The Discord role ID for the NamelessMC group - */ - public static function getDiscordRoleId(DB $db, int $nameless_group_id): ?int { - $nameless_injector = GroupSyncManager::getInstance()->getInjectorByClass(NamelessMCGroupSyncInjector::class); - - $discord_role_id = $db->get('group_sync', [$nameless_injector->getColumnName(), $nameless_group_id]); - if ($discord_role_id->count()) { - return $discord_role_id->first()->discord_role_id; - } - - return null; - } - - /** - * @return string|null Discord guild ID for this site - */ - public static function getGuildId(): ?string { - return Settings::get('discord'); - } - - /** - * Make a request to the Discord bot. - * - * @param string $url URL of the Discord bot instance - * @param string|null $body Body of the request - * @return false|string Response from the Discord bot or false if the request failed - */ - public static function botRequest(string $url, ?string $body = null) { - $client = HttpClient::post(BOT_URL . $url, $body); - - if ($client->hasError()) { - Log::getInstance()->log(Log::Action('discord/bot_request_failed'), $client->getError()); - return false; - } - - return $client->contents(); - } - - /** - * Get a language term for the Discord Integration module. - * - * @param string $term Term to search for - * @param array $variables Variables to replace in the term - * @return string Language term from the language file - */ - public static function getLanguageTerm(string $term, array $variables = []): string { - if (!isset(self::$_discord_integration_language)) { - self::$_discord_integration_language = new Language(ROOT_PATH . '/modules/Discord Integration/language'); - } - - return self::$_discord_integration_language->get('discord_integration', $term, $variables); - } - - /** - * Cache Discord roles. - * - * @param mixed $roles Discord roles to cache - */ - public static function saveRoles($roles): void { - $roles = [json_encode($roles)]; - file_put_contents(ROOT_PATH . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR . sha1('discord_roles') . '.cache', $roles); - } - - /** - * Get cached Discord roles. - * - * @return array Cached Discord roles - */ - public static function getRoles(): array { - if (file_exists(ROOT_PATH . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR . sha1('discord_roles') . '.cache')) { - return json_decode(file_get_contents(ROOT_PATH . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR . sha1('discord_roles') . '.cache'), true); - } - - return []; - } -} diff --git a/modules/Discord Integration/classes/DiscordApiErrors.php b/modules/Discord Integration/classes/DiscordApiErrors.php deleted file mode 100644 index 436f711784..0000000000 --- a/modules/Discord Integration/classes/DiscordApiErrors.php +++ /dev/null @@ -1,20 +0,0 @@ - $role['id'], - 'name' => Output::getClean($role['name']), - ]; - } - - return $roles; - } - - public function getValidationRules(): array { - return [ - Validate::MIN => 18, - Validate::MAX => 20, - Validate::NUMERIC => true - ]; - } - - public function getValidationMessages(Language $language): array { - return [ - Validate::MIN => Discord::getLanguageTerm('discord_role_id_length', ['min' => 18, 'max' => 20]), - Validate::MAX => Discord::getLanguageTerm('discord_role_id_length', ['min' => 18, 'max' => 20]), - Validate::NUMERIC => Discord::getLanguageTerm('discord_role_id_numeric'), - ]; - } - - public function addGroup(User $user, $group_id): bool { - throw new RuntimeException('Batchable injector should not have this called'); - } - - public function removeGroup(User $user, $group_id): bool { - throw new RuntimeException('Batchable injector should not have this called'); - } - - public function batchAddGroups(User $user, array $group_ids) { - return Discord::updateDiscordRoles($user, $group_ids, []); - } - - public function batchRemoveGroups(User $user, array $group_ids) { - return Discord::updateDiscordRoles($user, [], $group_ids); - } -} diff --git a/modules/Discord Integration/classes/DiscordIntegration.php b/modules/Discord Integration/classes/DiscordIntegration.php deleted file mode 100644 index 5b5b14fa51..0000000000 --- a/modules/Discord Integration/classes/DiscordIntegration.php +++ /dev/null @@ -1,190 +0,0 @@ -_name = 'Discord'; - $this->_icon = 'fab fa-discord'; - $this->_language = $language; - $this->_settings = ROOT_PATH . '/modules/Discord Integration/includes/admin_integrations/discord.php'; - - parent::__construct(); - } - - public function onLinkRequest(User $user) { - $link_method = Settings::get('integration_link_method', 'bot', 'Discord Integration'); - if ($link_method == 'oauth') { - // Link with oauth - Session::put('oauth_method', 'link_integration'); - - $providers = NamelessOAuth::getInstance()->getProvidersAvailable(); - $provider = $providers['discord']; - - Redirect::to($provider['url']); - } else { - // Discord bot linking - $token = uniqid('', true); - - $integrationUser = new IntegrationUser($this); - $integrationUser->linkIntegration($user, null, null, false, $token); - - Session::flash('connections_success', Discord::getLanguageTerm('discord_id_confirm', ['token' => $token])); - } - } - - public function onVerifyRequest(User $user) { - $token = uniqid('', true); - - $integrationUser = new IntegrationUser($this, $user->data()->id, 'user_id'); - $integrationUser->update([ - 'code' => $token - ]); - - Session::flash('connections_success', Discord::getLanguageTerm('discord_id_confirm', ['token' => $token])); - } - - public function onUnlinkRequest(User $user) { - $integrationUser = new IntegrationUser($this, $user->data()->id, 'user_id'); - $integrationUser->unlinkIntegration(); - - // Remove any linked Discord roles - $roles = array_unique(array_map(static function ($group_id) { - return Discord::getDiscordRoleId(DB::getInstance(), $group_id); - }, $user->getAllGroupIds())); - - Discord::updateDiscordRoles($user, [], $roles); - - Session::flash('connections_success', $this->_language->get('user', 'integration_unlinked', ['integration' => Output::getClean($this->_name)])); - } - - public function onSuccessfulVerification(IntegrationUser $integrationUser) { - // attempt to update their Discord roles - $user = $integrationUser->getUser(); - - $roles = array_unique(array_map(static function ($group_id) { - return Discord::getDiscordRoleId(DB::getInstance(), $group_id); - }, $user->getAllGroupIds())); - - Discord::updateDiscordRoles($user, $roles, []); - Session::flash('connections_success', $this->_language->get('user', 'integration_linked', ['integration' => Output::getClean($this->_name)])); - } - - public function validateUsername(string $username, int $integration_user_id = 0): bool { - $validation = Validate::check(['username' => $username], [ - 'username' => [ - Validate::REQUIRED => true, - Validate::MIN => 2, - Validate::MAX => 32 - ] - ])->messages([ - 'username' => [ - Validate::REQUIRED => $this->_language->get('admin', 'integration_username_required', ['integration' => $this->getName()]), - Validate::MIN => $this->_language->get('admin', 'integration_username_invalid', ['integration' => $this->getName()]), - Validate::MAX => $this->_language->get('admin', 'integration_username_invalid', ['integration' => $this->getName()]) - ] - ]); - - if (count($validation->errors())) { - // Validation errors - foreach ($validation->errors() as $error) { - $this->addError($error); - } - } else { - // Ensure identifier doesn't already exist - $exists = DB::getInstance()->query("SELECT * FROM nl2_users_integrations WHERE integration_id = ? AND username = ? AND id <> ?", [$this->data()->id, $username, $integration_user_id]); - if ($exists->count()) { - $this->addError($this->_language->get('user', 'integration_username_already_linked', ['integration' => $this->getName()])); - return false; - } - } - - return $validation->passed(); - } - - public function validateIdentifier(string $identifier, int $integration_user_id = 0): bool { - $validation = Validate::check(['identifier' => $identifier], [ - 'identifier' => [ - Validate::REQUIRED => true, - Validate::NUMERIC => true, - Validate::MIN => 17, - Validate::MAX => 20 - ] - ])->messages([ - 'identifier' => [ - Validate::REQUIRED => $this->_language->get('admin', 'integration_identifier_required', ['integration' => $this->getName()]), - Validate::NUMERIC => $this->_language->get('admin', 'integration_identifier_invalid', ['integration' => $this->getName()]), - Validate::MIN => $this->_language->get('admin', 'integration_identifier_invalid', ['integration' => $this->getName()]), - Validate::MAX => $this->_language->get('admin', 'integration_identifier_invalid', ['integration' => $this->getName()]), - ] - ]); - - if (count($validation->errors())) { - // Validation errors - foreach ($validation->errors() as $error) { - $this->addError($error); - } - } else { - // Ensure identifier doesn't already exist - $exists = DB::getInstance()->query("SELECT * FROM nl2_users_integrations WHERE integration_id = ? AND identifier = ? AND id <> ?", [$this->data()->id, $identifier, $integration_user_id]); - if ($exists->count()) { - $this->addError($this->_language->get('user', 'integration_identifier_already_linked', ['integration' => $this->getName()])); - return false; - } - } - - return $validation->passed(); - } - - public function allowLinking(): bool { - $link_method = Settings::get('integration_link_method', 'bot', 'Discord Integration'); - if ($link_method == 'oauth') { - return NamelessOAuth::getInstance()->isSetup('discord'); - } else { - return Discord::isBotSetup(); - } - } - - public function onRegistrationPageLoad(Fields $fields) { - // Nothing to do here - } - - public function beforeRegistrationValidation(Validate $validate) { - // Nothing to do here - } - - public function afterRegistrationValidation() { - // Nothing to do here - } - - public function successfulRegistration(User $user) { - // Link integration if user registered using discord oauth - if (Session::exists('oauth_register_data')) { - $data = json_decode(Session::get('oauth_register_data'), true); - if ($data['provider'] == 'discord' && isset($data['data']['username'])) { - - $username = $data['data']['username']; - $discord_id = $data['data']['id']; - if ($this->validateIdentifier($discord_id) && $this->validateUsername($username)) { - $integrationUser = new IntegrationUser($this); - $integrationUser->linkIntegration($user, $discord_id, $username, true); - $integrationUser->verifyIntegration(); - } - } - } - } - - public function syncIntegrationUser(IntegrationUser $integration_user): bool { - $this->addError($this->_language->get('admin', 'integration_sync_not_supported')); - - return false; - } -} diff --git a/modules/Discord Integration/classes/Events/DiscordWebhookFormatterEvent.php b/modules/Discord Integration/classes/Events/DiscordWebhookFormatterEvent.php deleted file mode 100644 index 225516640c..0000000000 --- a/modules/Discord Integration/classes/Events/DiscordWebhookFormatterEvent.php +++ /dev/null @@ -1,35 +0,0 @@ -event = $event; - $this->format = $format; - $this->data = $data; - } - - public static function description(): string { - return 'Discord webhook formatter'; - } - - public static function return(): bool { - return true; - } - - public static function internal(): bool { - return true; - } -} diff --git a/modules/Discord Integration/hooks/DiscordHook.php b/modules/Discord Integration/hooks/DiscordHook.php deleted file mode 100644 index bf2a7601e1..0000000000 --- a/modules/Discord Integration/hooks/DiscordHook.php +++ /dev/null @@ -1,77 +0,0 @@ -params() - : $event; - - $webhook_url = $event instanceof AbstractEvent - ? $webhook_url - : $params['webhook']; - - $name = $event instanceof AbstractEvent - ? $event::name() - : $params['event']; - - $format = $event instanceof DiscordDispatchable - ? $event->toDiscordWebhook() - : []; - - $return = EventHandler::executeEvent(new DiscordWebhookFormatterEvent( - $name, - $format, - $params, - ))['format']; - - if (is_array($return) && isset($return['webhook'])) { - unset($return['webhook']); - } - - if ($return instanceof DiscordWebhookBuilder) { - $return = $return->toArray(); - } - - if (!is_array($return) || !count($return)) { - try { - // Create generic fallback embed if no embeds are provided - $return = DiscordWebhookBuilder::make() - ->setUsername($params['username'] . ' | ' . SITE_NAME) - ->setAvatarUrl($params['avatar_url']) - ->addEmbed(function (DiscordEmbed $embed) use ($params) { - return $embed - ->setTitle($params['title']) - ->setDescription(Text::embedSafe($params['content_full'])) - ->setUrl($params['url']) - ->setFooter(Text::embedSafe($params['content'])); - }) - ->toArray(); - } catch (Exception $exception) { - // Likely enabled discord webhook for event - // that doesn't have valid fallback params - ErrorHandler::logWarning("Error creating fallback Discord embed for {$event::name()}: {$exception->getMessage()}. Does it support embeds?"); - return; - } - } - - $json = json_encode($return, JSON_UNESCAPED_SLASHES); - - $httpClient = HttpClient::post($webhook_url, $json, [ - 'headers' => [ - 'Content-Type' => 'application/json', - ], - ]); - - if ($httpClient->hasError()) { - trigger_error($httpClient->getError()); - } - } -} diff --git a/modules/Discord Integration/includes/admin_integrations/discord.php b/modules/Discord Integration/includes/admin_integrations/discord.php deleted file mode 100644 index e56201e856..0000000000 --- a/modules/Discord Integration/includes/admin_integrations/discord.php +++ /dev/null @@ -1,21 +0,0 @@ -get('admin', 'integration_updated_successfully')); - Redirect::to(URL::build('/panel/core/integrations/', 'integration=' . $integration->getName())); - } - } else { - $errors[] = $language->get('general', 'invalid_token'); - } -} - -$smarty->assign([ - 'OAUTH' => $language->get('admin', 'oauth'), - 'DISCORD_BOT' => Discord::getLanguageTerm('discord_bot'), - 'LINK_METHOD' => Discord::getLanguageTerm('link_method'), - 'LINK_METHOD_VALUE' => Settings::get('integration_link_method', 'bot', 'Discord Integration'), - 'SETTINGS_TEMPLATE' => 'integrations/discord/integration_settings.tpl' -]); diff --git a/modules/Discord Integration/includes/admin_widgets/discord.php b/modules/Discord Integration/includes/admin_widgets/discord.php deleted file mode 100644 index 90caebd2a0..0000000000 --- a/modules/Discord Integration/includes/admin_widgets/discord.php +++ /dev/null @@ -1,48 +0,0 @@ -setCache('social_media'); - -if (Input::exists()) { - if (Token::check()) { - if (isset($_POST['theme'])) { - $cache->store('discord_widget_theme', $_POST['theme']); - } - - $cache->store('discord', $guild_id); - - $success = $language->get('admin', 'widget_updated'); - - } else { - $errors = [$language->get('general', 'invalid_token')]; - } -} - -if ($cache->isCached('discord_widget_theme')) { - $discord_theme = $cache->retrieve('discord_widget_theme'); -} else { - $discord_theme = 'dark'; -} - -if (isset($errors) && count($errors)) { - $smarty->assign([ - 'ERRORS' => $errors, - ]); -} - -$smarty->assign([ - 'DISCORD_THEME' => Discord::getLanguageTerm('discord_widget_theme'), - 'DISCORD_THEME_VALUE' => $discord_theme, - 'SETTINGS_TEMPLATE' => 'discord_integration/widgets/discord.tpl', - 'DARK' => $language->get('admin', 'dark'), - 'LIGHT' => $language->get('admin', 'light') -]); diff --git a/modules/Discord Integration/includes/endpoints/SetDiscordRolesEndpoint.php b/modules/Discord Integration/includes/endpoints/SetDiscordRolesEndpoint.php deleted file mode 100644 index 59aa6717ac..0000000000 --- a/modules/Discord Integration/includes/endpoints/SetDiscordRolesEndpoint.php +++ /dev/null @@ -1,39 +0,0 @@ -_route = 'discord/set-roles'; - $this->_module = 'Discord Integration'; - $this->_description = 'Set a NamelessMC user\'s according to the supplied Discord Role ID list'; - $this->_method = 'POST'; - } - - public function execute(Nameless2API $api): void { - $api->validateParams($_POST, ['user']); - - if (!Discord::isBotSetup()) { - $api->throwError(DiscordApiErrors::ERROR_DISCORD_INTEGRATION_DISABLED); - } - - $user = $api->getUser('id', $_POST['user']); - - $log_array = GroupSyncManager::getInstance()->broadcastChange( - $user, - DiscordGroupSyncInjector::class, - $_POST['roles'] ?? [] - ); - - if (count($log_array)) { - Log::getInstance()->log(Log::Action('discord/role_set'), json_encode($log_array), $user->data()->id); - } - - $api->returnArray(array_merge(['message' => Discord::getLanguageTerm('group_updated')], $log_array)); - } -} diff --git a/modules/Discord Integration/includes/endpoints/SubmitDiscordRoleListEndpoint.php b/modules/Discord Integration/includes/endpoints/SubmitDiscordRoleListEndpoint.php deleted file mode 100644 index b960c71d9f..0000000000 --- a/modules/Discord Integration/includes/endpoints/SubmitDiscordRoleListEndpoint.php +++ /dev/null @@ -1,33 +0,0 @@ -_route = 'discord/submit-role-list'; - $this->_module = 'Discord Integration'; - $this->_description = 'Update NamelessMC\'s list of your Discord guild\'s roles.'; - $this->_method = 'POST'; - } - - public function execute(Nameless2API $api): void { - $roles = []; - - if ($_POST['roles'] != null) { - $roles = $_POST['roles']; - } - - try { - Discord::saveRoles($roles); - } catch (Exception $e) { - $api->throwError(DiscordApiErrors::ERROR_UNABLE_TO_UPDATE_DISCORD_ROLES, $e->getMessage(), Response::HTTP_INTERNAL_SERVER_ERROR); - } - - $api->returnArray(['message' => Discord::getLanguageTerm('discord_settings_updated')]); - } -} diff --git a/modules/Discord Integration/includes/endpoints/SyncDiscordRolesEndpoint.php b/modules/Discord Integration/includes/endpoints/SyncDiscordRolesEndpoint.php deleted file mode 100644 index cfe66f74e2..0000000000 --- a/modules/Discord Integration/includes/endpoints/SyncDiscordRolesEndpoint.php +++ /dev/null @@ -1,32 +0,0 @@ -_route = 'discord/{user}/sync-roles'; - $this->_module = 'Discord Integration'; - $this->_description = 'Set a NamelessMC user\'s according to the supplied Discord Role ID list'; - $this->_method = 'POST'; - } - - public function execute(Nameless2API $api, User $user): void { - $api->validateParams($_POST, []); - - if (!Discord::isBotSetup()) { - $api->throwError(DiscordApiErrors::ERROR_DISCORD_INTEGRATION_DISABLED); - } - - $log_array = GroupSyncManager::getInstance()->broadcastGroupChange( - $user, - DiscordGroupSyncInjector::class, - $_POST['add'] ?? [], - $_POST['remove'] ?? [] - ); - - if (count($log_array)) { - Log::getInstance()->log(Log::Action('discord/role_set'), json_encode($log_array), $user->data()->id); - } - - $api->returnArray(array_merge(['message' => Discord::getLanguageTerm('group_updated')], $log_array)); - } -} diff --git a/modules/Discord Integration/includes/endpoints/UpdateDiscordBotSettingsEndpoint.php b/modules/Discord Integration/includes/endpoints/UpdateDiscordBotSettingsEndpoint.php deleted file mode 100644 index 5ad2d1e682..0000000000 --- a/modules/Discord Integration/includes/endpoints/UpdateDiscordBotSettingsEndpoint.php +++ /dev/null @@ -1,55 +0,0 @@ -_route = 'discord/update-bot-settings'; - $this->_module = 'Discord Integration'; - $this->_description = 'Updates the Discord Bot URL and/or Guild ID setting'; - $this->_method = 'POST'; - } - - public function execute(Nameless2API $api): void { - if (isset($_POST['url'])) { - try { - Settings::set('discord_bot_url', $_POST['url']); - } catch (Exception $e) { - $api->throwError(DiscordApiErrors::ERROR_UNABLE_TO_SET_DISCORD_BOT_URL, $e->getMessage(), Response::HTTP_INTERNAL_SERVER_ERROR); - } - } - - if (isset($_POST['guild_id'])) { - try { - Settings::set('discord', $_POST['guild_id']); - } catch (Exception $e) { - $api->throwError(DiscordApiErrors::ERROR_UNABLE_TO_SET_DISCORD_GUILD_ID, $e->getMessage(), Response::HTTP_INTERNAL_SERVER_ERROR); - } - } - - if (isset($_POST['bot_username'])) { - try { - Settings::set('discord_bot_username', $_POST['bot_username']); - } catch (Exception $e) { - $api->throwError(DiscordApiErrors::ERROR_UNABLE_TO_SET_DISCORD_BOT_USERNAME, $e->getMessage(), Response::HTTP_INTERNAL_SERVER_ERROR); - } - } - - // If bot url and username is empty then its setup for the first time - if (empty(BOT_URL) && empty(BOT_USERNAME)) { - Settings::set('discord_integration', 1); - } - - if (isset($_POST['bot_user_id'])) { - Settings::set('discord_bot_user_id', $_POST['bot_user_id']); - } - - $api->returnArray(['message' => Discord::getLanguageTerm('discord_settings_updated')]); - } -} diff --git a/modules/Discord Integration/includes/endpoints/UpdateDiscordUsernames.php b/modules/Discord Integration/includes/endpoints/UpdateDiscordUsernames.php deleted file mode 100644 index c3470d89e3..0000000000 --- a/modules/Discord Integration/includes/endpoints/UpdateDiscordUsernames.php +++ /dev/null @@ -1,44 +0,0 @@ - Discord username to update - * - * @return string JSON Array - */ -class UpdateDiscordUsernames extends KeyAuthEndpoint { - - public function __construct() { - $this->_route = 'discord/update-usernames'; - $this->_module = 'Discord Integration'; - $this->_description = 'Bulk update many user\'s Discord usernames to display on their settings page.'; - $this->_method = 'POST'; - } - - public function execute(Nameless2API $api): void { - $api->validateParams($_POST, ['users']); - - try { - $integration = Integrations::getInstance()->getIntegration('Discord'); - $updated = 0; - foreach ($_POST['users'] as $row) { - $integrationUser = new IntegrationUser($integration, $row['id'], 'identifier'); - if ($integrationUser->exists()) { - $discord_username = Output::getClean($row['name']); - - if ($integrationUser->data()->username != $discord_username) { - $integrationUser->update([ - 'username' => $discord_username - ]); - - $updated++; - } - } - } - } catch (Exception $e) { - $api->throwError(DiscordApiErrors::ERROR_UNABLE_TO_SET_DISCORD_BOT_USERNAME, $e->getMessage(), Response::HTTP_INTERNAL_SERVER_ERROR); - } - - $api->returnArray(['message' => Discord::getLanguageTerm('discord_usernames_updated'), 'updated_users' => $updated]); - } -} diff --git a/modules/Discord Integration/init.php b/modules/Discord Integration/init.php deleted file mode 100644 index e84a0aceb9..0000000000 --- a/modules/Discord Integration/init.php +++ /dev/null @@ -1,5 +0,0 @@ -esto.{{linkEnd}}", - "discord_integration/discord_id_length": "Asegúrate de que tu ID de Discord tenga 18 caracteres.", - "discord_integration/discord_id_numeric": "Asegúrese de que su ID de Discord sea numérica (No debe contener letras).", - "discord_integration/discord_integration_not_setup": "La integración de Discord no está configurada", - "discord_integration/discord_invite_info": "Para invitar al bot Nameless Link a tu servidor Discord, haz clic en {{inviteLinkStart}}aquí{{inviteLinkEnd}}. A continuación, ejecuta el comando /apiurl para enlazar el bot con tu sitio web. También puedes {{selfHostLinkStart}}alojar el bot tú mismo{{selfHostLinkEnd}}.", - "discord_integration/discord_role_id": "ID de rol de discord", - "discord_integration/discord_role_id_length": "La ID del rol de discordia debe tener 18 dígitos.", - "discord_integration/discord_role_id_numeric": "El ID de rol de Discord debe ser numérico.", - "discord_integration/discord_settings_updated": "La configuración de discord se ha actualizado con éxito", - "discord_integration/discord_usernames_updated": "Los nombres de usuario de discord se han actualizado con éxito", - "discord_integration/discord_widget_theme": "Tema del Widget de Discord", - "discord_integration/enable_discord_integration": "¿Habilitar la integración de Discord?" -} diff --git a/modules/Discord Integration/language/es_ES.json b/modules/Discord Integration/language/es_ES.json deleted file mode 100644 index 86331f982d..0000000000 --- a/modules/Discord Integration/language/es_ES.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "discord_integration/discord": "Discord", - "discord_integration/discord_bot_must_be_setup": "No se puede habilitar la integración de Discord hasta que no se haya configurado el bot. Para obtener información, por favor {{linkStart}}haga clic aquí{{linkEnd}}.", - "discord_integration/discord_bot_setup": "¿Bot instalado?", - "discord_integration/discord_guild_id": "ID del servidor de Discord", - "discord_integration/discord_id_help": "Para obtener información sobre dónde encontrar IDs de Discord, por favor lea {{linkStart}}esto.{{linkEnd}}", - "discord_integration/discord_id_length": "Por favor, asegúrate de que tu ID de servidor de Discord tiene entre {{min}} y {{max}} dígitos.", - "discord_integration/discord_id_numeric": "Por favor, asegúrese de que su ID del servidor de Discord es numérica (sólo números).", - "discord_integration/discord_integration_not_setup": "La integración de Discord no está configurada", - "discord_integration/discord_role_id": "ID del rol de Discord", - "discord_integration/discord_role_id_length": "La ID del rol de Discord debe tener entre {{min}} y {{max}} digitos.", - "discord_integration/discord_role_id_numeric": "La ID del rol de Discord debe ser numérica.", - "discord_integration/discord_settings_updated": "La configuración de Discord se ha actualizado con éxito", - "discord_integration/discord_usernames_updated": "Los nombres de usuario de Discord se han actualizado con éxito", - "discord_integration/discord_widget_theme": "Tema del Widget de Discord", - "discord_integration/enable_discord_integration": "¿Activar la integración de Discord?", - "discord_integration/discord_id_confirm": "Por favor, ejecuta el comando \"/verify token:{{token}}\" en Discord para terminar de vincular tu cuenta de Discord.", - "discord_integration/discord_invite_info": "Para invitar al bot Nameless Link a tu servidor de Discord, haz clic en {{inviteLinkStart}}aquí{{inviteLinkEnd}}. A continuación, ejecuta el comando {{command}} para enlazar el bot con tu sitio web. También puedes {{selfHostLinkStart}}alojar el bot tú mismo{{selfHostLinkEnd}}.", - "discord_integration/discord_widget_disabled": "El widget está desactivado para el servidor de Discord especificado. Ve a la pestaña \"Widget\" en la configuración de tu servidor de Discord y asegúrate de que el widget de Discord está activado y de que el ID es correcto.", - "discord_integration/discord_widget_error": "Se ha producido un error al obtener el widget de Discord: {{error}}.", - "discord_integration/discord_id_required": "Por favor, introduce la ID del servidor de Discord.", - "discord_integration/discord_bot": "Bot de Discord", - "discord_integration/link_method": "Método de conexión" -} diff --git a/modules/Discord Integration/language/fa_IR.json b/modules/Discord Integration/language/fa_IR.json deleted file mode 100644 index 1bf9852a71..0000000000 --- a/modules/Discord Integration/language/fa_IR.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "discord_integration/discord_bot_setup": "ربات راه اندازی شد؟", - "discord_integration/discord_guild_id": "آیدی سرور دیسکورد", - "discord_integration/discord_id_help": "برای اطلاع در مورد مکان یافتن آیدی های دیسکورد، لطفاً {{linkEnd}}این را بخوانید.{{linkStart}}", - "discord_integration/discord_id_numeric": "لطفاً مطمئن شوید که آیدی سرور دیسکورد شما عددی است (فقط اعداد).", - "discord_integration/discord_id_required": "لطفا آیدی سرور دیسکورد خود را وارد کنید.", - "discord_integration/discord_integration_not_setup": "ادغام دیسکورد تنظیم نشده است", - "discord_integration/discord_role_id": "آیدی نقش دیسکورد", - "discord_integration/discord_role_id_length": "آیدی نقش دیسکورد باید بین {{min}} و {{max}} رقم باشد.", - "discord_integration/discord_role_id_numeric": "آیدی نقش دیسکورد باید عددی باشد.", - "discord_integration/discord_widget_error": "هنگام دریافت ویجت دیسکورد خطایی روی داد: {{error}}.", - "discord_integration/discord_bot": "ربات دیسکورد", - "discord_integration/discord": "دیسکورد", - "discord_integration/discord_id_confirm": "لطفاً دستور \"verify token:{{token}}/\" را در دیسکورد اجرا کنید تا پیوند حساب دیسکورد شما به پایان برسد.", - "discord_integration/discord_bot_must_be_setup": "تا زمانی که ربات را راه اندازی نکرده باشید، نمی توان ادغام دیسکورد را فعال کرد. برای اطلاعات، لطفاً {{linkEnd}}اینجا{{linkStart}} را کلیک کنید.", - "discord_integration/discord_id_length": "لطفاً مطمئن شوید که آیدی سرور دیسکورد شما بین {{min}} و {{max}} رقم باشد.", - "discord_integration/discord_invite_info": "برای لینک دعوت ربات Nameless به سرور دیسکورد خود، {{inviteLinkEnd}}اینجا{{inviteLinkStart}} را کلیک کنید. سپس دستور {{command}} را اجرا کنید تا ربات با وب سایت شما پیوند داده شود. از طرف دیگر، می‌توانید {{selfHostLinkEnd}} ربات را خودتان میزبانی کنید{{selfHostLinkStart}}.", - "discord_integration/discord_widget_disabled": "ویجت برای سرور دیسکورد مشخص شده غیرفعال است. لطفاً در تنظیمات سرور دیسکورد خود به تب \"ویجت\" بروید و مطمئن شوید که ویجت دیسکورد فعال است و آیدی صحیح است.", - "discord_integration/discord_widget_theme": "تم ویجت دیسکورد", - "discord_integration/enable_discord_integration": "ادغام دیسکورد فعال شود؟", - "discord_integration/discord_settings_updated": "تنظیمات دیسکورد با موفقیت به روز شد", - "discord_integration/discord_usernames_updated": "نام های کاربری دیسکورد با موفقیت به روز شد", - "discord_integration/link_method": "روش پیوند" -} diff --git a/modules/Discord Integration/language/fi_FI.json b/modules/Discord Integration/language/fi_FI.json deleted file mode 100644 index 0967ef424b..0000000000 --- a/modules/Discord Integration/language/fi_FI.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/modules/Discord Integration/language/fr_FR.json b/modules/Discord Integration/language/fr_FR.json deleted file mode 100644 index 267c7cb66c..0000000000 --- a/modules/Discord Integration/language/fr_FR.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "discord_integration/discord_bot_must_be_setup": "Vous ne pouvez pas activer l'intégration Discord avant d'avoir configuré le robot. Pour plus d'informations, veuillez cliquer sur {{linkStart}}here{{linkEnd}}.", - "discord_integration/discord_bot_setup": "Le robot est installé ?", - "discord_integration/discord_guild_id": "ID du serveur Discord", - "discord_integration/discord_id_help": "Pour savoir où trouver les identifiants Discord, veuillez lire {{linkStart}}this.{{linkEnd}}", - "discord_integration/discord_integration_not_setup": "L'intégration de Discord n'est pas configurée", - "discord_integration/discord_settings_updated": "Les paramètres de Discord ont été mis à jour avec succès", - "discord_integration/discord_usernames_updated": "Les noms d'utilisateur Discord ont été mis à jour avec succès", - "discord_integration/discord_widget_theme": "Thème Discord Widget", - "discord_integration/discord_widget_error": "Une erreur s'est produite lors de l'obtention du widget Discord : {{error}}.", - "discord_integration/discord_id_required": "Veuillez entrer votre ID de serveur Discord.", - "discord_integration/discord_id_confirm": "Veuillez exécuter la commande \"/verify token:{{token}}\" dans Discord pour terminer la liaison de votre compte Discord.", - "discord_integration/discord_id_length": "Veuillez vous assurer que votre ID de serveur Discord est compris entre {{min}} et {{max}} chiffres.", - "discord_integration/discord_role_id": "Discord Role ID", - "discord_integration/discord": "Discord", - "discord_integration/discord_id_numeric": "Veuillez vous assurer que votre ID de serveur Discord est numérique (uniquement des chiffres).", - "discord_integration/discord_invite_info": "Pour inviter le bot Nameless Link sur votre serveur Discord, cliquez sur {{inviteLinkStart}} ici{{inviteLinkEnd}}. Ensuite, exécutez la commande {{command}} pour lier le robot à votre site Web. Vous pouvez également {{selfHostLinkStart}} héberger le robot vous-même{{selfHostLinkEnd}}.", - "discord_integration/discord_role_id_length": "L'ID du rôle Discord doit être compris entre {{min}} et {{max}} chiffres.", - "discord_integration/discord_role_id_numeric": "L'ID de rôle Discord doit être numérique.", - "discord_integration/discord_widget_disabled": "Le widget est désactivé pour le serveur Discord spécifié. Veuillez accéder à l'onglet \"Widget\" dans les paramètres de votre serveur Discord, et assurez-vous que le widget Discord est activé et que l'ID est correct.", - "discord_integration/enable_discord_integration": "Activer l'intégration de Discord ?", - "discord_integration/discord_bot": "Bot Discord", - "discord_integration/link_method": "Méthode de liaison" -} diff --git a/modules/Discord Integration/language/hr_HR.json b/modules/Discord Integration/language/hr_HR.json deleted file mode 100644 index 0967ef424b..0000000000 --- a/modules/Discord Integration/language/hr_HR.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/modules/Discord Integration/language/hu_HU.json b/modules/Discord Integration/language/hu_HU.json deleted file mode 100644 index 8d8cce8418..0000000000 --- a/modules/Discord Integration/language/hu_HU.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "discord_integration/discord": "Discord", - "discord_integration/discord_bot_setup": "A Bot be van állítva?", - "discord_integration/discord_role_id": "Discord Rang ID", - "discord_integration/discord_role_id_length": "A Discord Rang ID {{min}} és {{max}} számjegy hosszú lehet.", - "discord_integration/discord_widget_error": "Hiba történt a Discord widget lekérdezésekor: {{error}}.", - "discord_integration/discord_id_help": "Információért, hogy hol találod meg a Discord ID-ket, kérlek, olvasd el {{linkStart}}ezt.{{linkEnd}}", - "discord_integration/discord_usernames_updated": "Discord felhasználónevek sikeresen frissítve", - "discord_integration/discord_widget_theme": "Discord Widget Téma", - "discord_integration/discord_invite_info": "A Nameless Link Bot meghívásához kattints a Discord szervereden {{inviteLinkStart}}erre{{inviteLinkEnd}} gombra. Ezután futtasd a {{command}} parancsot, hogy a botot összekapcsold a weboldallal. Alternatív megoldásként te is {{selfHostLinkStart}}hostolhatod a botot{{selfHostLinkEnd}}.", - "discord_integration/discord_guild_id": "Discord Szerver ID", - "discord_integration/discord_id_confirm": "Futtasd a \"/verify {{token}}\" parancsot Discordon, hogy befejezd a Discord fiók összekapcsolását.", - "discord_integration/discord_id_length": "Győződj meg arról, hogy a Discord Szerver ID {{min}} és a {{max}} számjegy hosszú.", - "discord_integration/discord_id_numeric": "Ellenőrizd, hogy a Discord Szerver ID csak számjegyekből áll.", - "discord_integration/discord_id_required": "Add meg a Discord Szerver ID-jét.", - "discord_integration/discord_role_id_numeric": "A Discord Rang ID csak számjegyekből állhat.", - "discord_integration/discord_settings_updated": "Discord beállítások sikeresen frissítve", - "discord_integration/discord_bot_must_be_setup": "Nem kapcsolhatod be addig a Discord Integrációt, ameddig nem állítottad be a Bot-ot. További információért {{linkStart}}ide{{linkEnd}} kattints.", - "discord_integration/discord_widget_disabled": "A widget le van tiltva a Discord szerveren. Menj be a Discrod szerver beállításokba a 'Widget' fülre és ellenőrizd hogy a widget be van-e kapcsolva illetvő hogy az ID helyes.", - "discord_integration/enable_discord_integration": "Bekapcsolod a Discord integrációt?", - "discord_integration/discord_integration_not_setup": "Discord integráció nincs beállítva", - "discord_integration/discord_bot": "Discord Bot", - "discord_integration/link_method": "Kapcsolódás módszer" -} diff --git a/modules/Discord Integration/language/id_ID.json b/modules/Discord Integration/language/id_ID.json deleted file mode 100644 index 19f845f6f1..0000000000 --- a/modules/Discord Integration/language/id_ID.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "discord_integration/discord_bot": "Bot Discord", - "discord_integration/discord_bot_must_be_setup": "Tidak dapat mengaktifkan Integrasi Perselisihan hingga Anda menyiapkan bot. Untuk informasi, silakan klik {{linkStart}}di sini{{linkEnd}}.", - "discord_integration/discord_bot_setup": "Penyiapan bot?", - "discord_integration/discord_guild_id": "ID Server Discord", - "discord_integration/discord_id_help": "Untuk informasi tentang tempat menemukan Discord ID, harap baca {{linkStart}}ini.{{linkEnd}}", - "discord_integration/discord_id_numeric": "Harap pastikan ID Server Discord Anda adalah numerik (hanya Angka).", - "discord_integration/discord_id_required": "Silakan masukkan ID Server Discord Anda.", - "discord_integration/discord_integration_not_setup": "Integrasi Discord tidak diatur", - "discord_integration/discord_role_id": "ID Role Discord", - "discord_integration/discord_role_id_length": "ID Role Discord harus antara {{min}} dan {{max}} digit.", - "discord_integration/discord_role_id_numeric": "ID Role Discord harus numerik.", - "discord_integration/discord_settings_updated": "Pengaturan Discord berhasil diperbarui", - "discord_integration/discord_usernames_updated": "Nama pengguna Discord berhasil diperbarui", - "discord_integration/discord_widget_error": "Terjadi kesalahan saat mendapatkan widget Discord: {{error}}.", - "discord_integration/discord_widget_theme": "Tema Widget Discord", - "discord_integration/enable_discord_integration": "Aktifkan integrasi Discord?", - "discord_integration/link_method": "Metode tautan", - "discord_integration/discord": "Discord", - "discord_integration/discord_id_confirm": "Silakan jalankan perintah \"/verify token:{{token}}\" di Discord untuk menyelesaikan penautan akun Discord Anda.", - "discord_integration/discord_id_length": "Pastikan ID Server Discord Anda antara {{min}} dan {{max}} digit.", - "discord_integration/discord_invite_info": "Untuk mengundang bot Nameless Link ke server Discord Anda, klik {{inviteLinkStart}}di sini{{inviteLinkEnd}}. Kemudian, jalankan perintah {{command}} untuk menautkan bot dengan situs web Anda. Atau, Anda dapat {{selfHostLinkStart}}menghosting bot sendiri{{selfHostLinkEnd}}.", - "discord_integration/discord_widget_disabled": "Widget dinonaktifkan untuk server Discord yang ditentukan. Silakan buka tab 'Widget' di pengaturan server Discord Anda, dan pastikan widget Discord diaktifkan dan ID sudah benar." -} diff --git a/modules/Discord Integration/language/it_IT.json b/modules/Discord Integration/language/it_IT.json deleted file mode 100644 index c084d5e8b4..0000000000 --- a/modules/Discord Integration/language/it_IT.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "discord_integration/discord": "Discord", - "discord_integration/discord_bot_must_be_setup": "Non è possibile abilitare l'integrazione con Discord fino a che il bot non sarà configurato. Per informazioni, {{linkStart}}clicca qui{{linkEnd}}.", - "discord_integration/discord_bot_setup": "Bot configurato?", - "discord_integration/discord_guild_id": "ID Server Discord", - "discord_integration/discord_id_help": "Per informazioni su dove trovare gli ID Discord, leggi {{linkStart}}questo.{{linkEnd}}", - "discord_integration/discord_id_length": "Assicurati che la lunghezza del tuo ID Discord Server sia compresa tra {{min}} e {{max}} caratteri.", - "discord_integration/discord_id_numeric": "Assicurati che l'ID del tuo server Discord sia numerico (solo numeri).", - "discord_integration/discord_role_id": "ID ruolo Discord", - "discord_integration/discord_role_id_length": "L'ID del ruolo Discord deve contenere tra le {{min}} e le {{max}} cifre.", - "discord_integration/discord_role_id_numeric": "L'ID ruolo Discord deve essere numerico.", - "discord_integration/discord_settings_updated": "Impostazioni di Discord aggiornate con successo", - "discord_integration/discord_widget_theme": "Tema del widget Discord", - "discord_integration/enable_discord_integration": "Abilitare l'integrazione di Discord?", - "discord_integration/discord_widget_error": "Si è verificato un errore durante il recupero del widget Discord: {{error}}.", - "discord_integration/discord_id_required": "Inserisci il tuo ID del server Discord.", - "discord_integration/discord_id_confirm": "Esegui il comando \"/verify token:{{token}}\" su Discord per completare il collegamento del tuo account Discord.", - "discord_integration/discord_integration_not_setup": "L'integrazione di Discord non è impostata", - "discord_integration/discord_invite_info": "Per invitare il bot Nameless Link sul tuo server Discord, fai clic {{inviteLinkStart}}qui{{inviteLinkEnd}}. Quindi, esegui il comando {{command}} per collegare il bot al tuo sito web. In alternativa, puoi {{selfHostLinkStart}}ospitare il bot da solo{{selfHostLinkEnd}}.", - "discord_integration/discord_usernames_updated": "Nomi utente Discord aggiornati correttamente", - "discord_integration/discord_widget_disabled": "Il widget è disabilitato per il server Discord specificato. Vai alla scheda \"Widget\" nelle impostazioni del tuo server Discord e assicurati che il widget Discord sia abilitato e che l'ID sia corretto.", - "discord_integration/discord_bot": "Bot Discord", - "discord_integration/link_method": "Metodo di collegamento" -} diff --git a/modules/Discord Integration/language/ja_JP.json b/modules/Discord Integration/language/ja_JP.json deleted file mode 100644 index e3158c8e44..0000000000 --- a/modules/Discord Integration/language/ja_JP.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "discord_integration/discord": "Discord", - "discord_integration/discord_bot_must_be_setup": "ボットを設定するまでは、Discord統合を有効にすることはできません。詳細については、{{linkStart}}ここをクリック{{linkEnd}} してください。", - "discord_integration/discord_bot_setup": "ボットの設定ですか?", - "discord_integration/discord_guild_id": "DiscordサーバーID", - "discord_integration/discord_id_confirm": "Discordで「/verify token:{{token}}」コマンドを実行し、Discordアカウントの連携を完了してください。", - "discord_integration/discord_id_help": "Discord IDがどこにあるかについては、{{linkStart}}こちら{{linkEnd}} をご確認ください。", - "discord_integration/discord_id_length": "Discord サーバーIDは{{min}}~{{max}}桁である必要があります。", - "discord_integration/discord_id_numeric": "Discord サーバーIDが数字であることを確認してください(数字のみ)。", - "discord_integration/discord_integration_not_setup": "Discord統合が設定されていません", - "discord_integration/discord_invite_info": "Nameless LinkボットをDiscordサーバーに招待するには、{{inviteLinkStart}}ここ{{inviteLinkEnd}} をクリックしてください。次に、{{command}} コマンドを実行して、ボットをウェブサイトにリンクさせます。また、{{selfHostLinkStart}}自分でボットをホストすることもできます{{selfHostLinkEnd}}。", - "discord_integration/discord_role_id": "Discord ロールID", - "discord_integration/discord_role_id_length": "Discord ロールIDは{{min}}~{{max}}桁である必要があります。", - "discord_integration/discord_role_id_numeric": "Discord ロールIDは数値でなければなりません。", - "discord_integration/discord_usernames_updated": "Discordユーザー名の更新に成功", - "discord_integration/discord_widget_theme": "Discord ウィジェットテーマ", - "discord_integration/enable_discord_integration": "Discordの連携を有効にしますか?", - "discord_integration/discord_id_required": "Discord サーバーIDを入力してください。", - "discord_integration/discord_settings_updated": "Discordの設定が正常に更新されました", - "discord_integration/discord_widget_error": "Discordウィジェットを取得する際にエラーが発生しました: {{error}}", - "discord_integration/discord_widget_disabled": "指定されたDiscordサーバーでウィジェットが無効になっています。Discordのサーバー設定の'ウィジェット'タブを開き、Discordのウィジェットが有効になっているか、IDが正しいかを確認してください。", - "discord_integration/discord_bot": "Discordボット", - "discord_integration/link_method": "リンク方法" -} diff --git a/modules/Discord Integration/language/nl_NL.json b/modules/Discord Integration/language/nl_NL.json deleted file mode 100644 index 4fe9f8284b..0000000000 --- a/modules/Discord Integration/language/nl_NL.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "discord_integration/discord": "Discord", - "discord_integration/discord_id_help": "Voor informatie over waar je de Discord IDs kunt vinden, lees alsjeblieft {{linkStart}}dit.{{linkEnd}}", - "discord_integration/discord_id_length": "Zorg ervoor dat je Discord server ID tussen {{min}} en {{max}} tekens lang is.", - "discord_integration/discord_id_numeric": "Zorg ervoor dat uw Discord server ID numeriek is (alleen cijfers).", - "discord_integration/discord_role_id": "Discord Rol ID", - "discord_integration/discord_role_id_length": "Discord Rol ID moet tussen {{min}} en {{max}} tekens lang zijn.", - "discord_integration/discord_role_id_numeric": "Discord Rol ID moet numeriek zijn.", - "discord_integration/discord_widget_theme": "Discord Widget Thema", - "discord_integration/enable_discord_integration": "Discordintegratie inschakelen?", - "discord_integration/discord_bot_must_be_setup": "Kan Discord integratie niet aanzetten tot je de bot hebt ingesteld. Voor meer informatie, klik {{linkStart}}hier{{linkEnd}}.", - "discord_integration/discord_bot_setup": "Bot ingesteld?", - "discord_integration/discord_guild_id": "Discord Server ID", - "discord_integration/discord_id_confirm": "Voer gelieve het commando \"/verify token:{{token}}\" uit in Discord om je Discord account te koppelen.", - "discord_integration/discord_integration_not_setup": "Discord integratie is niet ingesteld", - "discord_integration/discord_invite_info": "Om de Nameless Link bot uit te nodigen voor je Dsicord server, klik {{inviteLinkStart}}hier{{inviteLinkEnd}}. Daarna, voer het {{command}} commando uit om de bot met je website te koppelen. Als alternatief kun je de bot {{selfHostLinkStart}}zelf hosten{{selfHostLinkEnd}}.", - "discord_integration/discord_settings_updated": "Discord instellingen succesvol aangepast", - "discord_integration/discord_usernames_updated": "Discord gebruikersnamen succesvol aangepast", - "discord_integration/discord_widget_disabled": "De widget is uitgeschakeld voor de ingestelde Discord server. Ga alstublieft naar het \"Widget\" tabblad in je Discord server instellingen, en verzeker dat de Discord widget aan staat en dat de ID correct is.", - "discord_integration/discord_widget_error": "Er is een fout opgetreden tijdens het ophalen van de Discord widget: {{error}}.", - "discord_integration/discord_id_required": "Vul alsjeblieft je Discord server ID in.", - "discord_integration/link_method": "Koppelmethode", - "discord_integration/discord_bot": "Discordbot" -} diff --git a/modules/Discord Integration/language/no_NO.json b/modules/Discord Integration/language/no_NO.json deleted file mode 100644 index fa0300363f..0000000000 --- a/modules/Discord Integration/language/no_NO.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "discord_integration/discord": "Discord", - "discord_integration/discord_bot_must_be_setup": "Kan ikke aktivere Discord-integrering før du har satt opp boten. For informasjon, vennligst {{linkStart}}klikk her{{linkEnd}}.", - "discord_integration/discord_bot_setup": "Bot-oppsett", - "discord_integration/discord_guild_id": "Discord Server-ID", - "discord_integration/discord_id_help": "For mer informasjon om hvordan man finner Discord-bruker-ID, vennligst les {{linkStart}}denne artikkelen.{{linkEnd}}", - "discord_integration/discord_id_length": "Pass på at Discord-ID-en inneholder 18 tegn.", - "discord_integration/discord_id_numeric": "Pass på at Discord-ID-en er numerisk (kun tall).", - "discord_integration/discord_integration_not_setup": "Discord-integrering er ikke satt opp", - "discord_integration/discord_role_id": "Discord rolle-ID", - "discord_integration/discord_role_id_length": "Discord rolle-ID må inneholde 18 tegn.", - "discord_integration/discord_role_id_numeric": "Discord rolle-ID må være numerisk", - "discord_integration/discord_settings_updated": "Discord-innstillinger har blitt oppdatert", - "discord_integration/discord_usernames_updated": "Discord-brukernavn har blitt oppdatert", - "discord_integration/discord_widget_theme": "Discord Widget-Tema", - "discord_integration/enable_discord_integration": "Aktiver Discord-integrering?" -} diff --git a/modules/Discord Integration/language/pl_PL.json b/modules/Discord Integration/language/pl_PL.json deleted file mode 100644 index ee2a0f65f1..0000000000 --- a/modules/Discord Integration/language/pl_PL.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "discord_integration/discord": "Discord", - "discord_integration/discord_bot_must_be_setup": "Nie można włączyć integracji Discord dopóki dobrze jej nie skonfigurujesz, zobacz {{linkStart}}ten artykuł{{linkEnd}}.", - "discord_integration/discord_bot_setup": "Konfiguracja Bota", - "discord_integration/discord_guild_id": "ID Serwera Discord", - "discord_integration/discord_id_length": "Sprawdź czy twoje ID Serwera Discord ma 18 znaków.", - "discord_integration/discord_id_numeric": "Sprawdź czy twoje ID Discord ma odpowiednią ilość znaków (Tylko numery).", - "discord_integration/discord_integration_not_setup": "Integracja Discord nie jest włączona", - "discord_integration/discord_role_id": "ID Roli Discord", - "discord_integration/discord_role_id_length": "ID Roli musi mieć 18 znaków.", - "discord_integration/discord_role_id_numeric": "ID Roli musi być liczbą.", - "discord_integration/discord_settings_updated": "Discord settings updated successfully", - "discord_integration/discord_usernames_updated": "Discord usernames updated successfully", - "discord_integration/discord_widget_theme": "Styl widgetu Discord", - "discord_integration/enable_discord_integration": "Włączyć Integrację Discord?" -} diff --git a/modules/Discord Integration/language/pt_BR.json b/modules/Discord Integration/language/pt_BR.json deleted file mode 100644 index c110cec9ad..0000000000 --- a/modules/Discord Integration/language/pt_BR.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "discord_integration/discord": "Discord", - "discord_integration/discord_bot_must_be_setup": "Não é possível ativar a integração do Discord até que você configure o bot. Para informações, por favor {{linkStart}}clique aqui{{linkEnd}}.", - "discord_integration/discord_bot_setup": "Configuração do bot", - "discord_integration/discord_guild_id": "ID do Servidor do Discord", - "discord_integration/discord_id_confirm": "Por favor, execute o comando \"/verify {{token}}\" no Discord para terminar de vincular sua conta Discord.", - "discord_integration/discord_id_help": "Para obter informações sobre onde encontrar as IDs do Discord, por favor, leia {{linkStart}}isso.{{linkEnd}}", - "discord_integration/discord_id_length": "Certifique-se de que seu ID do Discord tenha 18 caracteres.", - "discord_integration/discord_id_numeric": "Certifique-se de que o seu ID Discord seja numérico (apenas números).", - "discord_integration/discord_integration_not_setup": "A integração do Discord não está configurada.", - "discord_integration/discord_invite_info": "Para convidar o bot Nameless Link para seu servidor Discord, clique {{inviteLinkStart}}aqui{{inviteLinkEnd}}. Então, envie uma mensagem privada para o bot com o comando /apiurl para vincular o bot ao seu site. Alternativamente, você pode {{selfHostLinkStart}}hospedar o bot você mesmo{{selfHostLinkEnd}}.", - "discord_integration/discord_role_id": "ID do Cargo do Discord", - "discord_integration/discord_role_id_length": "O ID do cargo do Discord deve ter 18 dígitos.", - "discord_integration/discord_role_id_numeric": "O ID do cargo do Discord deve ser numérico.", - "discord_integration/discord_settings_updated": "Configurações do Discord atualizadas com sucesso.", - "discord_integration/discord_usernames_updated": "Nomes de usuários do Discord atualizados com sucesso.", - "discord_integration/discord_widget_disabled": "O widget está desativado para o servidor Discord especificado. Por favor, vá para a guia \"Widget\" em suas configurações do servidor Discord, e certifique-se de que o widget Discord esteja habilitado e que a identificação esteja correta.", - "discord_integration/discord_widget_theme": "Tema do Widget do Discord", - "discord_integration/enable_discord_integration": "Ativar integração com o Discord?" -} diff --git a/modules/Discord Integration/language/ru_RU.json b/modules/Discord Integration/language/ru_RU.json deleted file mode 100644 index 034af307f9..0000000000 --- a/modules/Discord Integration/language/ru_RU.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "discord_integration/discord": "Discord", - "discord_integration/discord_bot_must_be_setup": "Нельзя интегрировать сайт, пока вы не подключите бота. Информация {{linkStart}}здесь{{linkEnd}}.", - "discord_integration/discord_bot_setup": "Бот настроен?", - "discord_integration/discord_guild_id": "Discord Сервер ID", - "discord_integration/discord_id_confirm": "Введите команду \"/verify token:{{token}}\" в Discord для завершения привязки.", - "discord_integration/discord_id_help": "Чтобы найти ваш ID сервера в Discord, прочитайте это {{linkStart}}руководство{{linkEnd}}", - "discord_integration/discord_id_length": "Убедитесь что ваш Discord Сервер ID между {{min}} и {{max}} символами.", - "discord_integration/discord_id_numeric": "Убедитесь что ваш Discord Сервер ID состоит из цифр.", - "discord_integration/discord_integration_not_setup": "Discord не настроен", - "discord_integration/discord_invite_info": "Чтобы пригласить бота Nameless Link на ваш Discord сервер, нажмите {{inviteLinkStart}}сюда{{inviteLinkEnd}}. Затем, отправьте {{command}} комманду для подключения бота с вашем сайтом. Как вариант, вы можете разместить {{selfHostLinkStart}}свою версию бота{{selfHostLinkEnd}}.", - "discord_integration/discord_role_id": "ID роли", - "discord_integration/discord_role_id_length": "ID роли имеет длину между {{min}} и {{max}} символами.", - "discord_integration/discord_role_id_numeric": "ID роли имеет только цифры.", - "discord_integration/discord_settings_updated": "Настройки в Discord обновлены", - "discord_integration/discord_usernames_updated": "Никнеймы в Discord обновлены", - "discord_integration/discord_widget_disabled": "Поддержка виджетов отключена на данном Discord сервере. Перейдите в раздел 'Виджет' в настройках вашего сервера и активируйте его.", - "discord_integration/discord_widget_theme": "Тема для виджета", - "discord_integration/enable_discord_integration": "Включить интеграцию?", - "discord_integration/discord_widget_error": "Произошла ошибка при Discord виджете: {{error}}.", - "discord_integration/discord_id_required": "Введите ваш Дискорд Сервер ID.", - "discord_integration/discord_bot": "Discord бот", - "discord_integration/link_method": "Метод подключения" -} diff --git a/modules/Discord Integration/language/sk_SK.json b/modules/Discord Integration/language/sk_SK.json deleted file mode 100644 index e74b039ccc..0000000000 --- a/modules/Discord Integration/language/sk_SK.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "discord_integration/discord": "Discord", - "discord_integration/discord_bot_must_be_setup": "Discord integráciu nie je možné povoliť, kým nenastavíte bota. Pre informáciu prosím {{linkStart}}kliknite tu{{linkEnd}}.", - "discord_integration/discord_bot_setup": "Bot nastavený?", - "discord_integration/discord_guild_id": "ID Discord serveru", - "discord_integration/discord_id_confirm": "Spustite na Discorde príkaz \"/verify token:{{token}}\" pre dokončenie prepojenia vášho Discord účtu.", - "discord_integration/discord_id_help": "Informácie o tom, kde nájdete Discord ID, si prečítajte {{linkStart}}toto.{{linkEnd}}", - "discord_integration/discord_id_length": "Uistite sa, že vaše ID Discord servera má {{min}} až {{max}} číslic.", - "discord_integration/discord_id_numeric": "Uistite sa, že vaše ID discord servera je číselné (iba čísla).", - "discord_integration/discord_integration_not_setup": "Integrácia Discordu nie je nastavená", - "discord_integration/discord_invite_info": "Ak chcete pozvať Nameless Link bota na svoj Discord server, kliknite {{inviteLinkStart}}sem{{inviteLinkEnd}}. Potom spustite príkaz {{command}} na prepojenie bota s vašou webstránkou. Prípadne môžete {{selfHostLinkStart}}hostiť robota sami{{selfHostLinkEnd}}.", - "discord_integration/discord_role_id": "ID Discord role", - "discord_integration/discord_role_id_length": "ID Discord roly musí mať dĺžku {{min}} až {{max}} číslic.", - "discord_integration/discord_role_id_numeric": "Discord Role ID musí byť číselné.", - "discord_integration/discord_settings_updated": "Nastavenia Discordu sa úspešne aktualizovali", - "discord_integration/discord_usernames_updated": "Discord užívateľské mená boli úspešne aktualizované", - "discord_integration/discord_widget_disabled": "Widget je pre zadaný Discord server zakázaný. Prejdite na kartu 'Widgety' v nastaveniach Discord servera a uistite sa, že je povolený Discord widget a či je ID správne.", - "discord_integration/discord_widget_theme": "Téma Discord widgetu", - "discord_integration/enable_discord_integration": "Povoliť Discord integráciu?", - "discord_integration/discord_widget_error": "Pri získavaní widgetu Discord sa vyskytla chyba: {{error}}.", - "discord_integration/discord_id_required": "Zadajte ID vášho discord servera.", - "discord_integration/discord_bot": "Discord Bot", - "discord_integration/link_method": "Metóda prepojenia" -} diff --git a/modules/Discord Integration/language/sl_SI.json b/modules/Discord Integration/language/sl_SI.json deleted file mode 100644 index 9e26dfeeb6..0000000000 --- a/modules/Discord Integration/language/sl_SI.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/modules/Discord Integration/language/sq_AL.json b/modules/Discord Integration/language/sq_AL.json deleted file mode 100644 index 0967ef424b..0000000000 --- a/modules/Discord Integration/language/sq_AL.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/modules/Discord Integration/language/sr_RS.json b/modules/Discord Integration/language/sr_RS.json deleted file mode 100644 index 0967ef424b..0000000000 --- a/modules/Discord Integration/language/sr_RS.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/modules/Discord Integration/language/sv_SE.json b/modules/Discord Integration/language/sv_SE.json deleted file mode 100644 index d150c5adef..0000000000 --- a/modules/Discord Integration/language/sv_SE.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "discord_integration/discord": "Discord", - "discord_integration/discord_bot_must_be_setup": "Kan inte aktivera Discord Integrationen tills att du har ställt in botten. För mer information, vänligen {{linkStart}}klicka här{{linkEnd}}.", - "discord_integration/discord_id_confirm": "Vänligen kör \"/verify {{token}}\" kommandot på Discord för att slutföra länkningen till ditt Discord-konto.", - "discord_integration/discord_id_help": "För information om var du hittar Discord ID's, vänligen läs {{linkStart}}detta.{{linkEnd}}", - "discord_integration/discord_id_length": "Vänligen se till att ditt Discord ID is 18 bokstäver långt.", - "discord_integration/discord_id_numeric": "Vängligen se till att ditt Discord ID är numeriskt. (Endast Nummer).", - "discord_integration/discord_integration_not_setup": "Discord Integrationen är inte inställd.", - "discord_integration/discord_role_id": "Discord Roll ID", - "discord_integration/discord_role_id_length": "Discord-roll-ID måste vara 18 siffror långt.", - "discord_integration/discord_role_id_numeric": "Discord-roll-ID måste vara numeriskt.", - "discord_integration/discord_settings_updated": "Discord-inställningar uppdaterades framgångsrikt", - "discord_integration/discord_usernames_updated": "Discord-användarnamn uppdaterades framgångsrikt", - "discord_integration/enable_discord_integration": "Aktivera Discord-integration?" -} diff --git a/modules/Discord Integration/language/th_TH.json b/modules/Discord Integration/language/th_TH.json deleted file mode 100644 index 8220b645d1..0000000000 --- a/modules/Discord Integration/language/th_TH.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "discord_integration/discord": "Discord", - "discord_integration/discord_bot_must_be_setup": "ไม่สามารถเปิดใช้งานการรวม Discord กันจนกว่าคุณจะตั้งค่าบอท สําหรับข้อมูลเพิ่มเติมโปรด {{linkStart}}click here{{linkEnd}}.", - "discord_integration/discord_bot_setup": "เซ็ตค่าบอทหรือไม่", - "discord_integration/discord_guild_id": "ID เซิฟเวอร์ Discord", - "discord_integration/discord_id_confirm": "โปรดเรียกใช้คำสั่ง \"/verify {{token}}\" ใน Discord เพื่อสิ้นสุดการเชื่อมโยงบัญชี Discord ของคุณ.", - "discord_integration/discord_id_help": "สำหรับข้อมูลเกี่ยวกับตำแหน่งของ Discord ID โปรดอ่าน {{linkStart}}this.{{linkEnd}}", - "discord_integration/discord_id_length": "โปรดตรวจสอบให้แน่ใจว่า ID Discord ของคุณมีความยาว 18 ตัวอักษร", - "discord_integration/discord_id_numeric": "โปรดตรวจสอบให้แน่ใจว่า ID Discord ของคุณเป็นตัวเลข (เฉพาะตัวเลข)", - "discord_integration/discord_integration_not_setup": "ไม่ได้ตั้งค่าการรวม Discord", - "discord_integration/discord_invite_info": "เมื่อต้องการเชิญบอทลิงก์นิรนามไปยังเซิร์ฟเวอร์ Discord ของคุณ ให้คลิก {{inviteLinkStart}}here{{inviteLinkEnd}}. จากนั้นรันคำสั่ง /apiurl เพื่อเชื่อมโยงบอทกับเว็บไซต์ของคุณ หรือคุณสามารถใช้ {{selfHostLinkStart}}host the bot yourself{{selfHostLinkEnd}}.", - "discord_integration/discord_role_id": "Discord Role ID", - "discord_integration/discord_role_id_length": "Discord Role ID ต้องมีความยาว 18 หลัก", - "discord_integration/discord_role_id_numeric": "Discord Role ID ต้องเป็นตัวเลข", - "discord_integration/discord_settings_updated": "อัปเดตการตั้งค่า Discord เรียบร้อยแล้ว", - "discord_integration/discord_usernames_updated": "อัปเดตชื่อผู้ใช้ Discord สำเร็จแล้ว", - "discord_integration/discord_widget_disabled": "วิดเจ็ตถูกปิดใช้งานสำหรับเซิร์ฟเวอร์ Discord ที่ระบุ โปรดไปที่แท็บ ในการตั้งค่าเซิร์ฟเวอร์ Discord ของคุณ และตรวจสอบให้แน่ใจว่าวิดเจ็ต Discord เปิดใช้งานอยู่และ ID นั้นถูกต้อง", - "discord_integration/discord_widget_theme": "ธีมวิดเจ็ต Discord", - "discord_integration/enable_discord_integration": "เปิดใช้งานการรวม Discord หรือไม่" -} diff --git a/modules/Discord Integration/language/tr_TR.json b/modules/Discord Integration/language/tr_TR.json deleted file mode 100644 index 388ddc43e6..0000000000 --- a/modules/Discord Integration/language/tr_TR.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "discord_integration/discord_id_length": "Lütfen Discord Sunucu ID'nizin {{min}} ile {{max}} hane uzunluğunda olduğundan emin olun.", - "discord_integration/discord_role_id_length": "Discord Rol ID'si, {{min}} ile {{max}} basamak uzunluğunda olmalıdır.", - "discord_integration/discord_bot": "Discord Botu", - "discord_integration/discord_bot_setup": "Bot kuruldu mu?", - "discord_integration/discord_bot_must_be_setup": "Botu kurana kadar Discord Entegrasyonu etkinleştirilemez. Bilgi için lütfen {{linkStart}}buraya{{linkEnd}} tıklayın.", - "discord_integration/discord_guild_id": "Discord Sunucu ID", - "discord_integration/discord_id_confirm": "Discord hesabınızı bağlamayı tamamlamak için lütfen Discord'da \"/verify token:{{token}}\" komutunu çalıştırın.", - "discord_integration/discord_id_help": "Discord ID'i nerede bulacağınız hakkında bilgi için lütfen {{linkStart}}bunu{{linkEnd}} okuyun", - "discord_integration/discord_role_id_numeric": "Discord Rol ID'si sayısal olmalıdır.", - "discord_integration/discord": "Discord", - "discord_integration/discord_id_numeric": "Lütfen Discord Sunucu ID'nizin sayısal olduğundan emin olun (yalnızca sayılar).", - "discord_integration/discord_role_id": "Discord Rol ID", - "discord_integration/discord_settings_updated": "Discord ayarları başarıyla güncellendi", - "discord_integration/discord_usernames_updated": "Discord kullanıcı adı başarıyla güncellendi", - "discord_integration/discord_widget_error": "Discord widget'ı alınırken bir hata oluştu: {{error}}.", - "discord_integration/discord_widget_theme": "Discord Widget Teması", - "discord_integration/enable_discord_integration": "Discord entegrasyonu etkinleştirilsin mi?", - "discord_integration/discord_widget_disabled": "Widget, belirtilen Discord sunucusu için devre dışı bırakıldı. Lütfen Discord sunucu ayarlarınızdaki 'Widget' sekmesine gidin ve Discord widget'ının etkinleştirildiğinden ve id'sinin olduğundan emin olun.", - "discord_integration/link_method": "Bağlantı yöntemi" -} diff --git a/modules/Discord Integration/language/uk_UA.json b/modules/Discord Integration/language/uk_UA.json deleted file mode 100644 index ba4ee9bb5f..0000000000 --- a/modules/Discord Integration/language/uk_UA.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "discord_integration/discord_bot_setup": "Бот налаштований?", - "discord_integration/discord_guild_id": "ID Discord серверу", - "discord_integration/discord_id_confirm": "Будь ласка, виконайте команду \"/verify token:{{token}}\" в Discord, щоб завершити прив'язку вашого облікового запису в Discord.", - "discord_integration/discord_id_help": "Щоб дізнатись як знайти ID Вашого серверу в Discord, прочитайте {{linkStart}}цей{{linkEnd}} посібник", - "discord_integration/discord_id_length": "Переконайтеся, що ID Вашого Discord серверу в проміжку від {{min}} до {{max}}.", - "discord_integration/discord_id_numeric": "Переконайтеся, що ID Вашого Discord серверу складається з цифр.", - "discord_integration/discord_id_required": "Введіть ID Вашого Discord серверу.", - "discord_integration/discord_integration_not_setup": "Discord інтеграція не налаштована", - "discord_integration/discord_role_id": "ID ролі", - "discord_integration/discord_role_id_length": "ID ролі має довжину між {{min}} і {{max}} символами.", - "discord_integration/discord_role_id_numeric": "ID ролі має тільки цифри.", - "discord_integration/discord_settings_updated": "Налаштування в Discord оновлені", - "discord_integration/discord_usernames_updated": "Нікнейми в Discord оновлені", - "discord_integration/discord_widget_disabled": "Підтримка віджетів відключена на цьому сервері Discord. Перейдіть у розділ «Віджет» у налаштуваннях вашого сервера та активуйте його.", - "discord_integration/discord_widget_error": "Відбулася помилка при Discord віджеті: {{error}}.", - "discord_integration/discord_widget_theme": "Тема для віджета", - "discord_integration/enable_discord_integration": "Включити інтеграцію?", - "discord_integration/discord": "Discord", - "discord_integration/discord_bot_must_be_setup": "Не можна інтегрувати сайт, доки ви не підключите бота. Інформація {{linkStart}} тут {{linkEnd}}.", - "discord_integration/discord_invite_info": "Для того, щоб запросити Nameless Link бота на Ваш Discord сервер, натисніть {{inviteLinkStart}}тут{{inviteLinkEnd}} . Потім запустіть команду {{command}}, щоб прив'язати бота до Вашого веб-сайту. З іншого боку, ви можете {{selfHostLinkStart}}розмістити бота самостійно{{selfHostLinkEnd}}.", - "discord_integration/discord_bot": "Бот розбрату", - "discord_integration/link_method": "Метод посилання" -} diff --git a/modules/Discord Integration/language/vi_VN.json b/modules/Discord Integration/language/vi_VN.json deleted file mode 100644 index 7a0191f322..0000000000 --- a/modules/Discord Integration/language/vi_VN.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "discord_integration/discord": "Discord", - "discord_integration/discord_bot_setup": "Bot được thiết lập chưa?", - "discord_integration/discord_id_help": "Để biết thông tin về nơi tìm Discord ID, vui lòng đọc {{linkStart}}cái này.{{linkEnd}}", - "discord_integration/discord_id_numeric": "Vui lòng đảm bảo ID máy chủ Discord của bạn là số.", - "discord_integration/discord_id_required": "Vui lòng nhập ID máy chủ Discord của bạn.", - "discord_integration/discord_role_id": "Discord Role ID", - "discord_integration/discord_role_id_length": "Discord Role ID phải dài từ {{min}} đến {{max}} chữ số.", - "discord_integration/discord_role_id_numeric": "Discord Role ID phải là số.", - "discord_integration/discord_settings_updated": "Đã cập nhật thành công cài đặt Discord", - "discord_integration/discord_usernames_updated": "Đã cập nhật thành công tên người dùng Discord", - "discord_integration/discord_widget_error": "Đã xảy ra lỗi khi tải tiện ích Discord: {{error}}.", - "discord_integration/discord_widget_theme": "Chủ đề Widget Discord", - "discord_integration/enable_discord_integration": "Bật tích hợp Discord?", - "discord_integration/discord_bot_must_be_setup": "Không thể bật Tích hợp Discord cho đến khi bạn thiết lập xong bot. Để biết thông tin, vui lòng nhấp vào {{linkStart}}đây{{linkEnd}}.", - "discord_integration/discord_guild_id": "ID máy chủ Discord", - "discord_integration/discord_id_confirm": "Vui lòng chạy lệnh \"/ verify {{token}}\" trong Discord để hoàn tất việc liên kết tài khoản Discord của bạn.", - "discord_integration/discord_id_length": "Hãy đảm bảo ID máy chủ Discord của bạn dài từ {{min}} đến {{max}} chữ số.", - "discord_integration/discord_integration_not_setup": "Tích hợp Discord không được thiết lập", - "discord_integration/discord_invite_info": "Để mời bot Nameless Link vào máy chủ Discord của bạn, hãy nhấp vào {{inviteLinkStart}}vào đây{{inviteLinkEnd}}. Sau đó, chạy lệnh {{command}} để liên kết bot với trang web của bạn. Ngoài ra, bạn có thể {{selfHostLinkStart}}tự mình host bot{{selfHostLinkEnd}}.", - "discord_integration/discord_widget_disabled": "Tiện ích con bị tắt đối với máy chủ Discord được chỉ định. Vui lòng chuyển đến tab 'Widget' trong cài đặt máy chủ Discord của bạn và đảm bảo rằng widget Discord được bật và ID chính xác." -} diff --git a/modules/Discord Integration/language/zh_CN.json b/modules/Discord Integration/language/zh_CN.json deleted file mode 100644 index eeb9a9fef1..0000000000 --- a/modules/Discord Integration/language/zh_CN.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "discord_integration/discord": "Discord", - "discord_integration/discord_bot_must_be_setup": "您必须配置 Discord 机器人后才能启用 Discord 集成。您可 {{linkStart}}点击此处{{linkEnd}} 了解详情。", - "discord_integration/discord_bot_setup": "机器人设置?", - "discord_integration/discord_guild_id": "Discord 服务器 ID", - "discord_integration/discord_id_help": "您可阅读 {{linkStart}}此文章{{linkEnd}} 来了解如何获得您的用户 ID。", - "discord_integration/discord_id_length": "请确保您的不和谐服务器 ID 介于 {{最小}} 和 {{最大}} 数字之间。", - "discord_integration/discord_id_numeric": "请确保您的 Discord ID 只包含数字。", - "discord_integration/discord_integration_not_setup": "Discord 集成尚未配置", - "discord_integration/discord_role_id": "Discord 身份组 ID", - "discord_integration/discord_role_id_length": "Discord 身份组 ID 的位数必须在 {{min}}到{{max}} 之间。", - "discord_integration/discord_role_id_numeric": "Discord 身份组 ID 必须为数字。", - "discord_integration/discord_settings_updated": "Discord 设置更新成功", - "discord_integration/discord_usernames_updated": "Discord 用户名更新成功", - "discord_integration/discord_widget_theme": "Discord Widget 主题", - "discord_integration/enable_discord_integration": "启用 Discord 集成?", - "discord_integration/discord_id_confirm": "请在 Discord 中运行 \"/verify {{token}}\" 指令来链接您的 Discord 账号。", - "discord_integration/discord_invite_info": "{{inviteLinkStart}}点击这里{{inviteLinkEnd}} 即可邀请 Nameless Link 机器人加入您的 Discord服务器。随后,您可运行 {{command}} 命令来将机器人链接到您的网站上。您也可以 {{selfHostLinkStart}}了解如何自建机器人{{selfHostLinkEnd}}。", - "discord_integration/discord_widget_disabled": "本组件在本 Discord 服务器上被禁用了。请在您的 Discord 服务器设置中前往 \"组件\" 标签页,并确保 Discord 组件已经启用,并且 ID 也是正确的。", - "discord_integration/discord_widget_error": "获取 Discord 连接时出错: {{error}}。", - "discord_integration/discord_id_required": "请输入您的Discord ID。", - "discord_integration/discord_bot": "Discord 机器人" -} diff --git a/modules/Discord Integration/module.php b/modules/Discord Integration/module.php deleted file mode 100644 index 31639b5dbb..0000000000 --- a/modules/Discord Integration/module.php +++ /dev/null @@ -1,86 +0,0 @@ -_language = $language; - - $name = 'Discord Integration'; - $author = 'Aberdeener'; - $module_version = '2.1.2'; - $nameless_version = '2.1.2'; - - parent::__construct($this, $name, $author, $module_version, $nameless_version); - - $bot_url = Settings::get('discord_bot_url'); - if ($bot_url === null) { - $bot_url = ''; - } - define('BOT_URL', $bot_url); - - $bot_username = Settings::get('discord_bot_username'); - if ($bot_username === null) { - $bot_username = ''; - } - define('BOT_USERNAME', $bot_username); - - $pages->add($this->getName(), '/panel/discord', 'pages/panel/discord.php'); - - $endpoints->loadEndpoints(ROOT_PATH . '/modules/Discord Integration/includes/endpoints'); - - // -- Events - EventHandler::registerEvent(DiscordWebhookFormatterEvent::class); - - GroupSyncManager::getInstance()->registerInjector(new DiscordGroupSyncInjector); - - Integrations::getInstance()->registerIntegration(new DiscordIntegration($language)); - } - - public function onInstall() { - } - - public function onUninstall() { - } - - public function onDisable() { - } - - public function onEnable() { - } - - public function onPageLoad(User $user, Pages $pages, Cache $cache, Smarty $smarty, $navs, Widgets $widgets, ?TemplateBase $template) { - PermissionHandler::registerPermissions($this->getName(), [ - 'admincp.discord' => $this->_language->get('admin', 'integrations') . ' » ' . Discord::getLanguageTerm('discord'), - ]); - - if ($pages->getActivePage()['widgets'] || (defined('PANEL_PAGE') && str_contains(PANEL_PAGE, 'widget'))) { - $widgets->add(new DiscordWidget($cache, $smarty)); - } - - if (!defined('FRONT_END')) { - $cache->setCache('panel_sidebar'); - - if ($user->hasPermission('admincp.discord')) { - if (!$cache->isCached('discord_icon')) { - $icon = ''; - $cache->store('discord_icon', $icon); - } else { - $icon = $cache->retrieve('discord_icon'); - } - - $navs[2]->addItemToDropdown('integrations', 'discord', Discord::getLanguageTerm('discord'), URL::build('/panel/discord'), 'top', null, $icon, 1); - } - } - } - - public function getDebugInfo(): array { - return [ - 'guild_id' => Discord::getGuildId(), - 'roles' => Discord::getRoles(), - 'bot_setup' => Discord::isBotSetup(), - 'bot_url' => BOT_URL, - ]; - } -} diff --git a/modules/Discord Integration/pages/panel/discord.php b/modules/Discord Integration/pages/panel/discord.php deleted file mode 100644 index 28645e3b92..0000000000 --- a/modules/Discord Integration/pages/panel/discord.php +++ /dev/null @@ -1,146 +0,0 @@ -handlePanelPageLoad('admincp.discord')) { - require_once(ROOT_PATH . '/403.php'); - die(); -} - -const PAGE = 'panel'; -const PARENT_PAGE = 'integrations'; -const PANEL_PAGE = 'discord'; -$page_title = Discord::getLanguageTerm('discord'); -require_once(ROOT_PATH . '/core/templates/backend_init.php'); - -if (Input::exists()) { - $errors = []; - - if (Token::check()) { - if (isset($_POST['discord_guild_id'])) { - $validation = Validate::check($_POST, [ - 'discord_guild_id' => [ - Validate::MIN => 18, - Validate::MAX => 20, - Validate::NUMERIC => true, - Validate::REQUIRED => true, - ] - ])->messages([ - 'discord_guild_id' => [ - Validate::MIN => Discord::getLanguageTerm('discord_id_length', ['min' => 18, 'max' => 20]), - Validate::MAX => Discord::getLanguageTerm('discord_id_length', ['min' => 18, 'max' => 20]), - Validate::NUMERIC => Discord::getLanguageTerm('discord_id_numeric'), - Validate::REQUIRED => Discord::getLanguageTerm('discord_id_required'), - ] - ]); - - if ($validation->passed()) { - Settings::set('discord', Input::get('discord_guild_id')); - - $success = Discord::getLanguageTerm('discord_settings_updated'); - - } else { - $errors = $validation->errors(); - } - } else { - // Valid token - // Either enable or disable Discord integration - if ($_POST['enable_discord'] === '1') { - if (BOT_URL == '' || BOT_USERNAME == '' || Discord::getGuildId() == '') { - $errors[] = Discord::getLanguageTerm('discord_bot_must_be_setup', [ - 'linkStart' => '', - 'linkEnd' => '', - ]); - Settings::set('discord_integration', '0'); - } else { - Settings::set('discord_integration', '1'); - } - } else { - Settings::set('discord_integration', '0'); - } - } - - if (!count($errors)) { - Session::flash('discord_success', Discord::getLanguageTerm('discord_settings_updated')); - Redirect::to(URL::build('/panel/discord')); - } - } else { - // Invalid token - $errors[] = [$language->get('general', 'invalid_token')]; - } -} - -// Load modules + template -Module::loadPage($user, $pages, $cache, $smarty, [$navigation, $cc_nav, $staffcp_nav], $widgets, $template); - -if (Session::exists('discord_success')) { - $success = Session::flash('discord'); -} - -if (isset($success)) { - $smarty->assign([ - 'SUCCESS' => $success, - 'SUCCESS_TITLE' => $language->get('general', 'success') - ]); -} - -if (isset($errors) && count($errors)) { - $smarty->assign([ - 'ERRORS' => $errors, - 'ERRORS_TITLE' => $language->get('general', 'error') - ]); -} - -if (Session::exists('discord_error')) { - $smarty->assign([ - 'ERRORS' => [Session::flash('discord_error')], - 'ERRORS_TITLE' => $language->get('general', 'error') - ]); -} - -// TODO: Add a check to see if the bot is online using `/status` endpoint Discord::botRequest('/status'); - -$smarty->assign([ - 'PARENT_PAGE' => PARENT_PAGE, - 'DASHBOARD' => $language->get('admin', 'dashboard'), - 'INTEGRATIONS' => $language->get('admin', 'integrations'), - 'DISCORD' => Discord::getLanguageTerm('discord'), - 'PAGE' => PANEL_PAGE, - 'INFO' => $language->get('general', 'info'), - 'TOKEN' => Token::get(), - 'SUBMIT' => $language->get('general', 'submit'), - 'ENABLE_DISCORD_INTEGRATION' => Discord::getLanguageTerm('enable_discord_integration'), - 'DISCORD_ENABLED' => Discord::isBotSetup(), - 'INVITE_LINK' => Discord::getLanguageTerm('discord_invite_info', [ - 'inviteLinkStart' => '', - 'inviteLinkEnd' => '', - 'command' => '/configure link', - 'selfHostLinkStart' => '', - 'selfHostLinkEnd' => '', - ]), - 'GUILD_ID_SET' => (Discord::getGuildId() != ''), - 'BOT_URL_SET' => (BOT_URL != ''), - 'BOT_USERNAME_SET' => (BOT_USERNAME != ''), - 'REQUIREMENTS' => rtrim($language->get('installer', 'requirements'), ':'), - 'BOT_SETUP' => Discord::getLanguageTerm('discord_bot_setup'), - 'DISCORD_GUILD_ID' => Discord::getLanguageTerm('discord_guild_id'), - 'DISCORD_GUILD_ID_VALUE' => Discord::getGuildId(), - 'ID_INFO' => Discord::getLanguageTerm('discord_id_help', [ - 'linkStart' => '', - 'linkEnd' => '', - ]), -]); - -$template->onPageLoad(); - -require(ROOT_PATH . '/core/templates/panel_navbar.php'); - -// Display template -$template->displayTemplate('integrations/discord/discord.tpl', $smarty); diff --git a/modules/Discord Integration/widgets/DiscordWidget.php b/modules/Discord Integration/widgets/DiscordWidget.php deleted file mode 100644 index 8c548d9b8c..0000000000 --- a/modules/Discord Integration/widgets/DiscordWidget.php +++ /dev/null @@ -1,74 +0,0 @@ -_module = 'Discord Integration'; - $this->_name = 'Discord'; - $this->_description = 'Display your Discord channel on your site. Make sure you have entered your Discord widget details in the StaffCP -> Integrations -> Discord tab first!'; - $this->_settings = ROOT_PATH . '/modules/Discord Integration/includes/admin_widgets/discord.php'; - $this->_smarty = $smarty; - - $this->_cache = $cache; - $this->_guild_id = Discord::getGuildId(); - } - - public function initialise(): void { - // Generate HTML code for widget - // If there is no Guild ID set, display error message - if ($this->_guild_id === null) { - $this->_content = Discord::getLanguageTerm('discord_widget_disabled'); - return; - } - - // First, check to see if the Discord server has the widget enabled. - $this->_cache->setCache('social_media'); - if ($this->_cache->isCached('discord_widget_check')) { - $result = $this->_cache->retrieve('discord_widget_check'); - - } else { - $request = HttpClient::get('https://discord.com/api/guilds/' . urlencode($this->_guild_id) . '/widget.json'); - if ($request->hasError()) { - $this->_content = Discord::getLanguageTerm('discord_widget_error', [ - 'error' => $request->getError() - ]); - return; - } - - $result = $request->json(); - - // Cache for 60 seconds - $this->_cache->store('discord_widget_check', $result, 60); - } - - // Check if the widget is disabled. - if (!isset($result->channels) || isset($result->code)) { - // Yes, it is: display message - $this->_content = Discord::getLanguageTerm('discord_widget_disabled'); - - } else { - // No, it isn't: display the widget - // Check cache for theme - $theme = 'dark'; - if ($this->_cache->isCached('discord_widget_theme')) { - $theme = $this->_cache->retrieve('discord_widget_theme'); - } - - $this->_content = '
'; - } - } -} diff --git a/modules/Forum/classes/HighestForumReactionScoresMemberListProvider.php b/modules/Forum/classes/HighestForumReactionScoresMemberListProvider.php index d4856e5fba..8c94d0c6e3 100644 --- a/modules/Forum/classes/HighestForumReactionScoresMemberListProvider.php +++ b/modules/Forum/classes/HighestForumReactionScoresMemberListProvider.php @@ -1,4 +1,5 @@ registerListProvider(new MostPostsMemberListProvider($forum_language)); - MemberListManager::getInstance()->registerListProvider(new HighestForumReactionScoresMemberListProvider($forum_language)); + // if (Util::isModuleEnabled('Members')) { + $memberListManager = Container::getInstance()->get('NamelessMC\Members\MemberListManager'); + $memberListManager->registerListProvider(new MostPostsMemberListProvider($forum_language)); + $memberListManager->registerListProvider(new HighestForumReactionScoresMemberListProvider($forum_language)); - MemberListManager::getInstance()->registerMemberMetadataProvider(function (User $member) use ($forum_language) { + $memberListManager->registerMemberMetadataProvider(function (User $member) use ($forum_language) { return [ $forum_language->get('forum', 'posts_title') => DB::getInstance()->query( @@ -157,7 +159,7 @@ public function __construct(Language $language, Language $forum_language, Pages ]; }); - MemberListManager::getInstance()->registerMemberMetadataProvider(function (User $member) use ($forum_language) { + $memberListManager->registerMemberMetadataProvider(function (User $member) use ($forum_language) { return [ $forum_language->get('forum', 'reaction_score') => DB::getInstance()->query( @@ -167,8 +169,8 @@ public function __construct(Language $language, Language $forum_language, Pages ]; }); - ReactionContextsManager::getInstance()->provideContext(new ForumPostReactionContext($forum_language)); - } + ReactionContextsManager::getInstance()->provideContext(new ForumPostReactionContext($forum_language)); + // } } public function onInstall() { diff --git a/modules/Members/classes/GroupMemberListProvider.php b/modules/Members/classes/GroupMemberListProvider.php deleted file mode 100644 index eea0a6d584..0000000000 --- a/modules/Members/classes/GroupMemberListProvider.php +++ /dev/null @@ -1,17 +0,0 @@ -_group_id = $group_id; - } - - protected function generator(): array { - return [ - "SELECT nl2_users.id AS id, nl2_users.username FROM nl2_users LEFT JOIN nl2_users_groups ON nl2_users.id = nl2_users_groups.user_id WHERE nl2_users_groups.group_id = {$this->_group_id} ORDER BY nl2_users.username", - 'id', - ]; - } -} diff --git a/modules/Members/classes/MemberListManager.php b/modules/Members/classes/MemberListManager.php deleted file mode 100644 index 8c8fa72147..0000000000 --- a/modules/Members/classes/MemberListManager.php +++ /dev/null @@ -1,110 +0,0 @@ -_lists[$provider->getName()] = $provider; - } - - /** - * Register a member metadata provider. - * Member metadata providers are used to add additional information to member lists under each member's name. - * - * @param Closure $provider The member metadata provider to register - */ - public function registerMemberMetadataProvider(Closure $provider): void { - $this->_metadata_providers[] = $provider; - } - - /** - * Return all lists, including disabled ones. - * - * @return MemberListProvider[] All lists - */ - public function allLists(): array { - return $this->_lists; - } - - /** - * Return all enabled lists. - * - * @return MemberListProvider[] All enabled lists - */ - public function allEnabledLists(): array { - $lists = []; - foreach ($this->allLists() as $list) { - if ($list->isEnabled()) { - $lists[] = $list; - } - } - - return $lists; - } - - /** - * Determine whether a list with the given name exists. - * - * @param string $name The name of the list - * @return bool Whether a list with the given name exists - */ - public function listExists(string $name): bool { - return isset($this->_lists[$name]); - } - - /** - * Get a member list provider with the given name, or create an instance of GroupMemberListProvider if the name is a group ID. - * - * @see GroupMemberListProvider - * @param string $name The name of the list, or the group ID if $for_group is true - * @param bool $for_group Whether the name is a group ID - * @return MemberListProvider The member list provider with the given name - */ - public function getList(string $name, bool $for_group = false): MemberListProvider { - if (!$this->listExists($name)) { - if (!$for_group) { - throw new RuntimeException("Member list '$name' does not exist"); - } - - $group_id = (int) $name; - return new GroupMemberListProvider($group_id); - } - - return $this->_lists[$name]; - } - - /** - * Get the metadata for a given user. Pipes the user through all registered metadata providers. - * - * @param User $user The user to get the metadata for - * @return array The metadata for the given user - */ - public function getMemberMetadata(User $user): array { - $metadata = []; - foreach ($this->_metadata_providers as $provider) { - $metadata += $provider($user); - } - - return $metadata; - } -} diff --git a/modules/Members/classes/MemberListProvider.php b/modules/Members/classes/MemberListProvider.php deleted file mode 100644 index f8527fd9af..0000000000 --- a/modules/Members/classes/MemberListProvider.php +++ /dev/null @@ -1,182 +0,0 @@ -_name; - } - - /** - * @return string The friendly name of the member list provider, displayed in navigation and user-facing pages - */ - public function getFriendlyName(): string { - return $this->_friendly_name; - } - - /** - * @return string The name of the module that the member list provider belongs to - */ - public function getModule(): string { - return $this->_module; - } - - /** - * @return string|null The icon to display next to the member list provider's name in the members page navigation sidebar - */ - public function getIcon(): ?string { - return $this->_icon; - } - - /** - * @return bool Whether the member list provider should be displayed on the member list overview page - */ - public function displayOnOverview(): bool { - return $this->_display_on_overview; - } - - /** - * @return string A URL to this specific member list page - */ - public function url(): string { - return URL::build('/members', 'list=' . $this->getName()); - } - - /** - * Determine whether the member list provider is enabled or not. Will automatically enable the member list if it is not already enabled. - * @return bool Whether the member list provider is enabled - */ - public function isEnabled(): bool { - if (isset($this->_enabled)) { - return $this->_enabled; - } - - $enabled = DB::getInstance()->get('member_lists', ['name', $this->getName()])->first()->enabled; - if ($enabled === null) { - DB::getInstance()->insert('member_lists', [ - 'name' => $this->getName(), - 'friendly_name' => $this->getFriendlyName(), - 'module' => $this->getModule(), - 'enabled' => true, - ]); - - return true; - } - - return $this->_enabled = $enabled; - } - - /** - * Get the information needed to generate the member list. - * - * @return array An array containing the SQL query to run, the column name of the user ID, and optionally the - * column name of the "count" value for this list. Count values are used to display the number of posts, likes, etc. - */ - abstract protected function generator(): array; - - /** - * Get an array of members to display on the member list page. - * - * @param bool $overview Whether the member list is being displayed on the overview page. If true, only 5 members will be returned, otherwise 20. - * @param int $page The page number to display, starting at 1 - pages are 20 members long - * @return array An array of members to display on the member list page - */ - public function getMembers(bool $overview, int $page): array { - [$sql, $id_column, $count_column] = $this->generator(); - - $rows = DB::getInstance()->query($sql)->results(); - if (Settings::get('member_list_hide_banned', false, 'Members')) { - $rows = $this->filterBanned($rows, $id_column); - } - - $rows = array_slice($rows, ($page - 1) * 20); - - $list_members = []; - $limit = $overview ? 5 : 20; - - - foreach ($rows as $row) { - if (count($list_members) === $limit) { - break; - } - - $user_id = $row->{$id_column}; - $member = new User($user_id); - - $list_members[] = array_merge( - [ - 'username' => Output::getClean($member->data()->username), - 'avatar_url' => $member->getAvatar(), - 'group_style' => $member->getGroupStyle(), - 'profile_url' => $member->getProfileURL(), - 'count' => $count_column ? $row->{$count_column} : null, - ], - $overview - ? [] - : [ - 'group_html' => $member->getAllGroupHtml(), - 'metadata' => MemberListManager::getInstance()->getMemberMetadata($member), - ], - ); - } - - return $list_members; - } - - /** - * Determine the total number of members in this list. - * @return int The total number of members in this list - */ - public function getMemberCount(): int { - [$sql, $id_column] = $this->generator(); - $rows = DB::getInstance()->query($sql)->results(); - - if (Settings::get('member_list_hide_banned', false, 'Members')) { - $rows = $this->filterBanned($rows, $id_column); - } - - return count($rows); - } - - /** - * Filter out banned users from a list of members. - * - * @param array $rows Rows returned from the member list query - * @param string $id_column The name of the column in each row containing the user ID - * @return array The rows with banned users filtered out - */ - private function filterBanned(array $rows, string $id_column): array { - $ids = implode(',', array_map(static fn ($row) => $row->{$id_column}, $rows)); - if (empty($ids)) { - return []; - } - - $banned_users = DB::getInstance()->query("SELECT id, isbanned FROM nl2_users WHERE id IN ($ids) AND isbanned = 1")->results(); - return array_filter($rows, static function ($row) use ($banned_users, $id_column) { - foreach ($banned_users as $banned_user) { - if ($banned_user->id == $row->{$id_column}) { - return false; - } - } - - return true; - }); - } -} diff --git a/modules/Members/init.php b/modules/Members/init.php deleted file mode 100644 index 305eac9c17..0000000000 --- a/modules/Members/init.php +++ /dev/null @@ -1,7 +0,0 @@ -_language = $language; - $this->_members_language = $members_language; - - $name = 'Members'; - $author = 'Aberdeener'; - $module_version = '2.1.2'; - $nameless_version = '2.1.2'; - - parent::__construct($this, $name, $author, $module_version, $nameless_version); - - // Define URLs which belong to this module - $pages->add('Members', '/members', 'pages/members.php'); - $pages->add('Members', '/queries/member_list', 'queries/member_list.php'); - $pages->add('Members', '/panel/members/member_lists', 'pages/panel/member_lists.php'); - $pages->add('Members', '/panel/members/settings', 'pages/panel/settings.php'); - } - - public function onInstall() { - // Not necessary for CookieConsent - } - - public function onUninstall() { - // Not necessary for CookieConsent - } - - public function onEnable() { - // Not necessary for CookieConsent - } - - public function onDisable() { - // Not necessary for CookieConsent - } - - public function onPageLoad(User $user, Pages $pages, Cache $cache, Smarty $smarty, $navs, Widgets $widgets, ?TemplateBase $template) { - $language = $this->_language; - - // AdminCP - PermissionHandler::registerPermissions($language->get('moderator', 'staff_cp'), [ - 'admincp.members' => $this->_members_language->get('members', 'member_lists') - ]); - - $cache->setCache('navbar_order'); - if (!$cache->isCached('members_order')) { - $members_order = 5; - $cache->store('members_order', 5); - } else { - $members_order = $cache->retrieve('members_order'); - } - - $cache->setCache('navbar_icons'); - if (!$cache->isCached('members_icon')) { - $members_icon = ''; - } else { - $members_icon = $cache->retrieve('members_icon'); - } - - $cache->setCache('nav_location'); - if (!$cache->isCached('members_location')) { - $link_location = 1; - $cache->store('members_location', 1); - } else { - $link_location = $cache->retrieve('members_location'); - } - - switch ($link_location) { - case 1: - // Navbar - $navs[0]->add('members', $this->_members_language->get('members', 'members'), URL::build('/members'), 'top', null, $members_order, $members_icon); - break; - case 2: - // "More" dropdown - $navs[0]->addItemToDropdown('more_dropdown', 'members', $this->_members_language->get('members', 'members'), URL::build('/members'), 'top', null, $members_icon, $members_order); - break; - case 3: - // Footer - $navs[0]->add('members', $this->_members_language->get('members', 'members'), URL::build('/members'), 'footer', null, $members_order, $members_icon); - break; - } - - if (defined('BACK_END')) { - $cache->setCache('panel_sidebar'); - - // StaffCP link - if ($user->hasPermission('admincp.members')) { - if (!$cache->isCached('members_order')) { - $order = 13; - $cache->store('members_order', 13); - } else { - $order = $cache->retrieve('members_order'); - } - - if (!$cache->isCached('members_settings_icon')) { - $members_settings_icon = ''; - $cache->store('members_settings_icon', $members_settings_icon); - } else { - $members_settings_icon = $cache->retrieve('members_settings_icon'); - } - - if (!$cache->isCached('member_lists_icon')) { - $member_lists_icon = ''; - $cache->store('member_lists_icon', $member_lists_icon); - } else { - $member_lists_icon = $cache->retrieve('member_lists_icon'); - } - - $navs[2]->add('members_divider', mb_strtoupper($this->_members_language->get('members', 'members'), 'UTF-8'), 'divider', 'top', null, $order); - $navs[2]->add('members_settings', $this->_language->get('admin', 'settings'), URL::build('/panel/members/settings'), 'top', null, $order + 0.1, $members_settings_icon); - $navs[2]->add('member_lists_settings', $this->_members_language->get('members', 'member_lists'), URL::build('/panel/members/member_lists'), 'top', null, $order + 0.2, $member_lists_icon); - } - } - } - - public function getDebugInfo(): array { - return []; - } -} diff --git a/modules/Members/pages/members.php b/modules/Members/pages/members.php deleted file mode 100644 index 91a6142a48..0000000000 --- a/modules/Members/pages/members.php +++ /dev/null @@ -1,133 +0,0 @@ -get('members', 'members'); -require_once(ROOT_PATH . '/core/templates/frontend_init.php'); - -if (isset($_GET['group'])) { - if (!in_array($_GET['group'], json_decode(Settings::get('member_list_viewable_groups', '{}', 'Members'), true))) { - Redirect::to(URL::build('/members')); - } - - $viewing_list = 'group'; - $viewing_group = Group::find($_GET['group']); - $smarty->assign([ - 'VIEWING_GROUP' => [ - 'id' => $viewing_group->id, - 'name' => Output::getClean($viewing_group->name), - ], - ]); - - $lists_viewing = []; -} else { - $viewing_list = $_GET['list'] ?? 'overview'; - if ($viewing_list !== 'overview' - && (!MemberListManager::getInstance()->listExists($viewing_list) || !MemberListManager::getInstance()->getList($viewing_list)->isEnabled()) - ) { - Redirect::to(URL::build('/members')); - } - - $lists_viewing = $viewing_list === 'overview' - ? array_filter(MemberListManager::getInstance()->allEnabledLists(), static fn (MemberListProvider $list) => $list->displayOnOverview()) - : [MemberListManager::getInstance()->getList($viewing_list)]; -} - -// TODO handle this in MemberListManager and cache it using UserRegisteredEvent -$new_members = []; -if (Settings::get('member_list_hide_banned', false, 'Members')) { - $query = DB::getInstance()->query('SELECT id FROM nl2_users WHERE isbanned = 0 ORDER BY joined DESC LIMIT 12'); -} else { - $query = DB::getInstance()->query('SELECT id FROM nl2_users ORDER BY joined DESC LIMIT 12'); -} -foreach ($query->results() as $new_member) { - $new_members[] = new User($new_member->id); -} - -if (isset($error)) { - $smarty->assign([ - 'ERROR_TITLE' => $language->get('general', 'error'), - 'ERROR' => $error, - ]); -} - -$groups = []; -foreach (json_decode(Settings::get('member_list_viewable_groups', '{}', 'Members'), true) as $group_id) { - $group = Group::find($group_id); - if (!$group) { - continue; - } - $groups[] = [ - 'id' => $group->id, - 'name' => Output::getClean($group->name), - ]; -} - -if ($viewing_list !== 'overview') { - $member_count = isset($_GET['group']) - ? MemberListManager::getInstance()->getList($_GET['group'], true)->getMemberCount() - : MemberListManager::getInstance()->getList($viewing_list)->getMemberCount(); - $url_param = isset($_GET['group']) - ? 'group=' . $_GET['group'] - : 'list=' . $viewing_list; - - $template_pagination['div'] = $template_pagination['div'] .= ' centered'; - $paginator = new Paginator( - $template_pagination ?? null, - $template_pagination_left ?? null, - $template_pagination_right ?? null - ); - $paginator->setValues($member_count, 20, $_GET['p'] ?? 1); - $smarty->assign([ - 'PAGINATION' => $paginator->generate(6, URL::build('/members/', $url_param)), - ]); -} - -// Sort sidebar lists to have displayOnOverview lists first -$sidebar_lists = MemberListManager::getInstance()->allEnabledLists(); -usort($sidebar_lists, static function ($a, $b) { - return $b->displayOnOverview() - $a->displayOnOverview(); -}); - -$smarty->assign([ - 'MEMBERS' => $members_language->get('members', 'members'), - 'SIDEBAR_MEMBER_LISTS' => $sidebar_lists, - 'MEMBER_LISTS_VIEWING' => $lists_viewing, - 'VIEWING_LIST' => $viewing_list, - 'MEMBER_LIST_URL' => URL::build('/members'), - 'QUERIES_URL' => URL::build('/queries/member_list', 'list={{list}}&page={{page}}&overview=' . ($viewing_list === 'overview' ? 'true' : 'false')), - 'OVERVIEW' => $language->get('user', 'overview'), - 'VIEW_ALL' => $members_language->get('members', 'view_all'), - 'GROUPS' => $groups, - 'VIEW_GROUP_URL' => URL::build('/members', 'group='), - 'NEW_MEMBERS' => $members_language->get('members', 'new_members'), - 'NEW_MEMBERS_VALUE' => $new_members, - 'FIND_MEMBER' => $members_language->get('members', 'find_member'), - 'NAME' => $members_language->get('members', 'name'), - 'SEARCH_URL' => URL::build('/queries/users'), - 'NO_RESULTS_HEADER' => $members_language->get('members', 'no_results_header'), - 'NO_RESULTS_TEXT' => $members_language->get('members', 'no_results_text'), - 'VIEW_GROUP' => $members_language->get('members', 'view_group'), - 'GROUP' => $members_language->get('members', 'group'), - 'NO_MEMBERS_FOUND' => $members_language->get('members', 'no_members'), - 'NO_OVERVIEW_LISTS_ENABLED' => $members_language->get('members', 'no_overview_lists_enabled'), -]); - -// Load modules + template -Module::loadPage($user, $pages, $cache, $smarty, [$navigation, $cc_nav, $staffcp_nav], $widgets, $template); - -$template->onPageLoad(); - -require(ROOT_PATH . '/core/templates/navbar.php'); -require(ROOT_PATH . '/core/templates/footer.php'); - -// Display template -$template->displayTemplate('members/members.tpl', $smarty); diff --git a/modules/Members/pages/panel/member_lists.php b/modules/Members/pages/panel/member_lists.php deleted file mode 100644 index e3e04fad2e..0000000000 --- a/modules/Members/pages/panel/member_lists.php +++ /dev/null @@ -1,71 +0,0 @@ -handlePanelPageLoad('admincp.members')) { - require_once(ROOT_PATH . '/403.php'); - die(); -} - -const PAGE = 'panel'; -const PARENT_PAGE = 'members'; -const PANEL_PAGE = 'member_lists_settings'; - -$page_title = $members_language->get('members', 'member_lists'); -require_once(ROOT_PATH . '/core/templates/backend_init.php'); - -if (Input::exists()) { - if (Token::check()) { - $list = MemberListManager::getInstance()->getList($_POST['list']); - $enabled = DB::getInstance()->get('member_lists', ['name', $list->getName()])->first()->enabled; - DB::getInstance()->update('member_lists', ['name', $list->getName()], [ - 'enabled' => !$enabled - ]); - - Session::flash('admin_member_lists_success', $members_language->get('members', !$enabled ? 'member_list_toggled_enabled' : 'member_list_toggled_disabled', [ - 'list' => $list->getFriendlyName(), - ])); - - Redirect::to(URL::build('/panel/members/member_lists')); - } else { - Session::flash('admin_member_lists_error', $language->get('general', 'invalid_token')); - } -} - -if (Session::exists('admin_member_lists_error')) { - $smarty->assign([ - 'ERRORS' => [Session::flash('admin_member_lists_error')], - 'ERRORS_TITLE' => $language->get('general', 'error'), - ]); -} - -if (Session::exists('admin_member_lists_success')) { - $smarty->assign([ - 'SUCCESS' => Session::flash('admin_member_lists_success'), - 'SUCCESS_TITLE' => $language->get('general', 'success'), - ]); -} - -$smarty->assign([ - 'PARENT_PAGE' => PARENT_PAGE, - 'DASHBOARD' => $language->get('admin', 'dashboard'), - 'MEMBERS' => $members_language->get('members', 'members'), - 'MEMBER_LISTS' => $members_language->get('members', 'member_lists'), - 'PAGE' => PANEL_PAGE, - 'MEMBER_LISTS_VALUES' => MemberListManager::getInstance()->allLists(), - 'NAME' => $language->get('admin', 'name'), - 'ENABLED' => $language->get('admin', 'enabled'), - 'MODULE' => $language->get('admin', 'module'), - 'EDIT' => $language->get('general', 'edit'), - 'ENABLE' => $language->get('admin', 'enable'), - 'DISABLE' => $language->get('admin', 'disable'), - 'TOKEN' => Token::get(), -]); - -// Load modules + template -Module::loadPage($user, $pages, $cache, $smarty, [$navigation, $cc_nav, $staffcp_nav], $widgets, $template); - -$template->onPageLoad(); - -require(ROOT_PATH . '/core/templates/panel_navbar.php'); - -// Display template -$template->displayTemplate('members/member_lists.tpl', $smarty); diff --git a/modules/Members/pages/panel/settings.php b/modules/Members/pages/panel/settings.php deleted file mode 100644 index df01617cca..0000000000 --- a/modules/Members/pages/panel/settings.php +++ /dev/null @@ -1,104 +0,0 @@ -handlePanelPageLoad('admincp.members')) { - require_once(ROOT_PATH . '/403.php'); - die(); -} - -const PAGE = 'panel'; -const PARENT_PAGE = 'members'; -const PANEL_PAGE = 'members_settings'; - -$page_title = $members_language->get('members', 'members'); -require_once(ROOT_PATH . '/core/templates/backend_init.php'); - -if (Input::exists()) { - if (Token::check()) { - Settings::set('member_list_viewable_groups', json_encode(Input::get('groups')), 'Members'); - Settings::set('member_list_hide_banned', Input::get('hide_banned_users'), 'Members'); - - // Update link location - if (isset($_POST['link_location'])) { - switch ($_POST['link_location']) { - case 1: - case 2: - case 3: - case 4: - $location = $_POST['link_location']; - break; - default: - $location = 1; - } - } else { - $location = 1; - } - - // Update Link location cache - $cache->setCache('nav_location'); - $cache->store('members_location', $location); - - Session::flash('admin_members_settings', $members_language->get('members', 'settings_updated_successfully')); - } else { - // Invalid token - Session::flash('admin_members_settings_error', $language->get('general', 'invalid_token')); - } - Redirect::to(URL::build('/panel/members/settings')); -} - -// Retrieve Link Location from cache -$cache->setCache('nav_location'); -$link_location = $cache->retrieve('members_location'); - -// Load modules + template -Module::loadPage($user, $pages, $cache, $smarty, [$navigation, $cc_nav, $staffcp_nav], $widgets, $template); - -if (Session::exists('admin_members_settings')) { - $smarty->assign([ - 'SUCCESS' => Session::flash('admin_members_settings'), - 'SUCCESS_TITLE' => $language->get('general', 'success') - ]); -} - -if (Session::exists('admin_members_settings_error')) { - $smarty->assign([ - 'ERROR' => Session::flash('admin_members_settings_error'), - 'ERRORS_TITLE' => $language->get('general', 'success') - ]); -} - -$group_array = []; -foreach (Group::all() as $group) { - $group_array[] = [ - 'id' => $group->id, - 'name' => Output::getClean($group->name), - ]; -} - -$smarty->assign([ - 'PARENT_PAGE' => PARENT_PAGE, - 'DASHBOARD' => $language->get('admin', 'dashboard'), - 'MEMBERS' => $members_language->get('members', 'members'), - 'SETTINGS' => $language->get('admin', 'settings'), - 'LINK_LOCATION' => $language->get('admin', 'page_link_location'), - 'LINK_LOCATION_VALUE' => $link_location, - 'LINK_NAVBAR' => $language->get('admin', 'page_link_navbar'), - 'LINK_MORE' => $language->get('admin', 'page_link_more'), - 'LINK_FOOTER' => $language->get('admin', 'page_link_footer'), - 'LINK_NONE' => $language->get('admin', 'page_link_none'), - 'HIDE_BANNED_USERS' => $members_language->get('members', 'member_list_hide_banned_users'), - 'HIDE_BANNED_USERS_VALUE' => Settings::get('member_list_hide_banned', false, 'Members'), - 'GROUPS' => $members_language->get('members', 'viewable_groups'), - 'GROUPS_ARRAY' => $group_array, - 'GROUPS_VALUE' => json_decode(Settings::get('member_list_viewable_groups', '{}', 'Members'), true) ?: [], - 'NO_ITEM_SELECTED' => $language->get('admin', 'no_item_selected'), - 'PAGE' => PANEL_PAGE, - 'TOKEN' => Token::get(), - 'SUBMIT' => $language->get('general', 'submit'), -]); - -$template->onPageLoad(); - -require(ROOT_PATH . '/core/templates/panel_navbar.php'); - -// Display template -$template->displayTemplate('members/members_settings.tpl', $smarty); diff --git a/modules/Members/queries/member_list.php b/modules/Members/queries/member_list.php deleted file mode 100644 index f56eacaf11..0000000000 --- a/modules/Members/queries/member_list.php +++ /dev/null @@ -1,29 +0,0 @@ -setCache('member_list_queries'); -$key = ($list . '_page_' . $page) . ($overview ? '_overview' : '') . (Settings::get('member_list_hide_banned', false, 'Members') ? '_hide_banned' : ''); -if ($cache->isCached($key)) { - die($cache->retrieve($key)); -} - -if (str_starts_with($list, 'group_')) { - $members = MemberListManager::getInstance()->getList((int) substr($list, 6), true)->getMembers(false, $page); -} else { - $members = MemberListManager::getInstance()->getList($list)->getMembers($overview, $page); -} - -$members = json_encode($members); - -$cache->store($key, $members, 60); - -die($members);