From c88e85d8aecb6b7ce056fb82b673852044abe98f Mon Sep 17 00:00:00 2001 From: Giulio Marcon Date: Wed, 20 May 2026 11:27:40 +0200 Subject: [PATCH 1/7] first implementation of custom modules installations statistics 1) telemetry server parameters 2) telemetry call 3) get statistics and make them available in the list of modules, allowing to sort --- resources/views/module_update.phtml | 9 ++ resources/views/settings.phtml | 40 ++++++ src/CustomModuleManager.php | 117 ++++++++++++++++++ .../CustomModuleUpdatePage.php | 17 ++- 4 files changed, 182 insertions(+), 1 deletion(-) diff --git a/resources/views/module_update.phtml b/resources/views/module_update.phtml index 800446e..bdaf15f 100644 --- a/resources/views/module_update.phtml +++ b/resources/views/module_update.phtml @@ -33,6 +33,7 @@ use Jefferson49\Webtrees\Module\CustomModuleManager\RequestHandlers\ReleaseNotes * @var array $module_names * @var bool $fetch_latest * @var string $modules_to_show + * @var array $telemetry_stats */ ?> @@ -78,6 +79,7 @@ use Jefferson49\Webtrees\Module\CustomModuleManager\RequestHandlers\ReleaseNotes null, null, null, + ['type' => 'num'], ['type' => 'html', 'searchable' => false], ['type' => 'html', 'searchable' => false], ['type' => 'html', 'searchable' => false], @@ -96,6 +98,7 @@ use Jefferson49\Webtrees\Module\CustomModuleManager\RequestHandlers\ReleaseNotes + @@ -240,6 +243,12 @@ use Jefferson49\Webtrees\Module\CustomModuleManager\RequestHandlers\ReleaseNotes + + + + 0 ? $install_count : '-' ?> + + diff --git a/resources/views/settings.phtml b/resources/views/settings.phtml index b26494d..d0f1f86 100644 --- a/resources/views/settings.phtml +++ b/resources/views/settings.phtml @@ -17,6 +17,9 @@ use Jefferson49\Webtrees\Module\CustomModuleManager\RequestHandlers\CustomModule * @var bool $php_extension_zip_missing * @var string $github_api_token * @var string $modules_to_show + * @var bool $telemetry_opt_in + * @var string $telemetry_url + * @var string $telemetry_key */ ?> @@ -113,6 +116,43 @@ use Jefferson49\Webtrees\Module\CustomModuleManager\RequestHandlers\CustomModule + +
+ +
+ +
+

+ +
+
+ + + +
+ I18N::translate('Opt-in to anonymous community telemetry'), 'name' => CustomModuleManager::PREF_TELEMETRY_OPT_IN, 'checked' => $telemetry_opt_in]) ?> +
+
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+

