Security: verwijder hardcoded wachtwoord, voeg random-wachtwoord-generator toe bij eerste installatie
This commit is contained in:
@@ -1,16 +1,54 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Lightweight API wrapper for plugins in the admin context.
|
||||
* Provides read-only access to the site config (no CMS instance needed).
|
||||
* Lichtgewicht API-wrapper voor plugins in de admin-context.
|
||||
*
|
||||
* Biedt alleen-lezen toegang tot de site-config (zonder CMS-instance nodig).
|
||||
*
|
||||
* @since 2.6.4
|
||||
*/
|
||||
class AdminPluginAPI implements PluginAPIInterface
|
||||
{
|
||||
/**
|
||||
* De site-configuratie als associatieve array.
|
||||
*
|
||||
* @since 2.6.4
|
||||
* @var array<string,mixed>
|
||||
*/
|
||||
private array $config;
|
||||
|
||||
/**
|
||||
* Het absolute pad naar de project-root.
|
||||
*
|
||||
* @since 2.6.4
|
||||
* @var string
|
||||
*/
|
||||
private string $projectRoot;
|
||||
|
||||
/**
|
||||
* De actieve admin-taalcode (bijv. 'nl', 'en').
|
||||
*
|
||||
* @since 2.6.4
|
||||
* @var string
|
||||
*/
|
||||
private string $adminLanguage;
|
||||
|
||||
/**
|
||||
* De admin PluginManager-instantie, optioneel via setPluginManager geïnjecteerd.
|
||||
*
|
||||
* @since 2.6.4
|
||||
* @var PluginManager|null
|
||||
*/
|
||||
private ?PluginManager $pluginManager = null;
|
||||
|
||||
/**
|
||||
* Construeer een AdminPluginAPI-instantie met de opgegeven site-config.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param array<string,mixed> $siteConfig De site-configuratie.
|
||||
* @param string $projectRoot Optioneel absoluut pad naar de project-root.
|
||||
*/
|
||||
public function __construct(array $siteConfig, string $projectRoot = '')
|
||||
{
|
||||
$this->config = $siteConfig;
|
||||
@@ -20,8 +58,13 @@ class AdminPluginAPI implements PluginAPIInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject the admin PluginManager so plugins can resolve their own
|
||||
* translations through the same fallback chain.
|
||||
* Injecteer de admin PluginManager zodat plugins hun eigen vertalingen
|
||||
* via dezelfde fallback-keten kunnen resolven.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param PluginManager $pm De admin PluginManager-instantie.
|
||||
* @return void
|
||||
*/
|
||||
public function setPluginManager(PluginManager $pm): void
|
||||
{
|
||||
@@ -29,7 +72,13 @@ class AdminPluginAPI implements PluginAPIInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configuration value using dot notation.
|
||||
* Haal een configuratiewaarde op via dot-notatie.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param string $key Configuratie-sleutel in dot-notatie (bijv. 'language.default').
|
||||
* @param mixed $default Standaardwaarde indien de sleutel niet bestaat.
|
||||
* @return mixed De gevonden waarde of $default indien niet gevonden.
|
||||
*/
|
||||
public function getConfig(string $key, $default = null)
|
||||
{
|
||||
@@ -47,7 +96,11 @@ class AdminPluginAPI implements PluginAPIInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the project root directory.
|
||||
* Haal de project-rootdirectory op.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @return string Absoluut pad naar de project-root.
|
||||
*/
|
||||
public function getProjectRoot(): string
|
||||
{
|
||||
@@ -55,15 +108,30 @@ class AdminPluginAPI implements PluginAPIInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the content directory path.
|
||||
* Haal het pad naar de contentdirectory op als absoluut pad.
|
||||
*
|
||||
* Relatieve paden uit config.json (bijv. 'content') worden afgezet tegen
|
||||
* de project-root, conform het gedrag van cms/core/config.php.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @return string Absoluut pad naar de contentdirectory.
|
||||
*/
|
||||
public function getContentDir(): string
|
||||
{
|
||||
return $this->config['content_dir'] ?? ($this->projectRoot . '/content');
|
||||
$dir = $this->config['content_dir'] ?? ($this->projectRoot . '/content');
|
||||
if ($dir !== '' && $dir[0] !== DIRECTORY_SEPARATOR && !preg_match('#^[A-Za-z]:[/\\\\]#', $dir)) {
|
||||
$dir = $this->projectRoot . '/' . $dir;
|
||||
}
|
||||
return rtrim($dir, '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the plugins directory path.
|
||||
* Haal het pad naar de pluginsdirectory op.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @return string Absoluut pad naar de pluginsdirectory.
|
||||
*/
|
||||
public function getPluginsDir(): string
|
||||
{
|
||||
@@ -71,7 +139,11 @@ class AdminPluginAPI implements PluginAPIInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of enabled plugins.
|
||||
* Haal de lijst met ingeschakelde plugins op.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @return array<int,string> Lijst met ingeschakelde plugin-namen.
|
||||
*/
|
||||
public function getEnabledPlugins(): array
|
||||
{
|
||||
@@ -79,7 +151,11 @@ class AdminPluginAPI implements PluginAPIInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the version info from version.php.
|
||||
* Haal de versie-informatie uit version.php op.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @return array<string,mixed> Versie-informatie met minimaal de sleutel 'version'.
|
||||
*/
|
||||
public function getVersionInfo(): array
|
||||
{
|
||||
@@ -94,8 +170,13 @@ class AdminPluginAPI implements PluginAPIInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the active admin language code (e.g. 'nl', 'en').
|
||||
* System plugins should use this to resolve their translations.
|
||||
* Haal de actieve admin-taalcode op (bijv. 'nl', 'en').
|
||||
*
|
||||
* System-plugins gebruiken deze om hun vertalingen te resolven.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @return string De actieve admin-taalcode.
|
||||
*/
|
||||
public function getAdminLanguage(): string
|
||||
{
|
||||
@@ -103,14 +184,16 @@ class AdminPluginAPI implements PluginAPIInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the translations for a plugin in the admin context.
|
||||
* Haal de vertalingen voor een plugin op in de admin-context.
|
||||
*
|
||||
* Fallback chain (handled by PluginManager):
|
||||
* requested language -> plugin default_language -> empty array.
|
||||
* Fallback-keten (afgehandeld door PluginManager):
|
||||
* aangevraagde taal -> default_language van de plugin -> lege array.
|
||||
*
|
||||
* @param string $pluginName Plugin directory name
|
||||
* @param string|null $lang Override language; defaults to the active admin language
|
||||
* @return array Translations [key => value]
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param string $pluginName Plugin-directorynaam.
|
||||
* @param string|null $lang Override-taal; standaard de actieve admin-taal.
|
||||
* @return array<string,string> Vertalingen als [key => value].
|
||||
*/
|
||||
public function getPluginTranslations(string $pluginName, ?string $lang = null): array
|
||||
{
|
||||
@@ -122,12 +205,14 @@ class AdminPluginAPI implements PluginAPIInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate a single key for a plugin in the admin context.
|
||||
* Vertaal een enkele sleutel voor een plugin in de admin-context.
|
||||
*
|
||||
* @param string $key Translation key
|
||||
* @param string $pluginName Plugin directory name
|
||||
* @param string|null $lang Override language; defaults to the active admin language
|
||||
* @return string Translated string, or $key if not found
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param string $key Vertaalsleutel.
|
||||
* @param string $pluginName Plugin-directorynaam.
|
||||
* @param string|null $lang Override-taal; standaard de actieve admin-taal.
|
||||
* @return string Vertaalde string, of $key indien niet gevonden.
|
||||
*/
|
||||
public function t(string $key, string $pluginName, ?string $lang = null): string
|
||||
{
|
||||
|
||||
+225
-38
@@ -1,18 +1,51 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Front-end plugin-API voor content-plugins.
|
||||
*
|
||||
* Biedt toegang tot de CodePressCMS-instance, pagina-informatie, menu, zoekresultaten,
|
||||
* vertalingen en bestandsoperaties binnen de front-end context.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*/
|
||||
class CMSAPI implements PluginAPIInterface
|
||||
{
|
||||
/**
|
||||
* De CodePressCMS-hoofdinstantie.
|
||||
*
|
||||
* @since 2.6.4
|
||||
* @var CodePressCMS
|
||||
*/
|
||||
private CodePressCMS $cms;
|
||||
|
||||
/**
|
||||
* De front-end PluginManager-instantie, optioneel via setPluginManager geïnjecteerd.
|
||||
*
|
||||
* @since 2.6.4
|
||||
* @var PluginManager|null
|
||||
*/
|
||||
private ?PluginManager $pluginManager = null;
|
||||
|
||||
/**
|
||||
* Construeer een CMSAPI-instantie met de opgegeven CodePressCMS.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param CodePressCMS $cms De CodePressCMS-hoofdinstantie.
|
||||
*/
|
||||
public function __construct(CodePressCMS $cms)
|
||||
{
|
||||
$this->cms = $cms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject the front-end PluginManager so content plugins can resolve
|
||||
* their own translations through the same fallback chain.
|
||||
* Injecteer de front-end PluginManager zodat content-plugins hun eigen
|
||||
* vertalingen via dezelfde fallback-keten kunnen resolven.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param PluginManager $pm De front-end PluginManager-instantie.
|
||||
* @return void
|
||||
*/
|
||||
public function setPluginManager(PluginManager $pm): void
|
||||
{
|
||||
@@ -20,7 +53,11 @@ class CMSAPI implements PluginAPIInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current page information
|
||||
* Haal de informatie van de huidige pagina op.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @return array<string,mixed> Pagina-informatie van de huidige pagina.
|
||||
*/
|
||||
public function getCurrentPage(): array
|
||||
{
|
||||
@@ -28,7 +65,11 @@ class CMSAPI implements PluginAPIInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current page title
|
||||
* Haal de titel van de huidige pagina op.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @return string De titel van de huidige pagina, of lege string.
|
||||
*/
|
||||
public function getCurrentPageTitle(): string
|
||||
{
|
||||
@@ -37,7 +78,11 @@ class CMSAPI implements PluginAPIInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current page content
|
||||
* Haal de inhoud van de huidige pagina op.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @return string De HTML-inhoud van de huidige pagina, of lege string.
|
||||
*/
|
||||
public function getCurrentPageContent(): string
|
||||
{
|
||||
@@ -46,7 +91,11 @@ class CMSAPI implements PluginAPIInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current page URL
|
||||
* Haal de URL van de huidige pagina op.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @return string De URL van de huidige pagina inclusief query-parameters.
|
||||
*/
|
||||
public function getCurrentPageUrl(): string
|
||||
{
|
||||
@@ -56,7 +105,11 @@ class CMSAPI implements PluginAPIInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Get menu structure
|
||||
* Haal de menustructuur op.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @return array<int,array<string,mixed>> De menustructuur.
|
||||
*/
|
||||
public function getMenu(): array
|
||||
{
|
||||
@@ -64,7 +117,13 @@ class CMSAPI implements PluginAPIInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configuration value
|
||||
* Haal een configuratiewaarde op via dot-notatie.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param string $key Configuratie-sleutel in dot-notatie (bijv. 'language.default').
|
||||
* @param mixed $default Standaardwaarde indien de sleutel niet bestaat.
|
||||
* @return mixed De gevonden waarde of $default indien niet gevonden.
|
||||
*/
|
||||
public function getConfig(string $key, $default = null)
|
||||
{
|
||||
@@ -82,7 +141,70 @@ class CMSAPI implements PluginAPIInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Get translation
|
||||
* Haal de project-rootdirectory op (absoluut, genormaliseerd).
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @return string Absoluut pad naar de project-root.
|
||||
*/
|
||||
public function getProjectRoot(): string
|
||||
{
|
||||
return dirname(__DIR__, 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Haal het pad naar de contentdirectory op (absoluut, genormaliseerd).
|
||||
*
|
||||
* De CMS-config lost relatieve content_dir-waarden al op
|
||||
* (zie cms/core/config.php), dus deze wordt as-is teruggegeven.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @return string Absoluut pad naar de contentdirectory.
|
||||
*/
|
||||
public function getContentDir(): string
|
||||
{
|
||||
return rtrim($this->cms->config['content_dir'] ?? ($this->getProjectRoot() . '/content'), '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Haal het pad naar de pluginsdirectory op (absoluut, genormaliseerd).
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @return string Absoluut pad naar de pluginsdirectory.
|
||||
*/
|
||||
public function getPluginsDir(): string
|
||||
{
|
||||
return $this->getProjectRoot() . '/plugins';
|
||||
}
|
||||
|
||||
/**
|
||||
* Haal de versie-informatie uit version.php op.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @return array<string,mixed> Versie-informatie met minimaal de sleutel 'version'.
|
||||
*/
|
||||
public function getVersionInfo(): array
|
||||
{
|
||||
$versionFile = $this->getProjectRoot() . '/version.php';
|
||||
if (file_exists($versionFile)) {
|
||||
$data = include $versionFile;
|
||||
if (is_array($data)) {
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
return ['version' => '0.0.0'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Haal een vertaling op voor de opgegeven sleutel.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param string $key Vertaalsleutel.
|
||||
* @return string De vertaalde string, of $key indien niet gevonden.
|
||||
*/
|
||||
public function translate(string $key): string
|
||||
{
|
||||
@@ -90,7 +212,11 @@ class CMSAPI implements PluginAPIInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current language
|
||||
* Haal de huidige content-taal op.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @return string De huidige taalcode (bijv. 'nl', 'en').
|
||||
*/
|
||||
public function getCurrentLanguage(): string
|
||||
{
|
||||
@@ -98,7 +224,11 @@ class CMSAPI implements PluginAPIInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user is on homepage
|
||||
* Controleer of de gebruiker zich op de homepage bevindt.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @return bool True indien de huidige pagina de default_page is.
|
||||
*/
|
||||
public function isHomepage(): bool
|
||||
{
|
||||
@@ -108,7 +238,11 @@ class CMSAPI implements PluginAPIInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Get file info for current page
|
||||
* Haal de bestandsinformatie van de huidige pagina op.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @return array<string,mixed>|null Bestandsinformatie of null indien niet aanwezig.
|
||||
*/
|
||||
public function getCurrentPageFileInfo(): ?array
|
||||
{
|
||||
@@ -117,7 +251,11 @@ class CMSAPI implements PluginAPIInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Get breadcrumb data
|
||||
* Haal de breadcrumb-HTML op.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @return string De gegenereerde breadcrumb-HTML.
|
||||
*/
|
||||
public function getBreadcrumb(): string
|
||||
{
|
||||
@@ -125,7 +263,11 @@ class CMSAPI implements PluginAPIInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if content directory has content
|
||||
* Controleer of de contentdirectory inhoud bevat.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @return bool True indien de contentdirectory niet leeg is.
|
||||
*/
|
||||
public function hasContent(): bool
|
||||
{
|
||||
@@ -133,7 +275,11 @@ class CMSAPI implements PluginAPIInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Get search results if searching
|
||||
* Haal de zoekresultaten op indien een zoekopdracht actief is.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @return array<int,array<string,mixed>> De zoekresultaten, of een lege array.
|
||||
*/
|
||||
public function getSearchResults(): array
|
||||
{
|
||||
@@ -144,7 +290,11 @@ class CMSAPI implements PluginAPIInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if currently searching
|
||||
* Controleer of momenteel een zoekopdracht wordt uitgevoerd.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @return bool True indien de search-query-parameter aanwezig is.
|
||||
*/
|
||||
public function isSearching(): bool
|
||||
{
|
||||
@@ -152,7 +302,11 @@ class CMSAPI implements PluginAPIInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available languages
|
||||
* Haal de beschikbare talen op.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @return array<int,string> Lijst met beschikbare taalcodes.
|
||||
*/
|
||||
public function getAvailableLanguages(): array
|
||||
{
|
||||
@@ -160,7 +314,13 @@ class CMSAPI implements PluginAPIInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Create URL for page
|
||||
* Maak een URL voor een pagina.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param string $page Pagina-identifier.
|
||||
* @param string|null $lang Taalcode; standaard de huidige content-taal.
|
||||
* @return string De gegenereerde pagina-URL met query-parameters.
|
||||
*/
|
||||
public function createUrl(string $page, ?string $lang = null): string
|
||||
{
|
||||
@@ -169,7 +329,15 @@ class CMSAPI implements PluginAPIInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute PHP file and capture output
|
||||
* Voer een PHP-bestand uit en vang de output op.
|
||||
*
|
||||
* Het bestand moet binnen de CMS-directory liggen om willekeurige
|
||||
* file-inclusion te voorkomen.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param string $filePath Absoluut pad naar het PHP-bestand.
|
||||
* @return string De opgevangen output, of lege string indien ongeldig.
|
||||
*/
|
||||
public function executePhpFile(string $filePath): string
|
||||
{
|
||||
@@ -190,7 +358,12 @@ class CMSAPI implements PluginAPIInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Get content from PHP/HTML/Markdown file
|
||||
* Haal de inhoud op uit een PHP-, HTML- of Markdown-bestand.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param string $filePath Pad naar het bestand.
|
||||
* @return string De bestandsinhoud (gerenderd indien PHP/Markdown), of lege string.
|
||||
*/
|
||||
public function getFileContent(string $filePath): string
|
||||
{
|
||||
@@ -215,7 +388,12 @@ class CMSAPI implements PluginAPIInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if file exists in content directory
|
||||
* Controleer of een bestand in de contentdirectory bestaat.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param string $filename Bestandsnaam relatief ten opzichte van de contentdirectory.
|
||||
* @return bool True indien het bestand bestaat.
|
||||
*/
|
||||
public function contentFileExists(string $filename): bool
|
||||
{
|
||||
@@ -224,9 +402,11 @@ class CMSAPI implements PluginAPIInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all content entries (mappen + bestanden) as a list of entry arrays.
|
||||
* Haal alle content-items (mappen + bestanden) op als lijst met entry-arrays.
|
||||
*
|
||||
* @return array List of ['path' => ..., 'title' => ..., 'type' => ...]
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @return array<int,array<string,mixed>> Lijst met entries ['path' => ..., 'title' => ..., 'type' => ...].
|
||||
*/
|
||||
public function getAllPages(): array
|
||||
{
|
||||
@@ -234,10 +414,13 @@ class CMSAPI implements PluginAPIInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the author metadata for the current page.
|
||||
* Returns author_name, author_email and created from the page frontmatter.
|
||||
* Haal de auteursmetadata van de huidige pagina op.
|
||||
*
|
||||
* @return array Author metadata with keys: author_name, author_email, created
|
||||
* Geeft author_name, author_email en created terug uit de pagina-frontmatter.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @return array<string,string> Auteursmetadata met sleutels: author_name, author_email, created.
|
||||
*/
|
||||
public function getPageAuthor(): array
|
||||
{
|
||||
@@ -251,15 +434,17 @@ class CMSAPI implements PluginAPIInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the translations for a plugin in the front-end context.
|
||||
* Haal de vertalingen voor een plugin op in de front-end context.
|
||||
*
|
||||
* Content plugins follow the current content language. Fallback chain
|
||||
* (handled by PluginManager): requested language -> plugin
|
||||
* default_language -> empty array.
|
||||
* Content-plugins volgen de huidige content-taal. Fallback-keten
|
||||
* (afgehandeld door PluginManager): aangevraagde taal ->
|
||||
* default_language van de plugin -> lege array.
|
||||
*
|
||||
* @param string $pluginName Plugin directory name
|
||||
* @param string|null $lang Override language; defaults to the current content language
|
||||
* @return array Translations [key => value]
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param string $pluginName Plugin-directorynaam.
|
||||
* @param string|null $lang Override-taal; standaard de huidige content-taal.
|
||||
* @return array<string,string> Vertalingen als [key => value].
|
||||
*/
|
||||
public function getPluginTranslations(string $pluginName, ?string $lang = null): array
|
||||
{
|
||||
@@ -271,12 +456,14 @@ class CMSAPI implements PluginAPIInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate a single key for a plugin in the front-end context.
|
||||
* Vertaal een enkele sleutel voor een plugin in de front-end context.
|
||||
*
|
||||
* @param string $key Translation key
|
||||
* @param string $pluginName Plugin directory name
|
||||
* @param string|null $lang Override language; defaults to the current content language
|
||||
* @return string Translated string, or $key if not found
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param string $key Vertaalsleutel.
|
||||
* @param string $pluginName Plugin-directorynaam.
|
||||
* @param string|null $lang Override-taal; standaard de huidige content-taal.
|
||||
* @return string Vertaalde string, of $key indien niet gevonden.
|
||||
*/
|
||||
public function t(string $key, string $pluginName, ?string $lang = null): string
|
||||
{
|
||||
|
||||
@@ -1,33 +1,89 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Interface for plugin API objects.
|
||||
* Both CMSAPI (front-end) and AdminPluginAPI (admin) implement this.
|
||||
* Interface voor plugin-API-objecten.
|
||||
*
|
||||
* Zowel CMSAPI (front-end) als AdminPluginAPI (admin) implementeren deze interface.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*/
|
||||
interface PluginAPIInterface
|
||||
{
|
||||
/**
|
||||
* Haal een configuratiewaarde op via dot-notatie.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param string $key Configuratie-sleutel in dot-notatie (bijv. 'language.default').
|
||||
* @param mixed $default Standaardwaarde indien de sleutel niet bestaat.
|
||||
* @return mixed De gevonden waarde of $default indien niet gevonden.
|
||||
*/
|
||||
public function getConfig(string $key, $default = null);
|
||||
|
||||
/**
|
||||
* Get the translations for a plugin in the current context.
|
||||
* Haal de project-rootdirectory op (absoluut, genormaliseerd).
|
||||
*
|
||||
* System plugins resolve against the admin language; content plugins
|
||||
* against the current content language. The implementation handles the
|
||||
* fallback to the plugin's default_language.
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param string $pluginName Plugin directory name
|
||||
* @param string|null $lang Override language (optional)
|
||||
* @return array Translations [key => value]
|
||||
* @return string Absoluut pad naar de project-root.
|
||||
*/
|
||||
public function getProjectRoot(): string;
|
||||
|
||||
/**
|
||||
* Haal het pad naar de contentdirectory op (absoluut, genormaliseerd).
|
||||
*
|
||||
* Bij een relatieve configuratiewaarde wordt deze afgezet tegen de project-root.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @return string Absoluut pad naar de contentdirectory.
|
||||
*/
|
||||
public function getContentDir(): string;
|
||||
|
||||
/**
|
||||
* Haal het pad naar de pluginsdirectory op (absoluut, genormaliseerd).
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @return string Absoluut pad naar de pluginsdirectory.
|
||||
*/
|
||||
public function getPluginsDir(): string;
|
||||
|
||||
/**
|
||||
* Haal de versie-informatie uit version.php op als associatieve array.
|
||||
*
|
||||
* Bevat altijd minimaal de sleutel 'version'.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @return array<string,mixed> Versie-informatie met minimaal de sleutel 'version'.
|
||||
*/
|
||||
public function getVersionInfo(): array;
|
||||
|
||||
/**
|
||||
* Haal de vertalingen voor een plugin op in de huidige context.
|
||||
*
|
||||
* System-plugins resolven tegen de admin-taal; content-plugins tegen de
|
||||
* huidige content-taal. De implementatie regelt de fallback naar de
|
||||
* default_language van de plugin.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param string $pluginName Plugin-directorynaam.
|
||||
* @param string|null $lang Optionele override-taal.
|
||||
* @return array<string,string> Vertalingen als [key => value].
|
||||
*/
|
||||
public function getPluginTranslations(string $pluginName, ?string $lang = null): array;
|
||||
|
||||
/**
|
||||
* Translate a single key for a plugin in the current context.
|
||||
* Vertaal een enkele sleutel voor een plugin in de huidige context.
|
||||
*
|
||||
* @param string $key Translation key
|
||||
* @param string $pluginName Plugin directory name
|
||||
* @param string|null $lang Override language (optional)
|
||||
* @return string Translated string, or $key if not found
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param string $key Vertaalsleutel.
|
||||
* @param string $pluginName Plugin-directorynaam.
|
||||
* @param string|null $lang Optionele override-taal.
|
||||
* @return string Vertaalde string, of $key indien niet gevonden.
|
||||
*/
|
||||
public function t(string $key, string $pluginName, ?string $lang = null): string;
|
||||
}
|
||||
@@ -1,15 +1,82 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Beheert het laden en hooks-systeem van plugins.
|
||||
*
|
||||
* Laadt ingeschakelde plugins uit de pluginsdirectory, registreert automatisch
|
||||
* action- en filter-hooks, en biedt methoden voor plugin-configuratie,
|
||||
* vertalingen, admin-routes en sidebar-content.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*/
|
||||
class PluginManager
|
||||
{
|
||||
/**
|
||||
* Geladen plugin-instanties, geïndexeerd op plugin-naam.
|
||||
*
|
||||
* @since 2.6.4
|
||||
* @var array<string,object>
|
||||
*/
|
||||
private array $plugins = [];
|
||||
|
||||
/**
|
||||
* Het absolute pad naar de pluginsdirectory.
|
||||
*
|
||||
* @since 2.6.4
|
||||
* @var string
|
||||
*/
|
||||
private string $pluginsPath;
|
||||
|
||||
/**
|
||||
* De geïnjecteerde plugin-API-instantie (CMSAPI of AdminPluginAPI).
|
||||
*
|
||||
* @since 2.6.4
|
||||
* @var PluginAPIInterface|null
|
||||
*/
|
||||
private $api = null;
|
||||
|
||||
/**
|
||||
* Lijst met ingeschakelde plugin-namen.
|
||||
*
|
||||
* @since 2.6.4
|
||||
* @var array<int,string>
|
||||
*/
|
||||
private array $enabledPlugins = [];
|
||||
|
||||
/**
|
||||
* Geregistreerde action-hooks, gegroepeerd per hook-naam en prioriteit.
|
||||
*
|
||||
* @since 2.6.4
|
||||
* @var array<string,array<int,array<int,callable>>>
|
||||
*/
|
||||
private array $actions = [];
|
||||
|
||||
/**
|
||||
* Geregistreerde filter-hooks, gegroepeerd per hook-naam en prioriteit.
|
||||
*
|
||||
* @since 2.6.4
|
||||
* @var array<string,array<int,array<int,callable>>>
|
||||
*/
|
||||
private array $filters = [];
|
||||
|
||||
/**
|
||||
* De standaardtaal van de site, gebruikt als fallback voor plugins
|
||||
* zonder eigen default_language in plugin.json.
|
||||
*
|
||||
* @since 2.6.4
|
||||
* @var string
|
||||
*/
|
||||
private string $siteDefaultLanguage = 'nl';
|
||||
|
||||
/**
|
||||
* Construeer een PluginManager en laadt direct de ingeschakelde plugins.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param string $pluginsPath Absoluut pad naar de pluginsdirectory.
|
||||
* @param array<int,string> $enabledPlugins Lijst met ingeschakelde plugin-namen.
|
||||
* @param string $siteDefaultLanguage Standaardtaal van de site.
|
||||
*/
|
||||
public function __construct(string $pluginsPath, array $enabledPlugins = [], string $siteDefaultLanguage = 'nl')
|
||||
{
|
||||
$this->pluginsPath = $pluginsPath;
|
||||
@@ -19,14 +86,31 @@ class PluginManager
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the CMS site default language. Used as a fallback when a plugin
|
||||
* does not declare its own `default_language` in plugin.json.
|
||||
* Stel de standaardtaal van de site in.
|
||||
*
|
||||
* Wordt gebruikt als fallback wanneer een plugin geen eigen `default_language`
|
||||
* declareert in plugin.json.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param string $lang Taalcode (bijv. 'nl', 'en').
|
||||
* @return void
|
||||
*/
|
||||
public function setSiteDefaultLanguage(string $lang): void
|
||||
{
|
||||
$this->siteDefaultLanguage = $lang !== '' ? $lang : 'nl';
|
||||
}
|
||||
|
||||
/**
|
||||
* Stel de plugin-API in en geef deze door aan alle geladen plugins.
|
||||
*
|
||||
* Plugins die een setAPI()-methode implementeren, ontvangen de API-instantie.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param PluginAPIInterface $api De plugin-API-instantie.
|
||||
* @return void
|
||||
*/
|
||||
public function setAPI($api): void
|
||||
{
|
||||
$this->api = $api;
|
||||
@@ -38,6 +122,16 @@ class PluginManager
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Laad alle ingeschakelde plugins uit de pluginsdirectory.
|
||||
*
|
||||
* Per plugin wordt het hoofdbestand (<naam>.php) geïncludeerd, de plugin-class
|
||||
* geïnstantieerd, en action- en filter-hooks automatisch geregistreerd.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function loadPlugins(): void
|
||||
{
|
||||
if (!is_dir($this->pluginsPath)) {
|
||||
@@ -82,16 +176,45 @@ class PluginManager
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registreer een action-callback voor een hook.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param string $hook Naam van de action-hook.
|
||||
* @param callable $callback De callback die bij de hook wordt uitgevoerd.
|
||||
* @param int $priority Uitvoer-volgorde; lager wordt eerder uitgevoerd.
|
||||
* @return void
|
||||
*/
|
||||
public function addAction(string $hook, callable $callback, int $priority = 10): void
|
||||
{
|
||||
$this->actions[$hook][$priority][] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registreer een filter-callback voor een hook.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param string $hook Naam van de filter-hook.
|
||||
* @param callable $callback De callback die de waarde filtert.
|
||||
* @param int $priority Uitvoer-volgorde; lager wordt eerder uitgevoerd.
|
||||
* @return void
|
||||
*/
|
||||
public function addFilter(string $hook, callable $callback, int $priority = 10): void
|
||||
{
|
||||
$this->filters[$hook][$priority][] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Voer alle geregistreerde callbacks voor een action-hook uit.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param string $hook Naam van de action-hook.
|
||||
* @param mixed ...$args Argumenten die aan elke callback worden doorgegeven.
|
||||
* @return void
|
||||
*/
|
||||
public function doAction(string $hook, ...$args): void
|
||||
{
|
||||
if (!isset($this->actions[$hook])) return;
|
||||
@@ -103,6 +226,16 @@ class PluginManager
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pas alle geregistreerde filter-callbacks toe op een waarde.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param string $hook Naam van de filter-hook.
|
||||
* @param mixed $value De initiële waarde die wordt gefilterd.
|
||||
* @param mixed ...$args Extra argumenten voor elke filter-callback.
|
||||
* @return mixed De gefilterde waarde.
|
||||
*/
|
||||
public function applyFilters(string $hook, $value, ...$args)
|
||||
{
|
||||
if (!isset($this->filters[$hook])) return $value;
|
||||
@@ -115,32 +248,66 @@ class PluginManager
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Haal een specifieke plugin-instantie op.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param string $name Plugin-naam (directorynaam).
|
||||
* @return object|null De plugin-instantie of null indien niet geladen.
|
||||
*/
|
||||
public function getPlugin(string $name): ?object
|
||||
{
|
||||
return $this->plugins[$name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Haal alle geladen plugin-instanties op.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @return array<string,object> Plugin-instanties geïndexeerd op naam.
|
||||
*/
|
||||
public function getAllPlugins(): array
|
||||
{
|
||||
return $this->plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Haal de lijst met ingeschakelde plugin-namen op.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @return array<int,string> Lijst met ingeschakelde plugin-namen.
|
||||
*/
|
||||
public function getEnabledPlugins(): array
|
||||
{
|
||||
return $this->enabledPlugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Controleer of een plugin is ingeschakeld.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param string $pluginName Plugin-naam (directorynaam).
|
||||
* @return bool True indien de plugin is ingeschakeld.
|
||||
*/
|
||||
public function isEnabled(string $pluginName): bool
|
||||
{
|
||||
return in_array($pluginName, $this->enabledPlugins, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the runtime config for a plugin: defaults from plugin.json `settings`
|
||||
* merged with overrides from the plugin's config.json.
|
||||
* Haal de runtime-configuratie van een plugin op.
|
||||
*
|
||||
* @param string $pluginName Plugin name (directory name)
|
||||
* @return array Resolved config: [key => value, ...]
|
||||
* Standaardwaarden uit plugin.json `settings` worden samengevoegd met
|
||||
* overrides uit het config.json van de plugin.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param string $pluginName Plugin-naam (directorynaam).
|
||||
* @return array<string,mixed> Opgeloste configuratie als [key => value].
|
||||
*/
|
||||
public function getPluginConfig(string $pluginName): array
|
||||
{
|
||||
@@ -167,15 +334,17 @@ class PluginManager
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default (fallback) language declared by a plugin.
|
||||
* Haal de standaard (fallback) taal van een plugin op.
|
||||
*
|
||||
* Resolved from (in order):
|
||||
* Resolved in volgorde van:
|
||||
* 1. plugin.json `default_language`
|
||||
* 2. CMS site config `language.default`
|
||||
* 2. CMS site-config `language.default`
|
||||
* 3. 'nl'
|
||||
*
|
||||
* @param string $pluginName Plugin directory name
|
||||
* @return string Language code (e.g. 'nl', 'en')
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param string $pluginName Plugin-directorynaam.
|
||||
* @return string Taalcode (bijv. 'nl', 'en').
|
||||
*/
|
||||
public function getPluginDefaultLanguage(string $pluginName): string
|
||||
{
|
||||
@@ -190,15 +359,17 @@ class PluginManager
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the translations for a plugin for the requested language.
|
||||
* Laad de vertalingen voor een plugin voor de aangevraagde taal.
|
||||
*
|
||||
* Fallback chain: requested language -> plugin default language -> empty array.
|
||||
* Fallback-keten: aangevraagde taal -> standaardtaal van de plugin -> lege array.
|
||||
*
|
||||
* @param string $pluginName Plugin directory name
|
||||
* @param string $lang Requested language code
|
||||
* @param string $context Translation context: 'admin' or 'site' (front-end).
|
||||
* Defaults to 'admin' for system plugins, 'site' for content plugins.
|
||||
* @return array Translations [key => value]
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param string $pluginName Plugin-directorynaam.
|
||||
* @param string $lang Aangevraagde taalcode.
|
||||
* @param string $context Vertaalcontext: 'admin' of 'site' (front-end).
|
||||
* Standaard 'admin' voor system-plugins, 'site' voor content-plugins.
|
||||
* @return array<string,string> Vertalingen als [key => value].
|
||||
*/
|
||||
public function getPluginTranslations(string $pluginName, string $lang, string $context = 'admin'): array
|
||||
{
|
||||
@@ -232,14 +403,16 @@ class PluginManager
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect translations for every loaded plugin for the requested language.
|
||||
* Verzamel vertalingen voor alle geladen plugins voor de aangevraagde taal.
|
||||
*
|
||||
* Returns an associative array keyed by plugin name:
|
||||
* Geeft een associatieve array terug, geïndexeerd op plugin-naam:
|
||||
* ['Statistics' => [...], 'Logs' => [...], ...]
|
||||
*
|
||||
* @param string $lang Requested language code
|
||||
* @param string $context 'admin' or 'site'
|
||||
* @return array Plugin translations keyed by plugin name
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param string $lang Aangevraagde taalcode.
|
||||
* @param string $context 'admin' of 'site'.
|
||||
* @return array<string,array<string,string>> Plugin-vertalingen geïndexeerd op plugin-naam.
|
||||
*/
|
||||
public function getAllPluginTranslations(string $lang, string $context = 'admin'): array
|
||||
{
|
||||
@@ -252,6 +425,16 @@ class PluginManager
|
||||
return $all;
|
||||
}
|
||||
|
||||
/**
|
||||
* Controleer of een plugin viewable is.
|
||||
*
|
||||
* Een plugin is viewable tenzij zijn configuratie 'viewable' expliciet op false zet.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param object $plugin De plugin-instantie.
|
||||
* @return bool True indien de plugin viewable is.
|
||||
*/
|
||||
public function isPluginViewable(object $plugin): bool
|
||||
{
|
||||
if (method_exists($plugin, 'getConfig')) {
|
||||
@@ -261,6 +444,17 @@ class PluginManager
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Haal de samengevoegde sidebar-content van alle viewable plugins op.
|
||||
*
|
||||
* Elke plugin met een getSidebarContent()-methode levert HTML die wordt
|
||||
* ingepakt in een Bootstrap-card met de plugin-titel.
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param array<int,string>|null $allowedPlugins Optionele whitelist van plugin-namen.
|
||||
* @return string De samengevoegde sidebar-HTML.
|
||||
*/
|
||||
public function getSidebarContent(?array $allowedPlugins = null): string
|
||||
{
|
||||
$sidebarContent = '';
|
||||
@@ -300,9 +494,11 @@ class PluginManager
|
||||
}
|
||||
|
||||
/**
|
||||
* Get CSS URLs from all enabled plugins that provide getCssUrl().
|
||||
* Haal CSS-URL's op van alle ingeschakelde plugins die getCssUrl() implementeren.
|
||||
*
|
||||
* @return array List of CSS URLs
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @return array<int,string> Lijst met CSS-URL's.
|
||||
*/
|
||||
public function getPluginCssUrls(): array
|
||||
{
|
||||
@@ -319,10 +515,13 @@ class PluginManager
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect admin menu items from all enabled plugins that implement getAdminMenu().
|
||||
* Each plugin returns [['route' => 'plugin-name/action', 'label' => 'Label', 'icon' => 'bi-icon']].
|
||||
* Verzamel admin-menu-items van alle plugins die getAdminMenu() implementeren.
|
||||
*
|
||||
* @return array Admin menu items from all plugins
|
||||
* Elke plugin levert [['route' => 'plugin-name/action', 'label' => 'Label', 'icon' => 'bi-icon']].
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @return array<int,array<string,mixed>> Admin-menu-items van alle plugins.
|
||||
*/
|
||||
public function getAdminMenuItems(): array
|
||||
{
|
||||
@@ -341,12 +540,16 @@ class PluginManager
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch an admin route to a plugin that handles it.
|
||||
* Plugins implement handleAdminRoute(string $action): ?string (returns rendered HTML or null).
|
||||
* Dispatch een admin-route naar de plugin die deze afhandelt.
|
||||
*
|
||||
* @param string $pluginName Plugin name
|
||||
* @param string $action Action/sub-route within the plugin
|
||||
* @return string|null Rendered HTML, or null if plugin doesn't handle it
|
||||
* Plugins implementeren handleAdminRoute(string $action): ?string
|
||||
* (geeft gerenderde HTML of null terug).
|
||||
*
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param string $pluginName Plugin-naam.
|
||||
* @param string $action Actie/sub-route binnen de plugin.
|
||||
* @return string|null Gerenderde HTML, of null indien de plugin deze niet afhandelt.
|
||||
*/
|
||||
public function dispatchAdminRoute(string $pluginName, string $action): ?string
|
||||
{
|
||||
@@ -361,10 +564,12 @@ class PluginManager
|
||||
}
|
||||
|
||||
/**
|
||||
* Find which plugin handles a given admin route.
|
||||
* Bepaal welke plugin een opgegeven admin-route afhandelt.
|
||||
*
|
||||
* @param string $route The admin route (e.g. 'statistics' or 'statistics/details')
|
||||
* @return array|null ['plugin' => name, 'action' => action, 'permission' => required permission] or null
|
||||
* @since 2.6.4
|
||||
*
|
||||
* @param string $route De admin-route (bijv. 'statistics' of 'statistics/details').
|
||||
* @return array<string,mixed>|null ['plugin' => naam, 'action' => actie, 'permission' => vereiste permissie] of null.
|
||||
*/
|
||||
public function resolveAdminRoute(string $route): ?array
|
||||
{
|
||||
@@ -382,4 +587,4 @@ class PluginManager
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user