diff --git a/src/CustomModuleManager.php b/src/CustomModuleManager.php index 100a71b..a83c76c 100644 --- a/src/CustomModuleManager.php +++ b/src/CustomModuleManager.php @@ -52,11 +52,13 @@ use Fisharebest\Webtrees\Module\ModuleListTrait; use Fisharebest\Webtrees\Registry; use Fisharebest\Webtrees\Services\ModuleService; +use Fisharebest\Webtrees\Site; use Fisharebest\Webtrees\Validator; use Fisharebest\Webtrees\Session; use Fisharebest\Webtrees\Tree; use Fisharebest\Webtrees\View; use Fisharebest\Webtrees\Webtrees; +use GuzzleHttp\Client; use Jefferson49\Webtrees\Exceptions\GithubCommunicationError; use Jefferson49\Webtrees\Helpers\GithubService; use Jefferson49\Webtrees\Log\CustomModuleLogInterface; @@ -118,6 +120,13 @@ class CustomModuleManager extends AbstractModule implements public const PREF_SHOW_ALL = 'show_all_modules'; public const PREF_SHOW_INSTALLED = 'show_installed_modules'; public const PREF_SHOW_NOT_INSTALLED = 'show_not_installed_modules'; + public const PREF_TELEMETRY_OPT_IN = 'telemetry_opt_in'; + public const PREF_TELEMETRY_URL = 'telemetry_url'; + public const PREF_TELEMETRY_KEY = 'telemetry_key'; + + //Telemetry defaults + public const DEFAULT_TELEMETRY_URL = 'https://fzzgvxqcqngxbhohnxdv.supabase.co/rest/v1/rpc'; + public const DEFAULT_TELEMETRY_KEY = 'sb_publishable_LYDPMFM9k8j1S8c9Ba1MGw_2MGiQxzP'; public const PREF_SHOW_MENU_LIST_ITEM = 'show_menu_list_item'; public const PREF_LATEST_VERSION = 'latest'; public const PREF_IGNORE_VERSION = 'ignore'; @@ -481,6 +490,9 @@ public function getAdminAction(ServerRequestInterface $request): ResponseInterfa self::PREF_GITHUB_API_TOKEN => $this->getPreference(self::PREF_GITHUB_API_TOKEN, ''), self::PREF_MODULES_TO_SHOW => $this->getPreference(self::PREF_MODULES_TO_SHOW, self::PREF_SHOW_ALL), self::PREF_SHOW_MENU_LIST_ITEM => boolval($this->getPreference(self::PREF_SHOW_MENU_LIST_ITEM, '1')), + self::PREF_TELEMETRY_OPT_IN => boolval($this->getPreference(self::PREF_TELEMETRY_OPT_IN, '1')), + self::PREF_TELEMETRY_URL => $this->getPreference(self::PREF_TELEMETRY_URL, self::DEFAULT_TELEMETRY_URL), + self::PREF_TELEMETRY_KEY => $this->getPreference(self::PREF_TELEMETRY_KEY, self::DEFAULT_TELEMETRY_KEY), ] ); } @@ -498,12 +510,18 @@ public function postAdminAction(ServerRequestInterface $request): ResponseInterf $github_api_token = Validator::parsedBody($request)->string(self::PREF_GITHUB_API_TOKEN, ''); $modules_to_show = Validator::parsedBody($request)->string(self::PREF_MODULES_TO_SHOW, self::PREF_SHOW_ALL); $show_menu_list_item = Validator::parsedBody($request)->boolean(self::PREF_SHOW_MENU_LIST_ITEM, false); + $telemetry_opt_in = Validator::parsedBody($request)->boolean(self::PREF_TELEMETRY_OPT_IN, false); + $telemetry_url = Validator::parsedBody($request)->string(self::PREF_TELEMETRY_URL, self::DEFAULT_TELEMETRY_URL); + $telemetry_key = Validator::parsedBody($request)->string(self::PREF_TELEMETRY_KEY, self::DEFAULT_TELEMETRY_KEY); //Save the received settings to the user preferences if ($save === '1') { $this->setPreference(self::PREF_GITHUB_API_TOKEN, $github_api_token); $this->setPreference(self::PREF_MODULES_TO_SHOW, $modules_to_show); $this->setPreference(self::PREF_SHOW_MENU_LIST_ITEM, $show_menu_list_item ? '1' : '0'); + $this->setPreference(self::PREF_TELEMETRY_OPT_IN, $telemetry_opt_in ? '1' : '0'); + $this->setPreference(self::PREF_TELEMETRY_URL, $telemetry_url); + $this->setPreference(self::PREF_TELEMETRY_KEY, $telemetry_key); } //Finally, show a success message @@ -838,6 +856,105 @@ public static function rememberGithubCommunciationError(): bool { return false; } + /** + * Submit telemetry data to the community telemetry service. + * Sends the list of installed custom module names along with a site UUID. + * + * @param array $module_names Array of installed custom module names + * + * @return void + */ + public function submitTelemetry(array $module_names): void + { + $opt_in = boolval($this->getPreference(self::PREF_TELEMETRY_OPT_IN, '1')); + + if (!$opt_in) { + return; + } + + $telemetry_url = $this->getPreference(self::PREF_TELEMETRY_URL, self::DEFAULT_TELEMETRY_URL); + $telemetry_key = $this->getPreference(self::PREF_TELEMETRY_KEY, self::DEFAULT_TELEMETRY_KEY); + + $site_uuid = Site::getPreference('SITE_UUID'); + + if ($site_uuid === '') { + $site_uuid = Registry::idFactory()->uuid(); + Site::setPreference('SITE_UUID', $site_uuid); + } + + try { + $client = new Client(); + $client->post($telemetry_url . '/submit_telemetry', [ + 'timeout' => 3.0, + 'headers' => [ + 'Content-Type' => 'application/json', + 'apikey' => $telemetry_key, + 'Authorization' => 'Bearer ' . $telemetry_key, + ], + 'json' => [ + 'site_uuid' => $site_uuid, + 'modules_list' => array_values($module_names), + ], + ]); + } catch (Throwable) { + // Silently ignore any telemetry errors + } + } + + /** + * Fetch module installation statistics from the community telemetry service. + * + * @return array Mapping of module_name => active_installs count + */ + public function fetchTelemetryStatistics(): array + { + static $cached_stats = null; + + if ($cached_stats !== null) { + return $cached_stats; + } + + $opt_in = boolval($this->getPreference(self::PREF_TELEMETRY_OPT_IN, '1')); + + if (!$opt_in) { + $cached_stats = []; + return $cached_stats; + } + + $telemetry_url = $this->getPreference(self::PREF_TELEMETRY_URL, self::DEFAULT_TELEMETRY_URL); + $telemetry_key = $this->getPreference(self::PREF_TELEMETRY_KEY, self::DEFAULT_TELEMETRY_KEY); + + try { + $client = new Client(); + $response = $client->post($telemetry_url . '/get_module_statistics', [ + 'timeout' => 3.0, + 'headers' => [ + 'Content-Type' => 'application/json', + 'apikey' => $telemetry_key, + 'Authorization' => 'Bearer ' . $telemetry_key, + ], + 'json' => (object) [], + ]); + + $data = json_decode($response->getBody()->getContents(), true); + + $stats = []; + if (is_array($data)) { + foreach ($data as $entry) { + if (isset($entry['module_name'], $entry['active_installs'])) { + $stats[$entry['module_name']] = (int) $entry['active_installs']; + } + } + } + + $cached_stats = $stats; + } catch (Throwable) { + $cached_stats = []; + } + + return $cached_stats; + } + /** * Whether the current version is the latest version of the module * diff --git a/src/RequestHandlers/CustomModuleUpdatePage.php b/src/RequestHandlers/CustomModuleUpdatePage.php index 293634b..9dfc68e 100644 --- a/src/RequestHandlers/CustomModuleUpdatePage.php +++ b/src/RequestHandlers/CustomModuleUpdatePage.php @@ -76,15 +76,30 @@ public function handle(ServerRequestInterface $request): ResponseInterface /** @var CustomModuleManager $custom_module_manager To avoid IDE warnings */ $custom_module_manager = $module_service->findByName(module_name: CustomModuleManager::activeModuleName()); + $module_names = ModuleUpdateServiceConfiguration::getModuleNames(); + + // Submit telemetry when checking for updates + if ($fetch_latest && $custom_module_manager !== null) { + $installed_modules = $module_service->findByInterface(ModuleCustomInterface::class, true) + ->map(static fn ($module) => $module->name()) + ->values() + ->all(); + $custom_module_manager->submitTelemetry($installed_modules); + } + + // Fetch telemetry statistics + $telemetry_stats = $custom_module_manager !== null ? $custom_module_manager->fetchTelemetryStatistics() : []; + return $this->viewResponse(CustomModuleManager::viewsNamespace() . '::module_update', [ 'title' => I18N::translate('Custom Module Updates'), 'custom_module_manager' => $custom_module_manager, 'module_service' => $module_service, - 'module_names' => ModuleUpdateServiceConfiguration::getModuleNames(), + 'module_names' => $module_names, 'custom_modules' => $module_service->findByInterface(ModuleCustomInterface::class, true), 'themes' => $module_service->findByInterface(ModuleThemeInterface::class, true), 'fetch_latest' => $fetch_latest, 'modules_to_show' => $custom_module_manager->getPreference(CustomModuleManager::PREF_MODULES_TO_SHOW, CustomModuleManager::PREF_SHOW_ALL), + 'telemetry_stats' => $telemetry_stats, ]); } } From 88d5456198fc38781ce60d7f3705c8175dcd7268 Mon Sep 17 00:00:00 2001 From: Giulio Marcon Date: Wed, 20 May 2026 11:52:35 +0200 Subject: [PATCH 2/7] installation statistics: add information about errors and add debugging information about successfull calls --- src/CustomModuleManager.php | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/CustomModuleManager.php b/src/CustomModuleManager.php index a83c76c..2c0136b 100644 --- a/src/CustomModuleManager.php +++ b/src/CustomModuleManager.php @@ -884,7 +884,7 @@ public function submitTelemetry(array $module_names): void try { $client = new Client(); - $client->post($telemetry_url . '/submit_telemetry', [ + $response = $client->post($telemetry_url . '/submit_telemetry', [ 'timeout' => 3.0, 'headers' => [ 'Content-Type' => 'application/json', @@ -896,8 +896,12 @@ public function submitTelemetry(array $module_names): void 'modules_list' => array_values($module_names), ], ]); - } catch (Throwable) { - // Silently ignore any telemetry errors + + if ($this->debuggingActivated()) { + FlashMessages::addMessage('Telemetry submit: HTTP ' . $response->getStatusCode() . ' — ' . $response->getBody()->getContents(), 'info'); + } + } catch (Throwable $ex) { + FlashMessages::addMessage('Telemetry submit error: ' . $ex->getMessage(), 'danger'); } } @@ -936,7 +940,13 @@ public function fetchTelemetryStatistics(): array 'json' => (object) [], ]); - $data = json_decode($response->getBody()->getContents(), true); + $body = $response->getBody()->getContents(); + + if ($this->debuggingActivated()) { + FlashMessages::addMessage('Telemetry stats: HTTP ' . $response->getStatusCode() . ' — ' . substr($body, 0, 500), 'info'); + } + + $data = json_decode($body, true); $stats = []; if (is_array($data)) { @@ -948,7 +958,8 @@ public function fetchTelemetryStatistics(): array } $cached_stats = $stats; - } catch (Throwable) { + } catch (Throwable $ex) { + FlashMessages::addMessage('Telemetry stats error: ' . $ex->getMessage(), 'danger'); $cached_stats = []; } From 884614b951b963616a4795ffe003925bf80fa7a7 Mon Sep 17 00:00:00 2001 From: Giulio Marcon Date: Wed, 20 May 2026 12:05:34 +0200 Subject: [PATCH 3/7] installation statistics: enhanced flash messages, fixed sql function parameter names --- src/CustomModuleManager.php | 58 ++++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 13 deletions(-) diff --git a/src/CustomModuleManager.php b/src/CustomModuleManager.php index 2c0136b..53ec10a 100644 --- a/src/CustomModuleManager.php +++ b/src/CustomModuleManager.php @@ -59,6 +59,7 @@ use Fisharebest\Webtrees\View; use Fisharebest\Webtrees\Webtrees; use GuzzleHttp\Client; +use GuzzleHttp\Exception\RequestException; use Jefferson49\Webtrees\Exceptions\GithubCommunicationError; use Jefferson49\Webtrees\Helpers\GithubService; use Jefferson49\Webtrees\Log\CustomModuleLogInterface; @@ -882,6 +883,11 @@ public function submitTelemetry(array $module_names): void Site::setPreference('SITE_UUID', $site_uuid); } + $request_body = json_encode([ + 'p_site_uuid' => $site_uuid, + 'p_modules_list' => array_values($module_names), + ]); + try { $client = new Client(); $response = $client->post($telemetry_url . '/submit_telemetry', [ @@ -891,17 +897,28 @@ public function submitTelemetry(array $module_names): void 'apikey' => $telemetry_key, 'Authorization' => 'Bearer ' . $telemetry_key, ], - 'json' => [ - 'site_uuid' => $site_uuid, - 'modules_list' => array_values($module_names), - ], + 'body' => $request_body, ]); - if ($this->debuggingActivated()) { - FlashMessages::addMessage('Telemetry submit: HTTP ' . $response->getStatusCode() . ' — ' . $response->getBody()->getContents(), 'info'); - } + $response_body = $response->getBody()->getContents(); + FlashMessages::addMessage( + 'Telemetry submit OK: HTTP ' . $response->getStatusCode() . + '
Request body: ' . e($request_body) . '' . + '
Response body: ' . e($response_body) . '', + 'info' + ); } catch (Throwable $ex) { - FlashMessages::addMessage('Telemetry submit error: ' . $ex->getMessage(), 'danger'); + $error_detail = $ex->getMessage(); + if ($ex instanceof RequestException && $ex->hasResponse()) { + $error_detail = $ex->getResponse()->getBody()->getContents(); + } + error_log('CustomModuleManager telemetry submit error: ' . $error_detail); + FlashMessages::addMessage( + 'Telemetry submit error:' . + '
Request body: ' . e($request_body) . '' . + '
Error: ' . e($error_detail) . '', + 'danger' + ); } } @@ -928,6 +945,8 @@ public function fetchTelemetryStatistics(): array $telemetry_url = $this->getPreference(self::PREF_TELEMETRY_URL, self::DEFAULT_TELEMETRY_URL); $telemetry_key = $this->getPreference(self::PREF_TELEMETRY_KEY, self::DEFAULT_TELEMETRY_KEY); + $request_body = json_encode((object) []); + try { $client = new Client(); $response = $client->post($telemetry_url . '/get_module_statistics', [ @@ -937,14 +956,17 @@ public function fetchTelemetryStatistics(): array 'apikey' => $telemetry_key, 'Authorization' => 'Bearer ' . $telemetry_key, ], - 'json' => (object) [], + 'body' => $request_body, ]); $body = $response->getBody()->getContents(); - if ($this->debuggingActivated()) { - FlashMessages::addMessage('Telemetry stats: HTTP ' . $response->getStatusCode() . ' — ' . substr($body, 0, 500), 'info'); - } + FlashMessages::addMessage( + 'Telemetry stats OK: HTTP ' . $response->getStatusCode() . + '
Request body: ' . e($request_body) . '' . + '
Response body: ' . e($body) . '', + 'info' + ); $data = json_decode($body, true); @@ -959,7 +981,17 @@ public function fetchTelemetryStatistics(): array $cached_stats = $stats; } catch (Throwable $ex) { - FlashMessages::addMessage('Telemetry stats error: ' . $ex->getMessage(), 'danger'); + $error_detail = $ex->getMessage(); + if ($ex instanceof RequestException && $ex->hasResponse()) { + $error_detail = $ex->getResponse()->getBody()->getContents(); + } + error_log('CustomModuleManager telemetry stats error: ' . $error_detail); + FlashMessages::addMessage( + 'Telemetry stats error:' . + '
Request body: ' . e($request_body) . '' . + '
Error: ' . e($error_detail) . '', + 'danger' + ); $cached_stats = []; } From 8a82f186f970f28e6b5f864c23e97dbcaee3137c Mon Sep 17 00:00:00 2001 From: Giulio Marcon Date: Wed, 20 May 2026 12:19:33 +0200 Subject: [PATCH 4/7] installation statistics: better to use the config key name instead of the module names that depend on the folder name --- resources/views/module_update.phtml | 6 +++++- src/CustomModuleManager.php | 9 ++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/resources/views/module_update.phtml b/resources/views/module_update.phtml index bdaf15f..d224969 100644 --- a/resources/views/module_update.phtml +++ b/resources/views/module_update.phtml @@ -244,7 +244,11 @@ use Jefferson49\Webtrees\Module\CustomModuleManager\RequestHandlers\ReleaseNotes - + 0 ? $install_count : '-' ?> diff --git a/src/CustomModuleManager.php b/src/CustomModuleManager.php index 53ec10a..fc2bf86 100644 --- a/src/CustomModuleManager.php +++ b/src/CustomModuleManager.php @@ -883,9 +883,16 @@ public function submitTelemetry(array $module_names): void Site::setPreference('SITE_UUID', $site_uuid); } + // Resolve folder-based module names to standard config key names (globally unique) + $module_identifiers = []; + foreach ($module_names as $module_name) { + $standard_name = ModuleUpdateServiceConfiguration::getStandardModuleName($module_name); + $module_identifiers[] = $standard_name !== '' ? $standard_name : $module_name; + } + $request_body = json_encode([ 'p_site_uuid' => $site_uuid, - 'p_modules_list' => array_values($module_names), + 'p_modules_list' => $module_identifiers, ]); try { From dabff4950aacd1f529a47a6c209bf4be7cbba5e2 Mon Sep 17 00:00:00 2001 From: Giulio Marcon Date: Wed, 20 May 2026 12:41:34 +0200 Subject: [PATCH 5/7] installation statitics: do not show column in table if not opted in --- resources/views/module_update.phtml | 11 ++++++++--- src/RequestHandlers/CustomModuleUpdatePage.php | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/resources/views/module_update.phtml b/resources/views/module_update.phtml index d224969..45072ad 100644 --- a/resources/views/module_update.phtml +++ b/resources/views/module_update.phtml @@ -33,6 +33,7 @@ use Jefferson49\Webtrees\Module\CustomModuleManager\RequestHandlers\ReleaseNotes * @var array $module_names * @var bool $fetch_latest * @var string $modules_to_show + * @var bool $telemetry_opt_in * @var array $telemetry_stats */ @@ -71,7 +72,7 @@ use Jefferson49\Webtrees\Module\CustomModuleManager\RequestHandlers\ReleaseNotes data-info="false" data-paging="false" data-filter="false" - data-columns=" 'html'], ['type' => 'html'], null, @@ -79,11 +80,11 @@ use Jefferson49\Webtrees\Module\CustomModuleManager\RequestHandlers\ReleaseNotes null, null, null, - ['type' => 'num'], + ], $telemetry_opt_in ? [['type' => 'num']] : [], [ ['type' => 'html', 'searchable' => false], ['type' => 'html', 'searchable' => false], ['type' => 'html', 'searchable' => false], - ], JSON_THROW_ON_ERROR)) ?>" + ]), JSON_THROW_ON_ERROR)) ?>" > @@ -98,7 +99,9 @@ use Jefferson49\Webtrees\Module\CustomModuleManager\RequestHandlers\ReleaseNotes + + @@ -244,6 +247,7 @@ use Jefferson49\Webtrees\Module\CustomModuleManager\RequestHandlers\ReleaseNotes + "> 0 ? $install_count : '-' ?> + diff --git a/src/RequestHandlers/CustomModuleUpdatePage.php b/src/RequestHandlers/CustomModuleUpdatePage.php index 9dfc68e..9b55d7c 100644 --- a/src/RequestHandlers/CustomModuleUpdatePage.php +++ b/src/RequestHandlers/CustomModuleUpdatePage.php @@ -99,6 +99,7 @@ public function handle(ServerRequestInterface $request): ResponseInterface 'themes' => $module_service->findByInterface(ModuleThemeInterface::class, true), 'fetch_latest' => $fetch_latest, 'modules_to_show' => $custom_module_manager->getPreference(CustomModuleManager::PREF_MODULES_TO_SHOW, CustomModuleManager::PREF_SHOW_ALL), + 'telemetry_opt_in' => boolval($custom_module_manager->getPreference(CustomModuleManager::PREF_TELEMETRY_OPT_IN, '1')), 'telemetry_stats' => $telemetry_stats, ]); } From 574908d09867a8e987ac33665d40ce22c5cf3671 Mon Sep 17 00:00:00 2001 From: Giulio Marcon Date: Wed, 20 May 2026 12:46:13 +0200 Subject: [PATCH 6/7] installation statitics: disclosure about data collection in README --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index ca8b9f4..9ffe6a9 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,20 @@ The module allows to provide a GitHub API token in the module settings. In order + Do NOT select any options + Press "Generate token" button at the bottom of the page +## Community Telemetry + +To help the community understand which custom modules are most actively used, this module includes a telemetry feature to collect aggregate data on module installations. + +This feature is enabled by default to help build accurate statistics, but can be easily opted out of by the site administrator in the module configuration. + +While enabled, the module will securely send an anonymous list of installed module names to a centralized database during the normal version-check routine. This data powers a feature in the Custom Module Manager, allowing you to see the global number of active installations for each module directly in the directory. + +Data Privacy Guarantees: + ++ Easy Opt-Out: Telemetry can be disabled at any time in the module settings. Once opted out, no data is sent. ++ Fully Anonymous: The system does not collect URLs, IP addresses, admin emails, or core database contents. Sites are identified only by a random site ID (the exact same ID used by the core webtrees installation to anonymously report PHP and database versions). ++ Ecosystem Only: The only data transmitted is an array of strings representing the internal names of installed custom modules. Not even the local folder names are sent. + ## How to use the module? + Go to "Control Panel/All Modules" and find the "Custom Module Manager" module From 951a2a09d1acaaf842e5118f78343e32a64f214f Mon Sep 17 00:00:00 2001 From: Giulio Marcon Date: Wed, 20 May 2026 12:49:00 +0200 Subject: [PATCH 7/7] installation statitics: disclosure about data collection in settings --- resources/views/settings.phtml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/resources/views/settings.phtml b/resources/views/settings.phtml index d0f1f86..0d1b041 100644 --- a/resources/views/settings.phtml +++ b/resources/views/settings.phtml @@ -131,6 +131,9 @@ use Jefferson49\Webtrees\Module\CustomModuleManager\RequestHandlers\CustomModule
I18N::translate('Opt-in to anonymous community telemetry'), 'name' => CustomModuleManager::PREF_TELEMETRY_OPT_IN, 'checked' => $telemetry_opt_in]) ?> +
+ +