summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/Http/Controllers/AbstractBaseController.php26
-rw-r--r--app/Http/Controllers/Admin/AbstractAdminController.php29
-rw-r--r--app/Http/Controllers/Admin/AnalyticsController.php91
-rw-r--r--app/Http/Controllers/AdminSiteController.php34
-rw-r--r--app/Module.php128
-rw-r--r--app/Module/AbstractModule.php12
-rw-r--r--app/Module/AhnentafelReportModule.php2
-rw-r--r--app/Module/AlbumModule.php2
-rw-r--r--app/Module/AncestorsChartModule.php2
-rw-r--r--app/Module/BatchUpdateModule.php2
-rw-r--r--app/Module/BingWebmasterToolsModule.php79
-rw-r--r--app/Module/BirthDeathMarriageReportModule.php2
-rw-r--r--app/Module/BirthReportModule.php2
-rw-r--r--app/Module/CalendarMenuModule.php2
-rw-r--r--app/Module/CemeteryReportModule.php2
-rw-r--r--app/Module/ChangeReportModule.php2
-rw-r--r--app/Module/ChartsBlockModule.php2
-rw-r--r--app/Module/ChartsMenuModule.php2
-rw-r--r--app/Module/ClippingsCartModule.php2
-rw-r--r--app/Module/CompactTreeChartModule.php2
-rw-r--r--app/Module/DeathReportModule.php2
-rw-r--r--app/Module/DescendancyChartModule.php2
-rw-r--r--app/Module/DescendancyModule.php2
-rw-r--r--app/Module/DescendancyReportModule.php2
-rw-r--r--app/Module/ExtraInformationModule.php2
-rw-r--r--app/Module/FactSourcesReportModule.php2
-rw-r--r--app/Module/FamilyBookChartModule.php2
-rw-r--r--app/Module/FamilyGroupReportModule.php2
-rw-r--r--app/Module/FamilyNavigatorModule.php2
-rw-r--r--app/Module/FamilyTreeFavoritesModule.php2
-rw-r--r--app/Module/FamilyTreeNewsModule.php2
-rw-r--r--app/Module/FamilyTreeStatisticsModule.php2
-rw-r--r--app/Module/FanChartModule.php2
-rw-r--r--app/Module/FrequentlyAskedQuestionsModule.php2
-rw-r--r--app/Module/GoogleAnalyticsModule.php92
-rw-r--r--app/Module/GoogleWebmasterToolsModule.php79
-rw-r--r--app/Module/HourglassChartModule.php2
-rw-r--r--app/Module/HtmlBlockModule.php2
-rw-r--r--app/Module/IndividualFactsTabModule.php2
-rw-r--r--app/Module/IndividualFamiliesReportModule.php2
-rw-r--r--app/Module/IndividualReportModule.php2
-rw-r--r--app/Module/InteractiveTreeModule.php2
-rw-r--r--app/Module/LifespansChartModule.php2
-rw-r--r--app/Module/ListsMenuModule.php2
-rw-r--r--app/Module/LoggedInUsersModule.php2
-rw-r--r--app/Module/LoginBlockModule.php2
-rw-r--r--app/Module/MarriageReportModule.php2
-rw-r--r--app/Module/MatomoAnalyticsModule.php80
-rw-r--r--app/Module/MediaTabModule.php2
-rw-r--r--app/Module/MissingFactsReportModule.php2
-rw-r--r--app/Module/ModuleAnalyticsInterface.php63
-rw-r--r--app/Module/ModuleAnalyticsTrait.php95
-rw-r--r--app/Module/ModuleBlockInterface.php2
-rw-r--r--app/Module/ModuleChartInterface.php2
-rw-r--r--app/Module/ModuleChartTrait.php5
-rw-r--r--app/Module/ModuleConfigInterface.php2
-rw-r--r--app/Module/ModuleConfigTrait.php5
-rw-r--r--app/Module/ModuleCustomInterface.php2
-rw-r--r--app/Module/ModuleInterface.php27
-rw-r--r--app/Module/ModuleMenuInterface.php2
-rw-r--r--app/Module/ModuleReportInterface.php2
-rw-r--r--app/Module/ModuleSidebarInterface.php2
-rw-r--r--app/Module/ModuleTabInterface.php2
-rw-r--r--app/Module/ModuleThemeInterface.php2
-rw-r--r--app/Module/NotesTabModule.php2
-rw-r--r--app/Module/OccupationReportModule.php2
-rw-r--r--app/Module/OnThisDayModule.php2
-rw-r--r--app/Module/PedigreeChartModule.php2
-rw-r--r--app/Module/PedigreeMapModule.php2
-rw-r--r--app/Module/PedigreeReportModule.php2
-rw-r--r--app/Module/PlacesModule.php2
-rw-r--r--app/Module/RecentChangesModule.php2
-rw-r--r--app/Module/RelatedIndividualsReportModule.php2
-rw-r--r--app/Module/RelationshipsChartModule.php2
-rw-r--r--app/Module/RelativesTabModule.php2
-rw-r--r--app/Module/ReportsMenuModule.php2
-rw-r--r--app/Module/ResearchTaskModule.php2
-rw-r--r--app/Module/ReviewChangesModule.php2
-rw-r--r--app/Module/SearchMenuModule.php2
-rw-r--r--app/Module/SiteMapModule.php2
-rw-r--r--app/Module/SlideShowModule.php2
-rw-r--r--app/Module/SourcesTabModule.php2
-rw-r--r--app/Module/StatcounterModule.php81
-rw-r--r--app/Module/StatisticsChartModule.php2
-rw-r--r--app/Module/StoriesModule.php2
-rw-r--r--app/Module/ThemeSelectModule.php2
-rw-r--r--app/Module/TimelineChartModule.php2
-rw-r--r--app/Module/TopGivenNamesModule.php2
-rw-r--r--app/Module/TopPageViewsModule.php2
-rw-r--r--app/Module/TopSurnamesModule.php2
-rw-r--r--app/Module/TreesMenuModule.php2
-rw-r--r--app/Module/UpcomingAnniversariesModule.php2
-rw-r--r--app/Module/UserFavoritesModule.php2
-rw-r--r--app/Module/UserJournalModule.php2
-rw-r--r--app/Module/UserMessagesModule.php2
-rw-r--r--app/Module/UserWelcomeModule.php2
-rw-r--r--app/Module/WelcomeBlockModule.php2
-rw-r--r--app/Module/YahrzeitModule.php2
-rw-r--r--app/Theme/AbstractTheme.php138
-rw-r--r--app/Theme/ThemeInterface.php7
-rw-r--r--modules_v4/custom.example/module.php2
-rw-r--r--resources/views/admin/analytics/bing-webmaster-form.phtml12
-rw-r--r--resources/views/admin/analytics/bing-webmaster-snippet.phtml1
-rw-r--r--resources/views/admin/analytics/edit.phtml35
-rw-r--r--resources/views/admin/analytics/google-analytics-form.phtml11
-rw-r--r--resources/views/admin/analytics/google-analytics-snippet.phtml7
-rw-r--r--resources/views/admin/analytics/google-webmaster-form.phtml12
-rw-r--r--resources/views/admin/analytics/google-webmaster-snippet.phtml1
-rw-r--r--resources/views/admin/analytics/index.phtml36
-rw-r--r--resources/views/admin/analytics/matomo-analytics-form.phtml21
-rw-r--r--resources/views/admin/analytics/matomo-analytics-snippet.phtml12
-rw-r--r--resources/views/admin/analytics/statcounter-form.phtml20
-rw-r--r--resources/views/admin/analytics/statcounter-snippet.phtml9
-rw-r--r--resources/views/admin/control-panel.phtml4
-rw-r--r--resources/views/admin/site-analytics.phtml136
-rw-r--r--resources/views/icons/help.phtml2
-rw-r--r--resources/views/icons/information.phtml1
-rw-r--r--resources/views/layouts/default.phtml4
-rw-r--r--routes/web.php5
119 files changed, 1075 insertions, 491 deletions
diff --git a/app/Http/Controllers/AbstractBaseController.php b/app/Http/Controllers/AbstractBaseController.php
index 8a3e3b2ece..46a2a12e89 100644
--- a/app/Http/Controllers/AbstractBaseController.php
+++ b/app/Http/Controllers/AbstractBaseController.php
@@ -17,33 +17,7 @@ declare(strict_types=1);
namespace Fisharebest\Webtrees\Http\Controllers;
-use Fisharebest\Webtrees\Exceptions\FamilyAccessDeniedException;
-use Fisharebest\Webtrees\Exceptions\FamilyNotFoundException;
-use Fisharebest\Webtrees\Exceptions\IndividualAccessDeniedException;
-use Fisharebest\Webtrees\Exceptions\IndividualNotFoundException;
-use Fisharebest\Webtrees\Exceptions\MediaAccessDeniedException;
-use Fisharebest\Webtrees\Exceptions\MediaNotFoundException;
-use Fisharebest\Webtrees\Exceptions\NoteAccessDeniedException;
-use Fisharebest\Webtrees\Exceptions\NoteNotFoundException;
-use Fisharebest\Webtrees\Exceptions\RecordAccessDeniedException;
-use Fisharebest\Webtrees\Exceptions\RecordNotFoundException;
-use Fisharebest\Webtrees\Exceptions\RepositoryAccessDeniedException;
-use Fisharebest\Webtrees\Exceptions\RepositoryNotFoundException;
-use Fisharebest\Webtrees\Exceptions\SourceAccessDeniedException;
-use Fisharebest\Webtrees\Exceptions\SourceNotFoundException;
-use Fisharebest\Webtrees\Family;
-use Fisharebest\Webtrees\GedcomRecord;
-use Fisharebest\Webtrees\I18N;
-use Fisharebest\Webtrees\Individual;
-use Fisharebest\Webtrees\Media;
-use Fisharebest\Webtrees\Module;
-use Fisharebest\Webtrees\Module\ModuleChartInterface;
-use Fisharebest\Webtrees\Note;
-use Fisharebest\Webtrees\Repository;
-use Fisharebest\Webtrees\Source;
-use Fisharebest\Webtrees\Tree;
use Symfony\Component\HttpFoundation\Response;
-use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Common functions for all controllers
diff --git a/app/Http/Controllers/Admin/AbstractAdminController.php b/app/Http/Controllers/Admin/AbstractAdminController.php
new file mode 100644
index 0000000000..013f89cf9f
--- /dev/null
+++ b/app/Http/Controllers/Admin/AbstractAdminController.php
@@ -0,0 +1,29 @@
+<?php
+/**
+ * webtrees: online genealogy
+ * Copyright (C) 2019 webtrees development team
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+declare(strict_types=1);
+
+namespace Fisharebest\Webtrees\Http\Controllers\Admin;
+
+use Fisharebest\Webtrees\Http\Controllers\AbstractBaseController;
+
+/**
+ * Common functions for admin controllers.
+ */
+abstract class AbstractAdminController extends AbstractBaseController
+{
+ /** @var string */
+ protected $layout = 'layouts/administration';
+}
diff --git a/app/Http/Controllers/Admin/AnalyticsController.php b/app/Http/Controllers/Admin/AnalyticsController.php
new file mode 100644
index 0000000000..f60feed569
--- /dev/null
+++ b/app/Http/Controllers/Admin/AnalyticsController.php
@@ -0,0 +1,91 @@
+<?php
+/**
+ * webtrees: online genealogy
+ * Copyright (C) 2019 webtrees development team
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+declare(strict_types=1);
+
+namespace Fisharebest\Webtrees\Http\Controllers\Admin;
+
+use Fisharebest\Webtrees\FlashMessages;
+use Fisharebest\Webtrees\I18N;
+use Fisharebest\Webtrees\Module;
+use Fisharebest\Webtrees\Module\ModuleAnalyticsInterface;
+use Illuminate\Support\Collection;
+use Symfony\Component\HttpFoundation\RedirectResponse;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+
+/**
+ * Controller for configuring the tracking and analytics modules.
+ */
+class AnalyticsController extends AbstractAdminController
+{
+ /**
+ * @return Response
+ */
+ public function list(): Response
+ {
+ /* I18N: e.g. http://www.google.com/analytics */
+ $title = I18N::translate('Tracking and analytics');
+
+ return $this->viewResponse('admin/analytics/index', [
+ 'modules' => Module::findByInterface(ModuleAnalyticsInterface::class),
+ 'title' => $title,
+ ]);
+ }
+
+ /**
+ * @param Request $request
+ *
+ * @return Response
+ */
+ public function edit(Request $request): Response
+ {
+ $module_name = $request->get('module');
+ $module = Module::findByName($module_name);
+
+ if ($module instanceof ModuleAnalyticsInterface) {
+ return $this->viewResponse('admin/analytics/edit', [
+ 'module_name' => $module_name,
+ 'form_fields' => $module->analyticsFormFields(),
+ 'preview' => $module->analyticsSnippet($module->analyticsParameters()),
+ 'title' => $module->title(),
+ ]);
+ }
+
+ return new RedirectResponse(route('analytics'));
+ }
+
+ /**
+ * @param Request $request
+ *
+ * @return RedirectResponse
+ */
+ public function save(Request $request): RedirectResponse
+ {
+ $module_name = $request->get('module', '');
+
+ $module = Module::findByName($module_name);
+
+ if ($module instanceof ModuleAnalyticsInterface) {
+ foreach (array_keys($module->analyticsParameters()) as $setting_name) {
+ $module->setPreference($setting_name, $request->get($setting_name, ''));
+ }
+
+ FlashMessages::addMessage(I18N::translate('The preferences for the module “%s” have been updated.', $module->title()), 'success');
+ }
+
+ return new RedirectResponse(route('analytics'));
+ }
+}
diff --git a/app/Http/Controllers/AdminSiteController.php b/app/Http/Controllers/AdminSiteController.php
index ea4c72ab7c..44ab3a89bb 100644
--- a/app/Http/Controllers/AdminSiteController.php
+++ b/app/Http/Controllers/AdminSiteController.php
@@ -46,40 +46,6 @@ class AdminSiteController extends AbstractBaseController
protected $layout = 'layouts/administration';
/**
- * @return Response
- */
- public function analyticsForm(): Response
- {
- /* I18N: e.g. http://www.google.com/analytics */
- $title = I18N::translate('Tracking and analytics');
-
- return $this->viewResponse('admin/site-analytics', [
- 'title' => $title,
- ]);
- }
-
- /**
- * @param Request $request
- *
- * @return RedirectResponse
- */
- public function analyticsSave(Request $request): RedirectResponse
- {
- Site::setPreference('BING_WEBMASTER_ID', $request->get('BING_WEBMASTER_ID'));
- Site::setPreference('GOOGLE_WEBMASTER_ID', $request->get('GOOGLE_WEBMASTER_ID'));
- Site::setPreference('GOOGLE_ANALYTICS_ID', $request->get('GOOGLE_ANALYTICS_ID'));
- Site::setPreference('PIWIK_URL', $request->get('PIWIK_URL'));
- Site::setPreference('PIWIK_SITE_ID', $request->get('PIWIK_SITE_ID'));
- Site::setPreference('STATCOUNTER_PROJECT_ID', $request->get('STATCOUNTER_PROJECT_ID'));
- Site::setPreference('STATCOUNTER_SECURITY_ID', $request->get('STATCOUNTER_SECURITY_ID'));
-
- FlashMessages::addMessage(I18N::translate('The website preferences have been updated.'), 'success');
- $url = route('admin-control-panel');
-
- return new RedirectResponse($url);
- }
-
- /**
* Show old user files in the data folder.
*
* @param Filesystem $filesystem
diff --git a/app/Module.php b/app/Module.php
index 20bcda9964..c0877ce531 100644
--- a/app/Module.php
+++ b/app/Module.php
@@ -18,6 +18,10 @@ declare(strict_types=1);
namespace Fisharebest\Webtrees;
use Closure;
+use Fisharebest\Webtrees\Module\BingWebmasterToolsModule;
+use Fisharebest\Webtrees\Module\GoogleAnalyticsModule;
+use Fisharebest\Webtrees\Module\GoogleWebmasterToolsModule;
+use Fisharebest\Webtrees\Module\MatomoAnalyticsModule;
use Fisharebest\Webtrees\Module\ModuleBlockInterface;
use Fisharebest\Webtrees\Module\ModuleChartInterface;
use Fisharebest\Webtrees\Module\ModuleInterface;
@@ -25,6 +29,7 @@ use Fisharebest\Webtrees\Module\ModuleMenuInterface;
use Fisharebest\Webtrees\Module\ModuleReportInterface;
use Fisharebest\Webtrees\Module\ModuleSidebarInterface;
use Fisharebest\Webtrees\Module\ModuleTabInterface;
+use Fisharebest\Webtrees\Module\StatcounterModule;
use Illuminate\Database\Capsule\Manager as DB;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
@@ -126,16 +131,75 @@ class Module
];
/**
- * All modules.
+ * All core modules in the system.
*
- * @return Collection|ModuleInterface[]
+ * @return Collection
*/
- public static function all(): Collection
+ private static function coreModules(): Collection
+ {
+ // Array keys are module names, and should match module names from earlier versions of webtrees.
+ $modules = new Collection([
+ 'bing-webmaster-tools' => BingWebmasterToolsModule::class,
+ 'google-analytics' => GoogleAnalyticsModule::class,
+ 'google-webmaster-tools' => GoogleWebmasterToolsModule::class,
+ 'matomo-analytics' => MatomoAnalyticsModule::class,
+ 'statcounter' => StatcounterModule::class,
+ ]);
+
+ return $modules->map(function (string $class, string $name): ModuleInterface {
+ $module = app()->make($class);
+
+ $module->setName($name);
+
+ return $module;
+ });
+ }
+
+ /**
+ * All custom modules in the system. Custom modules are defined in modules_v4/
+ *
+ * @return Collection
+ */
+ private static function customModules(): Collection
{
$pattern = WT_ROOT . Webtrees::MODULES_PATH . '*/module.php';
$filenames = glob($pattern);
- return app('cache.array')->rememberForever('all_modules', function () use ($filenames): Collection {
+ return (new Collection($filenames))
+ ->filter(function (string $filename): bool {
+ // Special characters will break PHP variable names.
+ // This also allows us to ignore modules called "foo.example" and "foo.disable"
+ return !Str::contains(basename(dirname($filename)), ['.', ' ', '[', ']']);
+ })
+ ->map(function (string $filename): ?ModuleInterface {
+ try {
+ $module = self::load($filename);
+
+ if ($module instanceof ModuleInterface) {
+ $module_name = 'custom-' . basename(dirname($filename));
+
+ $module->setName($module_name);
+ }
+
+ return $module;
+ } catch (Throwable $ex) {
+ $message = '<pre>' . e($ex->getMessage()) . "\n" . e($ex->getTraceAsString()) . '</pre>';
+ FlashMessages::addMessage($message, 'danger');
+
+ return null;
+ }
+ })
+ ->filter();
+ }
+
+ /**
+ * All modules.
+ *
+ * @return Collection|ModuleInterface[]
+ */
+ public static function all(): Collection
+ {
+ return app('cache.array')->rememberForever('all_modules', function (): Collection {
// Modules have a default status, order etc.
// We can override these from database settings.
$module_info = DB::table('module')
@@ -144,49 +208,31 @@ class Module
return [$row->module_name => $row];
});
- return (new Collection($filenames))
- ->filter(function (string $filename): bool {
- // Module names with dots break things.
- return !Str::contains(basename(dirname($filename)), '.');
- })
- ->map(function (string $filename) use ($module_info): ?ModuleInterface {
- try {
- $module_name = basename(dirname($filename));
- $module = self::load($filename);
+ return self::coreModules()
+ ->merge(self::customModules())
+ ->map(function (ModuleInterface $module) use ($module_info): ModuleInterface {
+ $info = $module_info->get($module->name());
- if ($module instanceof ModuleInterface) {
- $module->setName($module_name);
- }
-
- $info = $module_info->get($module_name);
-
- if ($info instanceof stdClass) {
- $module->setEnabled($info->status === 'enabled');
-
- if ($module instanceof ModuleMenuInterface && $info->menu_order !== null) {
- $module->setMenuOrder((int) $info->menu_order);
- }
-
- if ($module instanceof ModuleSidebarInterface && $info->sidebar_order !== null) {
- $module->setSidebarOrder((int) $info->sidebar_order);
- }
+ if ($info instanceof stdClass) {
+ $module->setEnabled($info->status === 'enabled');
- if ($module instanceof ModuleTabInterface && $info->tab_order !== null) {
- $module->setTabOrder((int) $info->tab_order);
- }
- } else {
- DB::table('module')->insert(['module_name' => $module_name]);
+ if ($module instanceof ModuleMenuInterface && $info->menu_order !== null) {
+ $module->setMenuOrder((int) $info->menu_order);
}
- return $module;
- } catch (Throwable $ex) {
- $message = '<pre>' . e($ex->getMessage()) . "\n" . e($ex->getTraceAsString()) . '</pre>';
- FlashMessages::addMessage($message, 'danger');
+ if ($module instanceof ModuleSidebarInterface && $info->sidebar_order !== null) {
+ $module->setSidebarOrder((int) $info->sidebar_order);
+ }
- return null;
+ if ($module instanceof ModuleTabInterface && $info->tab_order !== null) {
+ $module->setTabOrder((int) $info->tab_order);
+ }
+ } else {
+ DB::table('module')->insert(['module_name' => $module->name()]);
}
+
+ return $module;
})
- ->filter()
->sort(self::moduleSorter());
});
}
@@ -263,7 +309,7 @@ class Module
public static function findByComponent(string $component, Tree $tree, User $user): Collection
{
$interface = self::COMPONENTS[$component];
-
+
return self::findByInterface($interface)
->filter(function (ModuleInterface $module) use ($component, $tree, $user): bool {
return $module->accessLevel($tree, $component) >= Auth::accessLevel($tree, $user);
diff --git a/app/Module/AbstractModule.php b/app/Module/AbstractModule.php
index 1a2016cc65..35b08009d9 100644
--- a/app/Module/AbstractModule.php
+++ b/app/Module/AbstractModule.php
@@ -117,13 +117,11 @@ abstract class AbstractModule implements ModuleInterface
*
* @param string $name
*
- * @return ModuleInterface
+ * @return void
*/
- final public function setName(string $name): ModuleInterface
+ final public function setName(string $name): void
{
$this->name = $name;
-
- return $this;
}
/**
@@ -168,7 +166,7 @@ abstract class AbstractModule implements ModuleInterface
*
* @return string
*/
- final public function getPreference($setting_name, $default = ''): string
+ final public function getPreference(string $setting_name, string $default = ''): string
{
return DB::table('module_setting')
->where('module_name', '=', $this->name())
@@ -187,7 +185,7 @@ abstract class AbstractModule implements ModuleInterface
*
* @return $this
*/
- final public function setPreference($setting_name, $setting_value): self
+ final public function setPreference(string $setting_name, string $setting_value): void
{
DB::table('module_setting')->updateOrInsert([
'module_name' => $this->name(),
@@ -195,8 +193,6 @@ abstract class AbstractModule implements ModuleInterface
], [
'setting_value' => $setting_value,
]);
-
- return $this;
}
/**
diff --git a/app/Module/AhnentafelReportModule.php b/app/Module/AhnentafelReportModule.php
index 3d0274210f..2e9407da6b 100644
--- a/app/Module/AhnentafelReportModule.php
+++ b/app/Module/AhnentafelReportModule.php
@@ -24,7 +24,7 @@ use Fisharebest\Webtrees\Menu;
/**
* Class AhnentafelReportModule
*/
-class AhnentafelReportModule extends AbstractModule implements ModuleInterface, ModuleReportInterface
+class AhnentafelReportModule extends AbstractModule implements ModuleReportInterface
{
use ModuleReportTrait;
diff --git a/app/Module/AlbumModule.php b/app/Module/AlbumModule.php
index f0881c4ed3..4bdb03f068 100644
--- a/app/Module/AlbumModule.php
+++ b/app/Module/AlbumModule.php
@@ -25,7 +25,7 @@ use Fisharebest\Webtrees\Media;
/**
* Class AlbumModule
*/
-class AlbumModule extends AbstractModule implements ModuleInterface, ModuleTabInterface
+class AlbumModule extends AbstractModule implements ModuleTabInterface
{
use ModuleTabTrait;
diff --git a/app/Module/AncestorsChartModule.php b/app/Module/AncestorsChartModule.php
index 451027226e..1304f48976 100644
--- a/app/Module/AncestorsChartModule.php
+++ b/app/Module/AncestorsChartModule.php
@@ -35,7 +35,7 @@ use Symfony\Component\HttpFoundation\Response;
/**
* Class AncestorsChartModule
*/
-class AncestorsChartModule extends AbstractModule implements ModuleInterface, ModuleChartInterface
+class AncestorsChartModule extends AbstractModule implements ModuleChartInterface
{
use ModuleChartTrait;
diff --git a/app/Module/BatchUpdateModule.php b/app/Module/BatchUpdateModule.php
index fac4a66a66..7a5c3cf0ba 100644
--- a/app/Module/BatchUpdateModule.php
+++ b/app/Module/BatchUpdateModule.php
@@ -40,7 +40,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class BatchUpdateModule
*/
-class BatchUpdateModule extends AbstractModule implements ModuleInterface, ModuleConfigInterface
+class BatchUpdateModule extends AbstractModule implements ModuleConfigInterface
{
use ModuleConfigTrait;
diff --git a/app/Module/BingWebmasterToolsModule.php b/app/Module/BingWebmasterToolsModule.php
new file mode 100644
index 0000000000..0067b9e3ec
--- /dev/null
+++ b/app/Module/BingWebmasterToolsModule.php
@@ -0,0 +1,79 @@
+<?php
+/**
+ * webtrees: online genealogy
+ * Copyright (C) 2019 webtrees development team
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+declare(strict_types=1);
+
+namespace Fisharebest\Webtrees\Module;
+
+/**
+ * Class BingWebmasterToolsModule - add support for Bing webmaster tools
+ */
+class BingWebmasterToolsModule extends AbstractModule implements ModuleAnalyticsInterface
+{
+ use ModuleAnalyticsTrait;
+
+ /**
+ * How should this module be labelled on tabs, menus, etc.?
+ *
+ * @return string
+ */
+ public function title(): string {
+ return 'Bing™ webmaster tools';
+ }
+
+ /**
+ * Form fields to edit the parameters.
+ *
+ * @return string
+ */
+ public function analyticsFormFields(): string
+ {
+ return view('admin/analytics/bing-webmaster-form', $this->analyticsParameters());
+ }
+
+ /**
+ * Home page for the service.
+ *
+ * @return string
+ */
+ public function analyticsHomePageUrl(): string
+ {
+ return 'https://www.bing.com/toolbox/webmaster';
+ }
+
+ /**
+ * The parameters that need to be embedded in the snippet.
+ *
+ * @return string[]
+ */
+ public function analyticsParameters(): array
+ {
+ return [
+ 'BING_WEBMASTER_ID' => $this->getPreference('BING_WEBMASTER_ID')
+ ];
+ }
+
+ /**
+ * Embed placeholders in the snippet.
+ *
+ * @param string[] $parameters
+ *
+ * @return string
+ */
+ public function analyticsSnippet(array $parameters): string
+ {
+ return view('admin/analytics/bing-webmaster-snippet', $parameters);
+ }
+}
diff --git a/app/Module/BirthDeathMarriageReportModule.php b/app/Module/BirthDeathMarriageReportModule.php
index a0b559073b..3826d4b53f 100644
--- a/app/Module/BirthDeathMarriageReportModule.php
+++ b/app/Module/BirthDeathMarriageReportModule.php
@@ -24,7 +24,7 @@ use Fisharebest\Webtrees\Menu;
/**
* Class BirthDeathMarriageReportModule
*/
-class BirthDeathMarriageReportModule extends AbstractModule implements ModuleInterface, ModuleReportInterface
+class BirthDeathMarriageReportModule extends AbstractModule implements ModuleReportInterface
{
use ModuleReportTrait;
diff --git a/app/Module/BirthReportModule.php b/app/Module/BirthReportModule.php
index 5eaf0f39da..c584be8c01 100644
--- a/app/Module/BirthReportModule.php
+++ b/app/Module/BirthReportModule.php
@@ -24,7 +24,7 @@ use Fisharebest\Webtrees\Menu;
/**
* Class BirthReportModule
*/
-class BirthReportModule extends AbstractModule implements ModuleInterface, ModuleReportInterface
+class BirthReportModule extends AbstractModule implements ModuleReportInterface
{
use ModuleReportTrait;
diff --git a/app/Module/CalendarMenuModule.php b/app/Module/CalendarMenuModule.php
index c5ef709ea5..da2f070f65 100644
--- a/app/Module/CalendarMenuModule.php
+++ b/app/Module/CalendarMenuModule.php
@@ -24,7 +24,7 @@ use Fisharebest\Webtrees\Tree;
/**
* Class CalendarMenuModule - provide a menu option for the calendar
*/
-class CalendarMenuModule extends AbstractModule implements ModuleInterface, ModuleMenuInterface
+class CalendarMenuModule extends AbstractModule implements ModuleMenuInterface
{
use ModuleMenuTrait;
diff --git a/app/Module/CemeteryReportModule.php b/app/Module/CemeteryReportModule.php
index 172088b60a..7e9f5bd11e 100644
--- a/app/Module/CemeteryReportModule.php
+++ b/app/Module/CemeteryReportModule.php
@@ -24,7 +24,7 @@ use Fisharebest\Webtrees\Menu;
/**
* Class CemeteryReportModule
*/
-class CemeteryReportModule extends AbstractModule implements ModuleInterface, ModuleReportInterface
+class CemeteryReportModule extends AbstractModule implements ModuleReportInterface
{
use ModuleReportTrait;
diff --git a/app/Module/ChangeReportModule.php b/app/Module/ChangeReportModule.php
index 9f61978707..1c506a2a5d 100644
--- a/app/Module/ChangeReportModule.php
+++ b/app/Module/ChangeReportModule.php
@@ -25,7 +25,7 @@ use Fisharebest\Webtrees\Menu;
/**
* Class ChangeReportModule
*/
-class ChangeReportModule extends AbstractModule implements ModuleInterface, ModuleReportInterface
+class ChangeReportModule extends AbstractModule implements ModuleReportInterface
{
use ModuleReportTrait;
diff --git a/app/Module/ChartsBlockModule.php b/app/Module/ChartsBlockModule.php
index e28c4c482c..cd076423c1 100644
--- a/app/Module/ChartsBlockModule.php
+++ b/app/Module/ChartsBlockModule.php
@@ -28,7 +28,7 @@ use Symfony\Component\HttpFoundation\Request;
/**
* Class ChartsBlockModule
*/
-class ChartsBlockModule extends AbstractModule implements ModuleInterface, ModuleBlockInterface
+class ChartsBlockModule extends AbstractModule implements ModuleBlockInterface
{
use ModuleBlockTrait;
diff --git a/app/Module/ChartsMenuModule.php b/app/Module/ChartsMenuModule.php
index a6d459b5e1..a20a67e568 100644
--- a/app/Module/ChartsMenuModule.php
+++ b/app/Module/ChartsMenuModule.php
@@ -28,7 +28,7 @@ use Symfony\Component\HttpFoundation\Request;
/**
* Class ChartsMenuModule - provide a menu option for the charts
*/
-class ChartsMenuModule extends AbstractModule implements ModuleInterface, ModuleMenuInterface
+class ChartsMenuModule extends AbstractModule implements ModuleMenuInterface
{
use ModuleMenuTrait;
diff --git a/app/Module/ClippingsCartModule.php b/app/Module/ClippingsCartModule.php
index b35b4438a2..0fd035411d 100644
--- a/app/Module/ClippingsCartModule.php
+++ b/app/Module/ClippingsCartModule.php
@@ -50,7 +50,7 @@ use Symfony\Component\HttpFoundation\ResponseHeaderBag;
/**
* Class ClippingsCartModule
*/
-class ClippingsCartModule extends AbstractModule implements ModuleInterface, ModuleMenuInterface
+class ClippingsCartModule extends AbstractModule implements ModuleMenuInterface
{
use ModuleMenuTrait;
diff --git a/app/Module/CompactTreeChartModule.php b/app/Module/CompactTreeChartModule.php
index 8bd7138d61..9e6d550890 100644
--- a/app/Module/CompactTreeChartModule.php
+++ b/app/Module/CompactTreeChartModule.php
@@ -29,7 +29,7 @@ use Symfony\Component\HttpFoundation\Response;
/**
* Class CompactTreeChartModule
*/
-class CompactTreeChartModule extends AbstractModule implements ModuleInterface, ModuleChartInterface
+class CompactTreeChartModule extends AbstractModule implements ModuleChartInterface
{
use ModuleChartTrait;
diff --git a/app/Module/DeathReportModule.php b/app/Module/DeathReportModule.php
index 7c92e48f74..9d656df191 100644
--- a/app/Module/DeathReportModule.php
+++ b/app/Module/DeathReportModule.php
@@ -24,7 +24,7 @@ use Fisharebest\Webtrees\Menu;
/**
* Class DeathReportModule
*/
-class DeathReportModule extends AbstractModule implements ModuleInterface, ModuleReportInterface
+class DeathReportModule extends AbstractModule implements ModuleReportInterface
{
use ModuleReportTrait;
diff --git a/app/Module/DescendancyChartModule.php b/app/Module/DescendancyChartModule.php
index e0dd6ff876..9ac313723f 100644
--- a/app/Module/DescendancyChartModule.php
+++ b/app/Module/DescendancyChartModule.php
@@ -38,7 +38,7 @@ use Symfony\Component\HttpFoundation\Response;
/**
* Class DescendancyChartModule
*/
-class DescendancyChartModule extends AbstractModule implements ModuleInterface, ModuleChartInterface
+class DescendancyChartModule extends AbstractModule implements ModuleChartInterface
{
use ModuleChartTrait;
diff --git a/app/Module/DescendancyModule.php b/app/Module/DescendancyModule.php
index c7754608ac..1b12e1fd37 100644
--- a/app/Module/DescendancyModule.php
+++ b/app/Module/DescendancyModule.php
@@ -29,7 +29,7 @@ use Symfony\Component\HttpFoundation\Response;
/**
* Class DescendancyModule
*/
-class DescendancyModule extends AbstractModule implements ModuleInterface, ModuleSidebarInterface
+class DescendancyModule extends AbstractModule implements ModuleSidebarInterface
{
use ModuleSidebarTrait;
diff --git a/app/Module/DescendancyReportModule.php b/app/Module/DescendancyReportModule.php
index b02bca6ed1..e386e04cf6 100644
--- a/app/Module/DescendancyReportModule.php
+++ b/app/Module/DescendancyReportModule.php
@@ -24,7 +24,7 @@ use Fisharebest\Webtrees\Menu;
/**
* Class DescendancyReportModule
*/
-class DescendancyReportModule extends AbstractModule implements ModuleInterface, ModuleReportInterface
+class DescendancyReportModule extends AbstractModule implements ModuleReportInterface
{
use ModuleReportTrait;
diff --git a/app/Module/ExtraInformationModule.php b/app/Module/ExtraInformationModule.php
index 1dce10c6c4..110fc46c84 100644
--- a/app/Module/ExtraInformationModule.php
+++ b/app/Module/ExtraInformationModule.php
@@ -26,7 +26,7 @@ use Fisharebest\Webtrees\Individual;
* Class ExtraInformationModule
* A sidebar to show non-genealogy information about an individual
*/
-class ExtraInformationModule extends AbstractModule implements ModuleInterface, ModuleSidebarInterface
+class ExtraInformationModule extends AbstractModule implements ModuleSidebarInterface
{
use ModuleSidebarTrait;
diff --git a/app/Module/FactSourcesReportModule.php b/app/Module/FactSourcesReportModule.php
index 699855b8f2..9b43b2b1b9 100644
--- a/app/Module/FactSourcesReportModule.php
+++ b/app/Module/FactSourcesReportModule.php
@@ -25,7 +25,7 @@ use Fisharebest\Webtrees\Menu;
/**
* Class FactSourcesReportModule
*/
-class FactSourcesReportModule extends AbstractModule implements ModuleInterface, ModuleReportInterface
+class FactSourcesReportModule extends AbstractModule implements ModuleReportInterface
{
use ModuleReportTrait;
diff --git a/app/Module/FamilyBookChartModule.php b/app/Module/FamilyBookChartModule.php
index 073adf323e..4a9e73eaee 100644
--- a/app/Module/FamilyBookChartModule.php
+++ b/app/Module/FamilyBookChartModule.php
@@ -31,7 +31,7 @@ use Symfony\Component\HttpFoundation\Response;
/**
* Class FamilyBookChartModule
*/
-class FamilyBookChartModule extends AbstractModule implements ModuleInterface, ModuleChartInterface
+class FamilyBookChartModule extends AbstractModule implements ModuleChartInterface
{
use ModuleChartTrait;
diff --git a/app/Module/FamilyGroupReportModule.php b/app/Module/FamilyGroupReportModule.php
index 6149d8fa84..962502abc8 100644
--- a/app/Module/FamilyGroupReportModule.php
+++ b/app/Module/FamilyGroupReportModule.php
@@ -24,7 +24,7 @@ use Fisharebest\Webtrees\Menu;
/**
* Class FamilyGroupReportModule
*/
-class FamilyGroupReportModule extends AbstractModule implements ModuleInterface, ModuleReportInterface
+class FamilyGroupReportModule extends AbstractModule implements ModuleReportInterface
{
use ModuleReportTrait;
diff --git a/app/Module/FamilyNavigatorModule.php b/app/Module/FamilyNavigatorModule.php
index 2ac182fb1d..b691539462 100644
--- a/app/Module/FamilyNavigatorModule.php
+++ b/app/Module/FamilyNavigatorModule.php
@@ -23,7 +23,7 @@ use Fisharebest\Webtrees\Individual;
/**
* Class FamilyNavigatorModule
*/
-class FamilyNavigatorModule extends AbstractModule implements ModuleInterface, ModuleSidebarInterface
+class FamilyNavigatorModule extends AbstractModule implements ModuleSidebarInterface
{
use ModuleSidebarTrait;
diff --git a/app/Module/FamilyTreeFavoritesModule.php b/app/Module/FamilyTreeFavoritesModule.php
index 53404c8455..5138178311 100644
--- a/app/Module/FamilyTreeFavoritesModule.php
+++ b/app/Module/FamilyTreeFavoritesModule.php
@@ -30,7 +30,7 @@ use Symfony\Component\HttpFoundation\Request;
/**
* Class FamilyTreeFavoritesModule
*/
-class FamilyTreeFavoritesModule extends AbstractModule implements ModuleInterface, ModuleBlockInterface
+class FamilyTreeFavoritesModule extends AbstractModule implements ModuleBlockInterface
{
use ModuleBlockTrait;
diff --git a/app/Module/FamilyTreeNewsModule.php b/app/Module/FamilyTreeNewsModule.php
index 8e610a833e..396d9c79a0 100644
--- a/app/Module/FamilyTreeNewsModule.php
+++ b/app/Module/FamilyTreeNewsModule.php
@@ -29,7 +29,7 @@ use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
/**
* Class FamilyTreeNewsModule
*/
-class FamilyTreeNewsModule extends AbstractModule implements ModuleInterface, ModuleBlockInterface
+class FamilyTreeNewsModule extends AbstractModule implements ModuleBlockInterface
{
use ModuleBlockTrait;
diff --git a/app/Module/FamilyTreeStatisticsModule.php b/app/Module/FamilyTreeStatisticsModule.php
index 61bef1b007..d5053bd319 100644
--- a/app/Module/FamilyTreeStatisticsModule.php
+++ b/app/Module/FamilyTreeStatisticsModule.php
@@ -28,7 +28,7 @@ use Symfony\Component\HttpFoundation\Request;
/**
* Class FamilyTreeStatisticsModule
*/
-class FamilyTreeStatisticsModule extends AbstractModule implements ModuleInterface, ModuleBlockInterface
+class FamilyTreeStatisticsModule extends AbstractModule implements ModuleBlockInterface
{
use ModuleBlockTrait;
diff --git a/app/Module/FanChartModule.php b/app/Module/FanChartModule.php
index a5fc062cee..7e377b0c44 100644
--- a/app/Module/FanChartModule.php
+++ b/app/Module/FanChartModule.php
@@ -30,7 +30,7 @@ use Symfony\Component\HttpFoundation\Response;
/**
* Class FanChartModule
*/
-class FanChartModule extends AbstractModule implements ModuleInterface, ModuleChartInterface
+class FanChartModule extends AbstractModule implements ModuleChartInterface
{
use ModuleChartTrait;
diff --git a/app/Module/FrequentlyAskedQuestionsModule.php b/app/Module/FrequentlyAskedQuestionsModule.php
index 0b5240c35f..e67a47194b 100644
--- a/app/Module/FrequentlyAskedQuestionsModule.php
+++ b/app/Module/FrequentlyAskedQuestionsModule.php
@@ -31,7 +31,7 @@ use Symfony\Component\HttpFoundation\Response;
/**
* Class FrequentlyAskedQuestionsModule
*/
-class FrequentlyAskedQuestionsModule extends AbstractModule implements ModuleInterface, ModuleConfigInterface, ModuleMenuInterface
+class FrequentlyAskedQuestionsModule extends AbstractModule implements ModuleConfigInterface, ModuleMenuInterface
{
use ModuleConfigTrait;
use ModuleMenuTrait;
diff --git a/app/Module/GoogleAnalyticsModule.php b/app/Module/GoogleAnalyticsModule.php
new file mode 100644
index 0000000000..3fd7afb0d3
--- /dev/null
+++ b/app/Module/GoogleAnalyticsModule.php
@@ -0,0 +1,92 @@
+<?php
+/**
+ * webtrees: online genealogy
+ * Copyright (C) 2019 webtrees development team
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+declare(strict_types=1);
+
+namespace Fisharebest\Webtrees\Module;
+
+use Fisharebest\Webtrees\Auth;
+use Fisharebest\Webtrees\Tree;
+use Fisharebest\Webtrees\User;
+
+/**
+ * Class GoogleAnalyticsModule - add support for Google analytics.
+ */
+class GoogleAnalyticsModule extends AbstractModule implements ModuleAnalyticsInterface
+{
+ use ModuleAnalyticsTrait;
+
+ /**
+ * How should this module be labelled on tabs, menus, etc.?
+ *
+ * @return string
+ */
+ public function title(): string {
+ return 'Google™ analytics';
+ }
+
+ /**
+ * Form fields to edit the parameters.
+ *
+ * @return string
+ */
+ public function analyticsFormFields(): string
+ {
+ return view('admin/analytics/google-analytics-form', $this->analyticsParameters());
+ }
+
+ /**
+ * Home page for the service.
+ *
+ * @return string
+ */
+ public function analyticsHomePageUrl(): string
+ {
+ return 'https://www.google.com/analytics';
+ }
+
+ /**
+ * The parameters that need to be embedded in the snippet.
+ *
+ * @return string[]
+ */
+ public function analyticsParameters(): array
+ {
+ return [
+ 'GOOGLE_WEBMASTER_ID' => $this->getPreference('GOOGLE_WEBMASTER_ID')
+ ];
+ }
+
+ /**
+ * Embed placeholders in the snippet.
+ *
+ * @param string[] $parameters
+ *
+ * @return string
+ */
+ public function analyticsSnippet(array $parameters): string
+ {
+ // Add extra dimensions (i.e. filtering categories)
+ $tree = app()->make(Tree::class);
+ $user = app()->make(User::class);
+
+ $parameters['dimensions'] = (object) [
+ 'dimension1' => $tree instanceof Tree ? $tree->name() : '-',
+ 'dimension2' => $tree instanceof Tree ? Auth::accessLevel($tree, $user) : '-',
+ ];
+
+ return view('admin/analytics/google-analytics-snippet', $parameters);
+ }
+}
diff --git a/app/Module/GoogleWebmasterToolsModule.php b/app/Module/GoogleWebmasterToolsModule.php
new file mode 100644
index 0000000000..5ab78db990
--- /dev/null
+++ b/app/Module/GoogleWebmasterToolsModule.php
@@ -0,0 +1,79 @@
+<?php
+/**
+ * webtrees: online genealogy
+ * Copyright (C) 2019 webtrees development team
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+declare(strict_types=1);
+
+namespace Fisharebest\Webtrees\Module;
+
+/**
+ * Class GoogleWebmasterToolsModule - add support for Google webmaster tools.
+ */
+class GoogleWebmasterToolsModule extends AbstractModule implements ModuleAnalyticsInterface
+{
+ use ModuleAnalyticsTrait;
+
+ /**
+ * How should this module be labelled on tabs, menus, etc.?
+ *
+ * @return string
+ */
+ public function title(): string {
+ return 'Google™ webmaster tools';
+ }
+
+ /**
+ * Form fields to edit the parameters.
+ *
+ * @return string
+ */
+ public function analyticsFormFields(): string
+ {
+ return view('admin/analytics/google-webmaster-form', $this->analyticsParameters());
+ }
+
+ /**
+ * Home page for the service.
+ *
+ * @return string
+ */
+ public function analyticsHomePageUrl(): string
+ {
+ return 'https://www.google.com/webmasters';
+ }
+
+ /**
+ * The parameters that need to be embedded in the snippet.
+ *
+ * @return string[]
+ */
+ public function analyticsParameters(): array
+ {
+ return [
+ 'GOOGLE_WEBMASTER_ID' => $this->getPreference('GOOGLE_WEBMASTER_ID')
+ ];
+ }
+
+ /**
+ * Embed placeholders in the snippet.
+ *
+ * @param string[] $parameters
+ *
+ * @return string
+ */
+ public function analyticsSnippet(array $parameters): string
+ {
+ return view('admin/analytics/google-webmaster-snippet', $parameters);
+ }
+}
diff --git a/app/Module/HourglassChartModule.php b/app/Module/HourglassChartModule.php
index 7f2ccb4f76..153b6a12e2 100644
--- a/app/Module/HourglassChartModule.php
+++ b/app/Module/HourglassChartModule.php
@@ -31,7 +31,7 @@ use Symfony\Component\HttpFoundation\Response;
/**
* Class HourglassChartModule
*/
-class HourglassChartModule extends AbstractModule implements ModuleInterface, ModuleChartInterface
+class HourglassChartModule extends AbstractModule implements ModuleChartInterface
{
use ModuleChartTrait;
diff --git a/app/Module/HtmlBlockModule.php b/app/Module/HtmlBlockModule.php
index 3ff4c727a2..6ce1e8fecb 100644
--- a/app/Module/HtmlBlockModule.php
+++ b/app/Module/HtmlBlockModule.php
@@ -27,7 +27,7 @@ use Symfony\Component\HttpFoundation\Request;
/**
* Class HtmlBlockModule
*/
-class HtmlBlockModule extends AbstractModule implements ModuleInterface, ModuleBlockInterface
+class HtmlBlockModule extends AbstractModule implements ModuleBlockInterface
{
use ModuleBlockTrait;
diff --git a/app/Module/IndividualFactsTabModule.php b/app/Module/IndividualFactsTabModule.php
index 74ab353f50..a5a9b66a34 100644
--- a/app/Module/IndividualFactsTabModule.php
+++ b/app/Module/IndividualFactsTabModule.php
@@ -31,7 +31,7 @@ use Fisharebest\Webtrees\Site;
/**
* Class IndividualFactsTabModule
*/
-class IndividualFactsTabModule extends AbstractModule implements ModuleInterface, ModuleTabInterface
+class IndividualFactsTabModule extends AbstractModule implements ModuleTabInterface
{
use ModuleTabTrait;
diff --git a/app/Module/IndividualFamiliesReportModule.php b/app/Module/IndividualFamiliesReportModule.php
index 21dfb36bf9..693fff1f9e 100644
--- a/app/Module/IndividualFamiliesReportModule.php
+++ b/app/Module/IndividualFamiliesReportModule.php
@@ -25,7 +25,7 @@ use Fisharebest\Webtrees\Menu;
/**
* Class IndividualFamiliesReportModule
*/
-class IndividualFamiliesReportModule extends AbstractModule implements ModuleInterface, ModuleReportInterface
+class IndividualFamiliesReportModule extends AbstractModule implements ModuleReportInterface
{
use ModuleReportTrait;
diff --git a/app/Module/IndividualReportModule.php b/app/Module/IndividualReportModule.php
index 1b116a73d4..5558658658 100644
--- a/app/Module/IndividualReportModule.php
+++ b/app/Module/IndividualReportModule.php
@@ -24,7 +24,7 @@ use Fisharebest\Webtrees\Menu;
/**
* Class IndividualReportModule
*/
-class IndividualReportModule extends AbstractModule implements ModuleInterface, ModuleReportInterface
+class IndividualReportModule extends AbstractModule implements ModuleReportInterface
{
use ModuleReportTrait;
diff --git a/app/Module/InteractiveTreeModule.php b/app/Module/InteractiveTreeModule.php
index 4125810478..8622f51139 100644
--- a/app/Module/InteractiveTreeModule.php
+++ b/app/Module/InteractiveTreeModule.php
@@ -34,7 +34,7 @@ use Symfony\Component\HttpFoundation\Response;
* Class InteractiveTreeModule
* Tip : you could change the number of generations loaded before ajax calls both in individual page and in treeview page to optimize speed and server load
*/
-class InteractiveTreeModule extends AbstractModule implements ModuleInterface, ModuleChartInterface, ModuleTabInterface
+class InteractiveTreeModule extends AbstractModule implements ModuleChartInterface, ModuleTabInterface
{
use ModuleChartTrait;
use ModuleTabTrait;
diff --git a/app/Module/LifespansChartModule.php b/app/Module/LifespansChartModule.php
index 114777e3c1..978118ca95 100644
--- a/app/Module/LifespansChartModule.php
+++ b/app/Module/LifespansChartModule.php
@@ -33,7 +33,7 @@ use Symfony\Component\HttpFoundation\Response;
/**
* Class LifespansChartModule
*/
-class LifespansChartModule extends AbstractModule implements ModuleInterface, ModuleChartInterface
+class LifespansChartModule extends AbstractModule implements ModuleChartInterface
{
use ModuleChartTrait;
diff --git a/app/Module/ListsMenuModule.php b/app/Module/ListsMenuModule.php
index cb65f1a278..4e0a9ceacd 100644
--- a/app/Module/ListsMenuModule.php
+++ b/app/Module/ListsMenuModule.php
@@ -25,7 +25,7 @@ use Illuminate\Database\Capsule\Manager as DB;
/**
* Class ListsMenuModule - provide a menu option for the lists
*/
-class ListsMenuModule extends AbstractModule implements ModuleInterface, ModuleMenuInterface
+class ListsMenuModule extends AbstractModule implements ModuleMenuInterface
{
use ModuleMenuTrait;
diff --git a/app/Module/LoggedInUsersModule.php b/app/Module/LoggedInUsersModule.php
index c0b6c6938f..37a665f83a 100644
--- a/app/Module/LoggedInUsersModule.php
+++ b/app/Module/LoggedInUsersModule.php
@@ -27,7 +27,7 @@ use Symfony\Component\HttpFoundation\Request;
/**
* Class LoggedInUsersModule
*/
-class LoggedInUsersModule extends AbstractModule implements ModuleInterface, ModuleBlockInterface
+class LoggedInUsersModule extends AbstractModule implements ModuleBlockInterface
{
use ModuleBlockTrait;
diff --git a/app/Module/LoginBlockModule.php b/app/Module/LoginBlockModule.php
index 2ead102e5a..3f1859a275 100644
--- a/app/Module/LoginBlockModule.php
+++ b/app/Module/LoginBlockModule.php
@@ -26,7 +26,7 @@ use Symfony\Component\HttpFoundation\Request;
/**
* Class LoginBlockModule
*/
-class LoginBlockModule extends AbstractModule implements ModuleInterface, ModuleBlockInterface
+class LoginBlockModule extends AbstractModule implements ModuleBlockInterface
{
use ModuleBlockTrait;
diff --git a/app/Module/MarriageReportModule.php b/app/Module/MarriageReportModule.php
index 9b55f41f20..3f8e9e3a18 100644
--- a/app/Module/MarriageReportModule.php
+++ b/app/Module/MarriageReportModule.php
@@ -24,7 +24,7 @@ use Fisharebest\Webtrees\Menu;
/**
* Class MarriageReportModule
*/
-class MarriageReportModule extends AbstractModule implements ModuleInterface, ModuleReportInterface
+class MarriageReportModule extends AbstractModule implements ModuleReportInterface
{
use ModuleReportTrait;
diff --git a/app/Module/MatomoAnalyticsModule.php b/app/Module/MatomoAnalyticsModule.php
new file mode 100644
index 0000000000..57dd7d0b52
--- /dev/null
+++ b/app/Module/MatomoAnalyticsModule.php
@@ -0,0 +1,80 @@
+<?php
+/**
+ * webtrees: online genealogy
+ * Copyright (C) 2019 webtrees development team
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+declare(strict_types=1);
+
+namespace Fisharebest\Webtrees\Module;
+
+/**
+ * Class MatomoAnalyticsModule - add support for Matomo analytics.
+ */
+class MatomoAnalyticsModule extends AbstractModule implements ModuleAnalyticsInterface
+{
+ use ModuleAnalyticsTrait;
+
+ /**
+ * How should this module be labelled on tabs, menus, etc.?
+ *
+ * @return string
+ */
+ public function title(): string
+ {
+ return 'Matomo™ / Piwik™ analytics';
+ }
+
+ /**
+ * Form fields to edit the parameters.
+ *
+ * @return string
+ */
+ public function analyticsFormFields(): string
+ {
+ return view('admin/analytics/matomo-analytics-form', $this->analyticsParameters());
+ }
+
+ /**
+ * Home page for the service.
+ *
+ * @return string
+ */
+ public function analyticsHomePageUrl(): string
+ {
+ return 'https://matomo.org';
+ }
+
+ /**
+ * The parameters that need to be embedded in the snippet.
+ *
+ * @return string[]
+ */
+ public function analyticsParameters(): array
+ {
+ return [
+ 'MATOMO_WEBMASTER_ID' => $this->getPreference('MATOMO_WEBMASTER_ID'),
+ ];
+ }
+
+ /**
+ * Embed placeholders in the snippet.
+ *
+ * @param string[] $parameters
+ *
+ * @return string
+ */
+ public function analyticsSnippet(array $parameters): string
+ {
+ return view('admin/analytics/matomo-analytics-snippet', $parameters);
+ }
+}
diff --git a/app/Module/MediaTabModule.php b/app/Module/MediaTabModule.php
index f86aaf6f00..28173e715d 100644
--- a/app/Module/MediaTabModule.php
+++ b/app/Module/MediaTabModule.php
@@ -26,7 +26,7 @@ use Fisharebest\Webtrees\Individual;
/**
* Class MediaTabModule
*/
-class MediaTabModule extends AbstractModule implements ModuleInterface, ModuleTabInterface
+class MediaTabModule extends AbstractModule implements ModuleTabInterface
{
use ModuleTabTrait;
diff --git a/app/Module/MissingFactsReportModule.php b/app/Module/MissingFactsReportModule.php
index b70c3be6a8..cd608bd75f 100644
--- a/app/Module/MissingFactsReportModule.php
+++ b/app/Module/MissingFactsReportModule.php
@@ -25,7 +25,7 @@ use Fisharebest\Webtrees\Menu;
/**
* Class MissingFactsReportModule
*/
-class MissingFactsReportModule extends AbstractModule implements ModuleInterface, ModuleReportInterface
+class MissingFactsReportModule extends AbstractModule implements ModuleReportInterface
{
use ModuleReportTrait;
diff --git a/app/Module/ModuleAnalyticsInterface.php b/app/Module/ModuleAnalyticsInterface.php
new file mode 100644
index 0000000000..dbd16cb11b
--- /dev/null
+++ b/app/Module/ModuleAnalyticsInterface.php
@@ -0,0 +1,63 @@
+<?php
+/**
+ * webtrees: online genealogy
+ * Copyright (C) 2019 webtrees development team
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+declare(strict_types=1);
+
+namespace Fisharebest\Webtrees\Module;
+
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Interface ModuleAnalyticsInterface - Classes and libraries for module system
+ */
+interface ModuleAnalyticsInterface extends ModuleInterface
+{
+ /**
+ * Should we add this tracker?
+ *
+ * @return bool
+ */
+ public function analyticsCanShow(): bool;
+
+ /**
+ * Form fields to edit the parameters.
+ *
+ * @return string
+ */
+ public function analyticsFormFields(): string;
+
+ /**
+ * Home page for the service.
+ *
+ * @return string
+ */
+ public function analyticsHomePageUrl(): string;
+
+ /**
+ * The parameters that need to be embedded in the snippet.
+ *
+ * @return string[]
+ */
+ public function analyticsParameters(): array;
+
+ /**
+ * Embed placeholders in the snippet.
+ *
+ * @param string[] $parameters
+ *
+ * @return string
+ */
+ public function analyticsSnippet(array $parameters): string;
+}
diff --git a/app/Module/ModuleAnalyticsTrait.php b/app/Module/ModuleAnalyticsTrait.php
new file mode 100644
index 0000000000..d10afae301
--- /dev/null
+++ b/app/Module/ModuleAnalyticsTrait.php
@@ -0,0 +1,95 @@
+<?php
+/**
+ * webtrees: online genealogy
+ * Copyright (C) 2019 webtrees development team
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+declare(strict_types=1);
+
+namespace Fisharebest\Webtrees\Module;
+
+use Fisharebest\Webtrees\I18N;
+use Illuminate\Support\Collection;
+
+/**
+ * Trait ModuleAnalyticsTrait - default implementation of ModuleAnalyticsInterface
+ */
+trait ModuleAnalyticsTrait
+{
+ /**
+ * Should we add this tracker?
+ *
+ * @return bool
+ */
+ public function analyticsCanShow(): bool
+ {
+ foreach ($this->analyticsParameters() as $parameter) {
+ if ($parameter === '') {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * A sentence describing what this module does.
+ *
+ * @return string
+ */
+ public function description(): string
+ {
+ return I18N::translate('Tracking and analytics');
+ }
+
+ /**
+ * Form fields to edit the parameters.
+ *
+ * @return string
+ */
+ public function analyticsFormFields(): string
+ {
+ return '';
+ }
+
+ /**
+ * Home page for the service.
+ *
+ * @return string
+ */
+ public function analyticsHomePageURL(): string
+ {
+ return '';
+ }
+
+ /**
+ * The parameters that need to be embedded in the snippet.
+ *
+ * @return string[]
+ */
+ public function analyticsParameters(): array
+ {
+ return [];
+ }
+
+ /**
+ * Embed placeholders in the snippet.
+ *
+ * @param string[] $parameters
+ *
+ * @return string
+ */
+ public function analyticsSnippet(array $parameters): string
+ {
+ return '';
+ }
+}
diff --git a/app/Module/ModuleBlockInterface.php b/app/Module/ModuleBlockInterface.php
index 773264ca18..73ccce4588 100644
--- a/app/Module/ModuleBlockInterface.php
+++ b/app/Module/ModuleBlockInterface.php
@@ -23,7 +23,7 @@ use Symfony\Component\HttpFoundation\Request;
/**
* Interface ModuleBlockInterface - Classes and libraries for module system
*/
-interface ModuleBlockInterface
+interface ModuleBlockInterface extends ModuleInterface
{
/**
* Generate the HTML content of this block.
diff --git a/app/Module/ModuleChartInterface.php b/app/Module/ModuleChartInterface.php
index 4b8352fd06..4caec641f6 100644
--- a/app/Module/ModuleChartInterface.php
+++ b/app/Module/ModuleChartInterface.php
@@ -23,7 +23,7 @@ use Fisharebest\Webtrees\Menu;
/**
* Interface ModuleChartInterface - Classes and libraries for module system
*/
-interface ModuleChartInterface
+interface ModuleChartInterface extends ModuleInterface
{
/**
* A menu item for this chart for an individual box in a chart.
diff --git a/app/Module/ModuleChartTrait.php b/app/Module/ModuleChartTrait.php
index 949e7a1f14..43312f0ff9 100644
--- a/app/Module/ModuleChartTrait.php
+++ b/app/Module/ModuleChartTrait.php
@@ -26,11 +26,6 @@ use Fisharebest\Webtrees\Menu;
trait ModuleChartTrait
{
/**
- * @return string
- */
- abstract public function name(): string;
-
- /**
* A menu item for this chart for an individual box in a chart.
*
* @param Individual $individual
diff --git a/app/Module/ModuleConfigInterface.php b/app/Module/ModuleConfigInterface.php
index d252ae3b1c..c02512a7b0 100644
--- a/app/Module/ModuleConfigInterface.php
+++ b/app/Module/ModuleConfigInterface.php
@@ -20,7 +20,7 @@ namespace Fisharebest\Webtrees\Module;
/**
* Interface ModuleConfigInterface - Classes and libraries for module system
*/
-interface ModuleConfigInterface
+interface ModuleConfigInterface extends ModuleInterface
{
/**
* The URL to a page where the user can modify the configuration of this module.
diff --git a/app/Module/ModuleConfigTrait.php b/app/Module/ModuleConfigTrait.php
index fa04af13f1..108aac42c7 100644
--- a/app/Module/ModuleConfigTrait.php
+++ b/app/Module/ModuleConfigTrait.php
@@ -23,11 +23,6 @@ namespace Fisharebest\Webtrees\Module;
trait ModuleConfigTrait
{
/**
- * @return string
- */
- abstract public function name(): string;
-
- /**
* The URL to a page where the user can modify the configuration of this module.
*
* @return string
diff --git a/app/Module/ModuleCustomInterface.php b/app/Module/ModuleCustomInterface.php
index dbfa4a1c8d..7129ca3b63 100644
--- a/app/Module/ModuleCustomInterface.php
+++ b/app/Module/ModuleCustomInterface.php
@@ -20,7 +20,7 @@ namespace Fisharebest\Webtrees\Module;
/**
* Interface ModuleCustomInterface - Classes and libraries for module system
*/
-interface ModuleCustomInterface
+interface ModuleCustomInterface extends ModuleInterface
{
/**
* The person or organisation who created this module.
diff --git a/app/Module/ModuleInterface.php b/app/Module/ModuleInterface.php
index 64856b02cb..0fafe6d184 100644
--- a/app/Module/ModuleInterface.php
+++ b/app/Module/ModuleInterface.php
@@ -29,9 +29,9 @@ interface ModuleInterface
*
* @param string $name
*
- * @return self
+ * @return void
*/
- public function setName(string $name): self;
+ public function setName(string $name): void;
/**
* A unique internal name for this module (based on the installation folder).
@@ -79,4 +79,27 @@ interface ModuleInterface
* @return int
*/
public function accessLevel(Tree $tree, string $component): int;
+
+ /**
+ * Get a module setting. Return a default if the setting is not set.
+ *
+ * @param string $setting_name
+ * @param string $default
+ *
+ * @return string
+ */
+ public function getPreference(string $setting_name, string $default = ''): string;
+
+ /**
+ * Set a module setting.
+ *
+ * Since module settings are NOT NULL, setting a value to NULL will cause
+ * it to be deleted.
+ *
+ * @param string $setting_name
+ * @param string $setting_value
+ *
+ * @return $this
+ */
+ public function setPreference(string $setting_name, string $setting_value): void;
}
diff --git a/app/Module/ModuleMenuInterface.php b/app/Module/ModuleMenuInterface.php
index 6e01da1f7d..03f21e461a 100644
--- a/app/Module/ModuleMenuInterface.php
+++ b/app/Module/ModuleMenuInterface.php
@@ -23,7 +23,7 @@ use Fisharebest\Webtrees\Tree;
/**
* Interface ModuleMenuInterface - Classes and libraries for module system
*/
-interface ModuleMenuInterface
+interface ModuleMenuInterface extends ModuleInterface
{
/**
* Users change change the order of menus using the control panel.
diff --git a/app/Module/ModuleReportInterface.php b/app/Module/ModuleReportInterface.php
index 1aa3fd7563..e5a9910780 100644
--- a/app/Module/ModuleReportInterface.php
+++ b/app/Module/ModuleReportInterface.php
@@ -23,7 +23,7 @@ use Fisharebest\Webtrees\Menu;
/**
* Interface ModuleReportInterface - Classes and libraries for module system
*/
-interface ModuleReportInterface
+interface ModuleReportInterface extends ModuleInterface
{
/**
* Return a menu item for this report.
diff --git a/app/Module/ModuleSidebarInterface.php b/app/Module/ModuleSidebarInterface.php
index f28ce02717..91c8528b97 100644
--- a/app/Module/ModuleSidebarInterface.php
+++ b/app/Module/ModuleSidebarInterface.php
@@ -22,7 +22,7 @@ use Fisharebest\Webtrees\Individual;
/**
* Interface ModuleSidebarInterface - Classes and libraries for module system
*/
-interface ModuleSidebarInterface
+interface ModuleSidebarInterface extends ModuleInterface
{
/**
* Users change change the order of sidebars using the control panel.
diff --git a/app/Module/ModuleTabInterface.php b/app/Module/ModuleTabInterface.php
index f9b67d5d72..3f92e6a503 100644
--- a/app/Module/ModuleTabInterface.php
+++ b/app/Module/ModuleTabInterface.php
@@ -22,7 +22,7 @@ use Fisharebest\Webtrees\Individual;
/**
* Interface ModuleTabInterface - Classes and libraries for module system
*/
-interface ModuleTabInterface
+interface ModuleTabInterface extends ModuleInterface
{
/**
* Users change change the order of tabs using the control panel.
diff --git a/app/Module/ModuleThemeInterface.php b/app/Module/ModuleThemeInterface.php
index 4e2b484b9c..9c25e2c31b 100644
--- a/app/Module/ModuleThemeInterface.php
+++ b/app/Module/ModuleThemeInterface.php
@@ -22,6 +22,6 @@ namespace Fisharebest\Webtrees\Module;
*
* This class is not currently used
*/
-interface ModuleThemeInterface
+interface ModuleThemeInterface extends ModuleInterface
{
}
diff --git a/app/Module/NotesTabModule.php b/app/Module/NotesTabModule.php
index 49303e1839..8a9c2b4a3f 100644
--- a/app/Module/NotesTabModule.php
+++ b/app/Module/NotesTabModule.php
@@ -25,7 +25,7 @@ use Fisharebest\Webtrees\Individual;
/**
* Class NotesTabModule
*/
-class NotesTabModule extends AbstractModule implements ModuleInterface, ModuleTabInterface
+class NotesTabModule extends AbstractModule implements ModuleTabInterface
{
use ModuleTabTrait;
diff --git a/app/Module/OccupationReportModule.php b/app/Module/OccupationReportModule.php
index 75192eeef9..2fe4cff4ef 100644
--- a/app/Module/OccupationReportModule.php
+++ b/app/Module/OccupationReportModule.php
@@ -25,7 +25,7 @@ use Fisharebest\Webtrees\Menu;
/**
* Class OccupationReportModule
*/
-class OccupationReportModule extends AbstractModule implements ModuleInterface, ModuleReportInterface
+class OccupationReportModule extends AbstractModule implements ModuleReportInterface
{
use ModuleReportTrait;
diff --git a/app/Module/OnThisDayModule.php b/app/Module/OnThisDayModule.php
index 2fa940d90c..2babd43952 100644
--- a/app/Module/OnThisDayModule.php
+++ b/app/Module/OnThisDayModule.php
@@ -28,7 +28,7 @@ use Symfony\Component\HttpFoundation\Request;
/**
* Class OnThisDayModule
*/
-class OnThisDayModule extends AbstractModule implements ModuleInterface, ModuleBlockInterface
+class OnThisDayModule extends AbstractModule implements ModuleBlockInterface
{
use ModuleBlockTrait;
diff --git a/app/Module/PedigreeChartModule.php b/app/Module/PedigreeChartModule.php
index 6a60b77516..76bd3865c1 100644
--- a/app/Module/PedigreeChartModule.php
+++ b/app/Module/PedigreeChartModule.php
@@ -32,7 +32,7 @@ use Symfony\Component\HttpFoundation\Response;
/**
* Class PedigreeChartModule
*/
-class PedigreeChartModule extends AbstractModule implements ModuleInterface, ModuleChartInterface
+class PedigreeChartModule extends AbstractModule implements ModuleChartInterface
{
use ModuleChartTrait;
diff --git a/app/Module/PedigreeMapModule.php b/app/Module/PedigreeMapModule.php
index 9f2d69dce6..4d3b4744f0 100644
--- a/app/Module/PedigreeMapModule.php
+++ b/app/Module/PedigreeMapModule.php
@@ -35,7 +35,7 @@ use Symfony\Component\HttpFoundation\Response;
/**
* Class PedigreeMapModule
*/
-class PedigreeMapModule extends AbstractModule implements ModuleInterface, ModuleChartInterface
+class PedigreeMapModule extends AbstractModule implements ModuleChartInterface
{
use ModuleChartTrait;
diff --git a/app/Module/PedigreeReportModule.php b/app/Module/PedigreeReportModule.php
index fa421691ef..ceb8d62a60 100644
--- a/app/Module/PedigreeReportModule.php
+++ b/app/Module/PedigreeReportModule.php
@@ -24,7 +24,7 @@ use Fisharebest\Webtrees\Menu;
/**
* Class PedigreeReportModule
*/
-class PedigreeReportModule extends AbstractModule implements ModuleInterface, ModuleReportInterface
+class PedigreeReportModule extends AbstractModule implements ModuleReportInterface
{
use ModuleReportTrait;
diff --git a/app/Module/PlacesModule.php b/app/Module/PlacesModule.php
index f170f5d23f..4d18d0b781 100644
--- a/app/Module/PlacesModule.php
+++ b/app/Module/PlacesModule.php
@@ -31,7 +31,7 @@ use Symfony\Component\HttpFoundation\Request;
/**
* Class PlacesMapModule
*/
-class PlacesModule extends AbstractModule implements ModuleInterface, ModuleTabInterface
+class PlacesModule extends AbstractModule implements ModuleTabInterface
{
use ModuleTabTrait;
diff --git a/app/Module/RecentChangesModule.php b/app/Module/RecentChangesModule.php
index 794061ed38..9ed1b792aa 100644
--- a/app/Module/RecentChangesModule.php
+++ b/app/Module/RecentChangesModule.php
@@ -28,7 +28,7 @@ use Symfony\Component\HttpFoundation\Request;
/**
* Class RecentChangesModule
*/
-class RecentChangesModule extends AbstractModule implements ModuleInterface, ModuleBlockInterface
+class RecentChangesModule extends AbstractModule implements ModuleBlockInterface
{
use ModuleBlockTrait;
diff --git a/app/Module/RelatedIndividualsReportModule.php b/app/Module/RelatedIndividualsReportModule.php
index 4d2e74be28..83152d5ad5 100644
--- a/app/Module/RelatedIndividualsReportModule.php
+++ b/app/Module/RelatedIndividualsReportModule.php
@@ -24,7 +24,7 @@ use Fisharebest\Webtrees\Menu;
/**
* Class RelatedIndividualsReportModule
*/
-class RelatedIndividualsReportModule extends AbstractModule implements ModuleInterface, ModuleReportInterface
+class RelatedIndividualsReportModule extends AbstractModule implements ModuleReportInterface
{
use ModuleReportTrait;
diff --git a/app/Module/RelationshipsChartModule.php b/app/Module/RelationshipsChartModule.php
index 31ec030a51..a0bec9ea38 100644
--- a/app/Module/RelationshipsChartModule.php
+++ b/app/Module/RelationshipsChartModule.php
@@ -38,7 +38,7 @@ use Symfony\Component\HttpFoundation\Response;
/**
* Class RelationshipsChartModule
*/
-class RelationshipsChartModule extends AbstractModule implements ModuleInterface, ModuleChartInterface, ModuleConfigInterface
+class RelationshipsChartModule extends AbstractModule implements ModuleChartInterface, ModuleConfigInterface
{
use ModuleChartTrait;
use ModuleConfigTrait;
diff --git a/app/Module/RelativesTabModule.php b/app/Module/RelativesTabModule.php
index 508dafcaf0..f2bc84542a 100644
--- a/app/Module/RelativesTabModule.php
+++ b/app/Module/RelativesTabModule.php
@@ -24,7 +24,7 @@ use Fisharebest\Webtrees\Individual;
/**
* Class RelativesTabModule
*/
-class RelativesTabModule extends AbstractModule implements ModuleInterface, ModuleTabInterface
+class RelativesTabModule extends AbstractModule implements ModuleTabInterface
{
use ModuleTabTrait;
diff --git a/app/Module/ReportsMenuModule.php b/app/Module/ReportsMenuModule.php
index b8c34f0fc5..4d37f81802 100644
--- a/app/Module/ReportsMenuModule.php
+++ b/app/Module/ReportsMenuModule.php
@@ -28,7 +28,7 @@ use Symfony\Component\HttpFoundation\Request;
/**
* Class ReportsMenuModule - provide a menu option for the reports
*/
-class ReportsMenuModule extends AbstractModule implements ModuleInterface, ModuleMenuInterface
+class ReportsMenuModule extends AbstractModule implements ModuleMenuInterface
{
use ModuleMenuTrait;
diff --git a/app/Module/ResearchTaskModule.php b/app/Module/ResearchTaskModule.php
index 70f53e270c..92f54fe5d8 100644
--- a/app/Module/ResearchTaskModule.php
+++ b/app/Module/ResearchTaskModule.php
@@ -31,7 +31,7 @@ use Symfony\Component\HttpFoundation\Request;
/**
* Class ResearchTaskModule
*/
-class ResearchTaskModule extends AbstractModule implements ModuleInterface, ModuleBlockInterface
+class ResearchTaskModule extends AbstractModule implements ModuleBlockInterface
{
use ModuleBlockTrait;
diff --git a/app/Module/ReviewChangesModule.php b/app/Module/ReviewChangesModule.php
index ac83ee5e58..9fea8c1e1e 100644
--- a/app/Module/ReviewChangesModule.php
+++ b/app/Module/ReviewChangesModule.php
@@ -31,7 +31,7 @@ use Symfony\Component\HttpFoundation\Request;
/**
* Class ReviewChangesModule
*/
-class ReviewChangesModule extends AbstractModule implements ModuleInterface, ModuleBlockInterface
+class ReviewChangesModule extends AbstractModule implements ModuleBlockInterface
{
use ModuleBlockTrait;
diff --git a/app/Module/SearchMenuModule.php b/app/Module/SearchMenuModule.php
index f3b19d012e..3a49d5a327 100644
--- a/app/Module/SearchMenuModule.php
+++ b/app/Module/SearchMenuModule.php
@@ -25,7 +25,7 @@ use Fisharebest\Webtrees\Tree;
/**
* Class SearchMenuModule - provide a menu option for the search options
*/
-class SearchMenuModule extends AbstractModule implements ModuleInterface, ModuleMenuInterface
+class SearchMenuModule extends AbstractModule implements ModuleMenuInterface
{
use ModuleMenuTrait;
diff --git a/app/Module/SiteMapModule.php b/app/Module/SiteMapModule.php
index 3a45442464..7d8fe291bf 100644
--- a/app/Module/SiteMapModule.php
+++ b/app/Module/SiteMapModule.php
@@ -38,7 +38,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class SiteMapModule
*/
-class SiteMapModule extends AbstractModule implements ModuleInterface, ModuleConfigInterface
+class SiteMapModule extends AbstractModule implements ModuleConfigInterface
{
use ModuleConfigTrait;
diff --git a/app/Module/SlideShowModule.php b/app/Module/SlideShowModule.php
index ea13e01325..251176e1b9 100644
--- a/app/Module/SlideShowModule.php
+++ b/app/Module/SlideShowModule.php
@@ -30,7 +30,7 @@ use Symfony\Component\HttpFoundation\Request;
/**
* Class SlideShowModule
*/
-class SlideShowModule extends AbstractModule implements ModuleInterface, ModuleBlockInterface
+class SlideShowModule extends AbstractModule implements ModuleBlockInterface
{
use ModuleBlockTrait;
diff --git a/app/Module/SourcesTabModule.php b/app/Module/SourcesTabModule.php
index ca104a5483..474fc1edc9 100644
--- a/app/Module/SourcesTabModule.php
+++ b/app/Module/SourcesTabModule.php
@@ -25,7 +25,7 @@ use Fisharebest\Webtrees\Individual;
/**
* Class SourcesTabModule
*/
-class SourcesTabModule extends AbstractModule implements ModuleInterface, ModuleTabInterface
+class SourcesTabModule extends AbstractModule implements ModuleTabInterface
{
use ModuleTabTrait;
diff --git a/app/Module/StatcounterModule.php b/app/Module/StatcounterModule.php
new file mode 100644
index 0000000000..f39d7869ef
--- /dev/null
+++ b/app/Module/StatcounterModule.php
@@ -0,0 +1,81 @@
+<?php
+/**
+ * webtrees: online genealogy
+ * Copyright (C) 2019 webtrees development team
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+declare(strict_types=1);
+
+namespace Fisharebest\Webtrees\Module;
+
+/**
+ * Class StatcounterModule - add support for statcounter.
+ */
+class StatcounterModule extends AbstractModule implements ModuleAnalyticsInterface
+{
+ use ModuleAnalyticsTrait;
+
+ /**
+ * How should this module be labelled on tabs, menus, etc.?
+ *
+ * @return string
+ */
+ public function title(): string
+ {
+ return 'Statcounter™';
+ }
+
+ /**
+ * Form fields to edit the parameters.
+ *
+ * @return string
+ */
+ public function analyticsFormFields(): string
+ {
+ return view('admin/analytics/statcounter-form', $this->analyticsParameters());
+ }
+
+ /**
+ * Home page for the service.
+ *
+ * @return string
+ */
+ public function analyticsHomePageUrl(): string
+ {
+ return 'https://statcounter.com';
+ }
+
+ /**
+ * The parameters that need to be embedded in the snippet.
+ *
+ * @return string[]
+ */
+ public function analyticsParameters(): array
+ {
+ return [
+ 'STATCOUNTER_PROJECT_ID' => $this->getPreference('STATCOUNTER_PROJECT_ID'),
+ 'STATCOUNTER_SECURITY_ID' => $this->getPreference('STATCOUNTER_SECURITY_ID'),
+ ];
+ }
+
+ /**
+ * Embed placeholders in the snippet.
+ *
+ * @param string[] $parameters
+ *
+ * @return string
+ */
+ public function analyticsSnippet(array $parameters): string
+ {
+ return view('admin/analytics/statcounter-snippet', $parameters);
+ }
+}
diff --git a/app/Module/StatisticsChartModule.php b/app/Module/StatisticsChartModule.php
index c3a1dbfca0..33ccf025cf 100644
--- a/app/Module/StatisticsChartModule.php
+++ b/app/Module/StatisticsChartModule.php
@@ -31,7 +31,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class StatisticsChartModule
*/
-class StatisticsChartModule extends AbstractModule implements ModuleInterface, ModuleChartInterface
+class StatisticsChartModule extends AbstractModule implements ModuleChartInterface
{
use ModuleChartTrait;
diff --git a/app/Module/StoriesModule.php b/app/Module/StoriesModule.php
index 686aa561b9..5c830bb4a7 100644
--- a/app/Module/StoriesModule.php
+++ b/app/Module/StoriesModule.php
@@ -31,7 +31,7 @@ use Symfony\Component\HttpFoundation\Response;
/**
* Class StoriesModule
*/
-class StoriesModule extends AbstractModule implements ModuleInterface, ModuleConfigInterface, ModuleMenuInterface, ModuleTabInterface
+class StoriesModule extends AbstractModule implements ModuleConfigInterface, ModuleMenuInterface, ModuleTabInterface
{
use ModuleTabTrait;
use ModuleConfigTrait;
diff --git a/app/Module/ThemeSelectModule.php b/app/Module/ThemeSelectModule.php
index 3973d27d04..de9ef813bd 100644
--- a/app/Module/ThemeSelectModule.php
+++ b/app/Module/ThemeSelectModule.php
@@ -25,7 +25,7 @@ use Symfony\Component\HttpFoundation\Request;
/**
* Class ThemeSelectModule
*/
-class ThemeSelectModule extends AbstractModule implements ModuleInterface, ModuleBlockInterface
+class ThemeSelectModule extends AbstractModule implements ModuleBlockInterface
{
use ModuleBlockTrait;
diff --git a/app/Module/TimelineChartModule.php b/app/Module/TimelineChartModule.php
index 84c9477156..0f8d989425 100644
--- a/app/Module/TimelineChartModule.php
+++ b/app/Module/TimelineChartModule.php
@@ -30,7 +30,7 @@ use Symfony\Component\HttpFoundation\Response;
/**
* Class TimelineChartModule
*/
-class TimelineChartModule extends AbstractModule implements ModuleInterface, ModuleChartInterface
+class TimelineChartModule extends AbstractModule implements ModuleChartInterface
{
use ModuleChartTrait;
diff --git a/app/Module/TopGivenNamesModule.php b/app/Module/TopGivenNamesModule.php
index d2e8f93d5a..117f8cfa76 100644
--- a/app/Module/TopGivenNamesModule.php
+++ b/app/Module/TopGivenNamesModule.php
@@ -26,7 +26,7 @@ use Symfony\Component\HttpFoundation\Request;
/**
* Class TopGivenNamesModule
*/
-class TopGivenNamesModule extends AbstractModule implements ModuleInterface, ModuleBlockInterface
+class TopGivenNamesModule extends AbstractModule implements ModuleBlockInterface
{
use ModuleBlockTrait;
diff --git a/app/Module/TopPageViewsModule.php b/app/Module/TopPageViewsModule.php
index 918847f1bf..22ed835d2e 100644
--- a/app/Module/TopPageViewsModule.php
+++ b/app/Module/TopPageViewsModule.php
@@ -27,7 +27,7 @@ use Symfony\Component\HttpFoundation\Request;
/**
* Class TopPageViewsModule
*/
-class TopPageViewsModule extends AbstractModule implements ModuleInterface, ModuleBlockInterface
+class TopPageViewsModule extends AbstractModule implements ModuleBlockInterface
{
use ModuleBlockTrait;
diff --git a/app/Module/TopSurnamesModule.php b/app/Module/TopSurnamesModule.php
index fcf1acf8cc..b88e184cc2 100644
--- a/app/Module/TopSurnamesModule.php
+++ b/app/Module/TopSurnamesModule.php
@@ -27,7 +27,7 @@ use Symfony\Component\HttpFoundation\Request;
/**
* Class TopSurnamesModule
*/
-class TopSurnamesModule extends AbstractModule implements ModuleInterface, ModuleBlockInterface
+class TopSurnamesModule extends AbstractModule implements ModuleBlockInterface
{
use ModuleBlockTrait;
diff --git a/app/Module/TreesMenuModule.php b/app/Module/TreesMenuModule.php
index 1cc9ce1c06..c0d9ce6311 100644
--- a/app/Module/TreesMenuModule.php
+++ b/app/Module/TreesMenuModule.php
@@ -25,7 +25,7 @@ use Fisharebest\Webtrees\Tree;
/**
* Class TreesMenuModule - provide a menu option for the trees options
*/
-class TreesMenuModule extends AbstractModule implements ModuleInterface, ModuleMenuInterface
+class TreesMenuModule extends AbstractModule implements ModuleMenuInterface
{
use ModuleMenuTrait;
diff --git a/app/Module/UpcomingAnniversariesModule.php b/app/Module/UpcomingAnniversariesModule.php
index e7529e314f..89a0e144b2 100644
--- a/app/Module/UpcomingAnniversariesModule.php
+++ b/app/Module/UpcomingAnniversariesModule.php
@@ -28,7 +28,7 @@ use Symfony\Component\HttpFoundation\Request;
/**
* Class UpcomingAnniversariesModule
*/
-class UpcomingAnniversariesModule extends AbstractModule implements ModuleInterface, ModuleBlockInterface
+class UpcomingAnniversariesModule extends AbstractModule implements ModuleBlockInterface
{
use ModuleBlockTrait;
diff --git a/app/Module/UserFavoritesModule.php b/app/Module/UserFavoritesModule.php
index c670427e59..abaf28c432 100644
--- a/app/Module/UserFavoritesModule.php
+++ b/app/Module/UserFavoritesModule.php
@@ -30,7 +30,7 @@ use Symfony\Component\HttpFoundation\Request;
/**
* Class UserFavoritesModule
*/
-class UserFavoritesModule extends AbstractModule implements ModuleInterface, ModuleBlockInterface
+class UserFavoritesModule extends AbstractModule implements ModuleBlockInterface
{
use ModuleBlockTrait;
diff --git a/app/Module/UserJournalModule.php b/app/Module/UserJournalModule.php
index 2b22ad8eaf..d4af94cf02 100644
--- a/app/Module/UserJournalModule.php
+++ b/app/Module/UserJournalModule.php
@@ -29,7 +29,7 @@ use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
/**
* Class UserJournalModule
*/
-class UserJournalModule extends AbstractModule implements ModuleInterface, ModuleBlockInterface
+class UserJournalModule extends AbstractModule implements ModuleBlockInterface
{
use ModuleBlockTrait;
diff --git a/app/Module/UserMessagesModule.php b/app/Module/UserMessagesModule.php
index 88d747aad3..276ed8c6e5 100644
--- a/app/Module/UserMessagesModule.php
+++ b/app/Module/UserMessagesModule.php
@@ -31,7 +31,7 @@ use Symfony\Component\HttpFoundation\Response;
/**
* Class UserMessagesModule
*/
-class UserMessagesModule extends AbstractModule implements ModuleInterface, ModuleBlockInterface
+class UserMessagesModule extends AbstractModule implements ModuleBlockInterface
{
use ModuleBlockTrait;
diff --git a/app/Module/UserWelcomeModule.php b/app/Module/UserWelcomeModule.php
index b9a0ebb392..0f0aa614c1 100644
--- a/app/Module/UserWelcomeModule.php
+++ b/app/Module/UserWelcomeModule.php
@@ -27,7 +27,7 @@ use Symfony\Component\HttpFoundation\Request;
/**
* Class UserWelcomeModule
*/
-class UserWelcomeModule extends AbstractModule implements ModuleInterface, ModuleBlockInterface
+class UserWelcomeModule extends AbstractModule implements ModuleBlockInterface
{
use ModuleBlockTrait;
diff --git a/app/Module/WelcomeBlockModule.php b/app/Module/WelcomeBlockModule.php
index e83aeda920..c4bace206a 100644
--- a/app/Module/WelcomeBlockModule.php
+++ b/app/Module/WelcomeBlockModule.php
@@ -27,7 +27,7 @@ use Symfony\Component\HttpFoundation\Request;
/**
* Class WelcomeBlockModule
*/
-class WelcomeBlockModule extends AbstractModule implements ModuleInterface, ModuleBlockInterface
+class WelcomeBlockModule extends AbstractModule implements ModuleBlockInterface
{
use ModuleBlockTrait;
diff --git a/app/Module/YahrzeitModule.php b/app/Module/YahrzeitModule.php
index 9777aa9a8d..81f03d7034 100644
--- a/app/Module/YahrzeitModule.php
+++ b/app/Module/YahrzeitModule.php
@@ -30,7 +30,7 @@ use Symfony\Component\HttpFoundation\Request;
/**
* Class YahrzeitModule
*/
-class YahrzeitModule extends AbstractModule implements ModuleInterface, ModuleBlockInterface
+class YahrzeitModule extends AbstractModule implements ModuleBlockInterface
{
use ModuleBlockTrait;
diff --git a/app/Theme/AbstractTheme.php b/app/Theme/AbstractTheme.php
index de71a0e7c3..de3f0c2d0d 100644
--- a/app/Theme/AbstractTheme.php
+++ b/app/Theme/AbstractTheme.php
@@ -90,144 +90,6 @@ abstract class AbstractTheme
}
/**
- * Create scripts for analytics and tracking.
- *
- * @return string
- */
- public function analytics()
- {
- if (!empty($_SERVER['HTTP_DNT'])) {
- return '';
- }
-
- return
- $this->analyticsBingWebmaster(
- Site::getPreference('BING_WEBMASTER_ID')
- ) .
- $this->analyticsGoogleWebmaster(
- Site::getPreference('GOOGLE_WEBMASTER_ID')
- ) .
- $this->analyticsGoogleTracker(
- Site::getPreference('GOOGLE_ANALYTICS_ID')
- ) .
- $this->analyticsPiwikTracker(
- Site::getPreference('PIWIK_URL'),
- Site::getPreference('PIWIK_SITE_ID')
- ) .
- $this->analyticsStatcounterTracker(
- Site::getPreference('STATCOUNTER_PROJECT_ID'),
- Site::getPreference('STATCOUNTER_SECURITY_ID')
- );
- }
-
- /**
- * Create the verification code for Google Webmaster Tools.
- *
- * @param string $verification_id
- *
- * @return string
- */
- public function analyticsBingWebmaster($verification_id): string
- {
- return '<meta name="msvalidate.01" content="' . $verification_id . '">';
- }
-
- /**
- * Create the verification code for Google Webmaster Tools.
- *
- * @param string $verification_id
- *
- * @return string
- */
- public function analyticsGoogleWebmaster($verification_id): string
- {
- return '<meta name="google-site-verification" content="' . $verification_id . '">';
- }
-
- /**
- * Create the tracking code for Google Analytics.
- * See https://developers.google.com/analytics/devguides/collection/analyticsjs/advanced
- *
- * @param string $analytics_id
- *
- * @return string
- */
- public function analyticsGoogleTracker($analytics_id)
- {
- if ($analytics_id) {
- // Add extra dimensions (i.e. filtering categories)
- $dimensions = (object) [
- 'dimension1' => $this->tree ? $this->tree->name() : '-',
- 'dimension2' => $this->tree ? Auth::accessLevel($this->tree) : '-',
- ];
-
- return
- '<script async src="https://www.google-analytics.com/analytics.js"></script>' .
- '<script>' .
- 'window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;' .
- 'ga("create","' . $analytics_id . '","auto");' .
- 'ga("send", "pageview", ' . json_encode($dimensions) . ');' .
- '</script>';
- }
-
- return '';
- }
-
- /**
- * Create the tracking code for Piwik Analytics.
- *
- * @param string $url - The domain/path to Piwik
- * @param string $site_id - The Piwik site identifier
- *
- * @return string
- */
- public function analyticsPiwikTracker($url, $site_id)
- {
- $url = preg_replace([
- '/^https?:\/\//',
- '/\/$/',
- ], '', $url);
-
- if ($url && $site_id) {
- return
- '<script>' .
- 'var _paq=_paq||[];' .
- '(function(){var u=(("https:"==document.location.protocol)?"https://' . $url . '/":"http://' . $url . '/");' .
- '_paq.push(["setSiteId",' . $site_id . ']);' .
- '_paq.push(["setTrackerUrl",u+"piwik.php"]);' .
- '_paq.push(["trackPageView"]);' .
- '_paq.push(["enableLinkTracking"]);' .
- 'var d=document,g=d.createElement("script"),s=d.getElementsByTagName("script")[0];g.defer=true;g.async=true;g.src=u+"piwik.js";' .
- 's.parentNode.insertBefore(g,s);})();' .
- '</script>';
- }
-
- return '';
- }
-
- /**
- * Create the tracking code for Statcounter.
- *
- * @param string $project_id - The statcounter project ID
- * @param string $security_id - The statcounter security ID
- *
- * @return string
- */
- public function analyticsStatcounterTracker($project_id, $security_id)
- {
- if ($project_id && $security_id) {
- return
- '<script>' .
- 'var sc_project=' . (int) $project_id . ',sc_invisible=1,sc_security="' . $security_id .
- '",scJsHost = (("https:"===document.location.protocol)?"https://secure.":"http://www.");' .
- 'document.write("<sc"+"ript src=\'"+scJsHost+"statcounter.com/counter/counter.js\'></"+"script>");' .
- '</script>';
- }
-
- return '';
- }
-
- /**
* Where are our CSS, JS and other assets?
*
* @deprecated - use the constant directly
diff --git a/app/Theme/ThemeInterface.php b/app/Theme/ThemeInterface.php
index afe9d1ea80..fd6d1469d0 100644
--- a/app/Theme/ThemeInterface.php
+++ b/app/Theme/ThemeInterface.php
@@ -30,13 +30,6 @@ use Symfony\Component\HttpFoundation\Request;
interface ThemeInterface
{
/**
- * Create scripts for analytics and tracking.
- *
- * @return string
- */
- public function analytics();
-
- /**
* Create a contact link for a user.
*
* @param User $user
diff --git a/modules_v4/custom.example/module.php b/modules_v4/custom.example/module.php
index 4af36139c2..b74740d4ab 100644
--- a/modules_v4/custom.example/module.php
+++ b/modules_v4/custom.example/module.php
@@ -36,7 +36,7 @@ use Fisharebest\Webtrees\Module\ModuleInterface;
* We return an anonymouse class here. This prevents conflict with existing
* class names.
*/
-return new class extends AbstractModule implements ModuleInterface, ModuleCustomInterface {
+return new class extends AbstractModule implements ModuleCustomInterface {
// We implement ModuleCustomInterface, so we must also use the corresponding trait.
use ModuleCustomTrait;
diff --git a/resources/views/admin/analytics/bing-webmaster-form.phtml b/resources/views/admin/analytics/bing-webmaster-form.phtml
new file mode 100644
index 0000000000..e483ede59a
--- /dev/null
+++ b/resources/views/admin/analytics/bing-webmaster-form.phtml
@@ -0,0 +1,12 @@
+<?php use Fisharebest\Webtrees\I18N; ?>
+
+<div class="row form-group">
+ <label for="BING_WEBMASTER_ID" class="col-sm-3 col-form-label">
+ <?= I18N::translate('Site verification code') ?>
+ </label>
+
+ <div class="col-sm-9">
+ <input type="text" class="form-control" id="BING_WEBMASTER_ID" name="BING_WEBMASTER_ID" value="<?= e($BING_WEBMASTER_ID ?? '') ?>" maxlength="255" pattern="[0-9a-zA-Z+=/_:.!-]*">
+ </div>
+</div>
+
diff --git a/resources/views/admin/analytics/bing-webmaster-snippet.phtml b/resources/views/admin/analytics/bing-webmaster-snippet.phtml
new file mode 100644
index 0000000000..f78fb17c81
--- /dev/null
+++ b/resources/views/admin/analytics/bing-webmaster-snippet.phtml
@@ -0,0 +1 @@
+<meta name="msvalidate.01" content="<?= e($BING_WEBMASTER_ID ?? '') ?>">
diff --git a/resources/views/admin/analytics/edit.phtml b/resources/views/admin/analytics/edit.phtml
new file mode 100644
index 0000000000..ce9879b5c1
--- /dev/null
+++ b/resources/views/admin/analytics/edit.phtml
@@ -0,0 +1,35 @@
+<?php use Fisharebest\Webtrees\I18N; ?>
+
+<?= view('components/breadcrumbs', ['links' => [route('admin-control-panel') => I18N::translate('Control panel'), route('analytics') => I18N::translate('Tracking and analytics'), $title]]) ?>
+
+<h1><?= $title ?></h1>
+
+<form method="post" class="form-horizontal">
+ <?= csrf_field() ?>
+ <?= $form_fields ?>
+
+ <div class="row form-group">
+ <div class="col-sm-3">
+ <?= I18N::translate('Preview') ?>
+ </div>
+
+ <div class="col-sm-9">
+ <pre><code><?= e($preview) ?></code></pre>
+ </div>
+ </div>
+
+
+ <div class="row form-group">
+ <div class="offset-sm-3 col-sm-9">
+ <button type="submit" class="btn btn-primary">
+ <?= view('icons/save') ?>
+ <?= I18N::translate('save') ?>
+ </button>
+
+ <a href="<?= e(route('analytics')) ?>" class="btn btn-secondary">
+ <?= view('icons/cancel') ?>
+ <?= I18N::translate('cancel') ?>
+ </a>
+ </div>
+ </div>
+</form>
diff --git a/resources/views/admin/analytics/google-analytics-form.phtml b/resources/views/admin/analytics/google-analytics-form.phtml
new file mode 100644
index 0000000000..a246214c5c
--- /dev/null
+++ b/resources/views/admin/analytics/google-analytics-form.phtml
@@ -0,0 +1,11 @@
+<?php use Fisharebest\Webtrees\I18N; ?>
+
+<div class="row form-group">
+ <label for="GOOGLE_ANALYTICS_ID" class="col-sm-3 col-form-label">
+ <?= /* I18N: A configuration setting */ I18N::translate('Site identification code') ?>
+ <span class="sr-only">Google Analytics</span>
+ </label>
+ <div class="col-sm-9">
+ <input type="text" class="form-control" id="GOOGLE_ANALYTICS_ID" name="GOOGLE_ANALYTICS_ID" value="<?= e($GOOGLE_ANALYTICS_ID ?? '') ?>" placeholder="UA-12345-6" maxlength="255" pattern="UA-[0-9]+-[0-9]+">
+ </div>
+</div>
diff --git a/resources/views/admin/analytics/google-analytics-snippet.phtml b/resources/views/admin/analytics/google-analytics-snippet.phtml
new file mode 100644
index 0000000000..e55b77b528
--- /dev/null
+++ b/resources/views/admin/analytics/google-analytics-snippet.phtml
@@ -0,0 +1,7 @@
+<script async src="https://www.google-analytics.com/analytics.js"></script>
+<script>
+ window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};
+ ga.l=+new Date;
+ ga("create", "<?= e($GOOGLE_ANALYTICS_ID ?? '') ?>", "auto");
+ ga("send", "pageview", <?= json_encode($dimensions) ?>);
+</script>
diff --git a/resources/views/admin/analytics/google-webmaster-form.phtml b/resources/views/admin/analytics/google-webmaster-form.phtml
new file mode 100644
index 0000000000..4e3a7bd731
--- /dev/null
+++ b/resources/views/admin/analytics/google-webmaster-form.phtml
@@ -0,0 +1,12 @@
+<?php use Fisharebest\Webtrees\I18N; ?>
+
+<div class="row form-group">
+ <label for="GOOGLE_WEBMASTER_ID" class="col-sm-3 col-form-label">
+ <?= /* I18N: A configuration setting */ I18N::translate('Site verification code') ?>
+ <span class="sr-only">Google Webmaster Tools</span>
+ </label>
+ <div class="col-sm-9">
+ <input type="text" class="form-control" id="GOOGLE_WEBMASTER_ID" name="GOOGLE_WEBMASTER_ID" value="<?= e($GOOGLE_WEBMASTER_ID ?? '') ?>" maxlength="255" pattern="[0-9a-zA-Z+=/_:.!-]*">
+ </div>
+</div>
+
diff --git a/resources/views/admin/analytics/google-webmaster-snippet.phtml b/resources/views/admin/analytics/google-webmaster-snippet.phtml
new file mode 100644
index 0000000000..8728d9ada4
--- /dev/null
+++ b/resources/views/admin/analytics/google-webmaster-snippet.phtml
@@ -0,0 +1 @@
+<meta name="google-site-verification" content="<?= e($GOOGLE_WEBMASTER_ID ?? '') ?>">
diff --git a/resources/views/admin/analytics/index.phtml b/resources/views/admin/analytics/index.phtml
new file mode 100644
index 0000000000..1e31c27075
--- /dev/null
+++ b/resources/views/admin/analytics/index.phtml
@@ -0,0 +1,36 @@
+<?php use Fisharebest\Webtrees\I18N; ?>
+<?php use Fisharebest\Webtrees\Site; ?>
+
+<?= view('components/breadcrumbs', ['links' => [route('admin-control-panel') => I18N::translate('Control panel'), $title]]) ?>
+
+<h1><?= $title ?></h1>
+
+<p>
+ <?= I18N::translate('If you use one of the following tracking and analytics services, webtrees can add the tracking codes automatically.') ?>
+</p>
+
+<p>
+ <?= I18N::translate('Tracking and analytics are not added to the control panel.') ?>
+</p>
+
+<p>
+ <?= I18N::translate('Site verification codes do not work when webtrees is installed in a subfolder.') ?>
+</p>
+
+<?php foreach ($modules as $module) : ?>
+ <h2>
+ <?= e($module->title()) ?>
+ </h2>
+
+ <p>
+ <a class="btn btn-link" href="<?= e(route('analytics-edit', ['module' => $module->name()])) ?>">
+ <?= view('icons/preferences') ?>
+ <?= I18N::translate('Preferences') ?>
+ </a>
+
+ <a class="btn btn-link" href="<?= e($module->analyticsHomePageUrl()) ?>">
+ <?= view('icons/information') ?>
+ <?= e($module->analyticsHomePageUrl()) ?>
+ </a>
+ </p>
+<?php endforeach ?>
diff --git a/resources/views/admin/analytics/matomo-analytics-form.phtml b/resources/views/admin/analytics/matomo-analytics-form.phtml
new file mode 100644
index 0000000000..dcd4edfe92
--- /dev/null
+++ b/resources/views/admin/analytics/matomo-analytics-form.phtml
@@ -0,0 +1,21 @@
+<?php use Fisharebest\Webtrees\I18N; ?>
+
+<div class="row form-group">
+ <label for="PIWIK_SITE_ID" class="col-sm-3 col-form-label">
+ <?= /* I18N: A configuration setting */ I18N::translate('Site identification code') ?>
+ </label>
+ <div class="col-sm-9">
+ <input type="text" class="form-control" id="PIWIK_SITE_ID" name="PIWIK_SITE_ID" value="<?= e($PIWIK_SITE_ID ?? '') ?>" maxlength="255" pattern="[0-9]+">
+ </div>
+</div>
+
+<!-- PIWIK_URL -->
+<div class="row form-group">
+ <label for="PIWIK_URL" class="col-sm-3 col-form-label">
+ <?= /* I18N: A configuration setting */ I18N::translate('URL') ?>
+ </label>
+ <div class="col-sm-9">
+ <input type="text" class="form-control" id="PIWIK_URL" name="PIWIK_URL" value="<?= e($PIWIK_URL ?? '') ?>" placeholder="//example.com/piwik/" maxlength="255">
+ </div>
+</div>
+
diff --git a/resources/views/admin/analytics/matomo-analytics-snippet.phtml b/resources/views/admin/analytics/matomo-analytics-snippet.phtml
new file mode 100644
index 0000000000..c81a17f402
--- /dev/null
+++ b/resources/views/admin/analytics/matomo-analytics-snippet.phtml
@@ -0,0 +1,12 @@
+<script type="text/javascript">
+ var _paq = window._paq || [];
+ _paq.push(['trackPageView']);
+ _paq.push(['enableLinkTracking']);
+ (function() {
+ var u=<?= json_encode($PIWIK_URL ?? '') ?>;
+ _paq.push(['setTrackerUrl', u+'piwik.php']);
+ _paq.push(['setSiteId', <?= json_encode($PIWIK_SITE_ID ?? '') ?>]);
+ var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
+ g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
+ })();
+</script>
diff --git a/resources/views/admin/analytics/statcounter-form.phtml b/resources/views/admin/analytics/statcounter-form.phtml
new file mode 100644
index 0000000000..23a38294b0
--- /dev/null
+++ b/resources/views/admin/analytics/statcounter-form.phtml
@@ -0,0 +1,20 @@
+<?php use Fisharebest\Webtrees\I18N; ?>
+
+<div class="row form-group">
+ <label for="STATCOUNTER_PROJECT_ID" class="col-sm-3 col-form-label">
+ <?= /* I18N: A configuration setting */ I18N::translate('Site identification code') ?>
+ </label>
+ <div class="col-sm-9">
+ <input type="text" class="form-control" id="STATCOUNTER_PROJECT_ID" name="STATCOUNTER_PROJECT_ID" value="<?= e($STATCOUNTER_PROJECT_ID ?? '') ?>" maxlength="255" pattern="[0-9]+">
+ </div>
+</div>
+
+<!-- STATCOUNTER_SECURITY_ID -->
+<div class="row form-group">
+ <label for="STATCOUNTER_SECURITY_ID" class="col-sm-3 col-form-label">
+ <?= /* I18N: A configuration setting */ I18N::translate('Security code') ?>
+ </label>
+ <div class="col-sm-9">
+ <input type="text" class="form-control" id="STATCOUNTER_SECURITY_ID" name="STATCOUNTER_SECURITY_ID" value="<?= e($STATCOUNTER_SECURITY_ID ?? '') ?>" maxlength="255" pattern="[0-9a-zA-Z]+">
+ </div>
+</div>
diff --git a/resources/views/admin/analytics/statcounter-snippet.phtml b/resources/views/admin/analytics/statcounter-snippet.phtml
new file mode 100644
index 0000000000..13dfa49009
--- /dev/null
+++ b/resources/views/admin/analytics/statcounter-snippet.phtml
@@ -0,0 +1,9 @@
+<script>
+ var sc_project=<?= json_encode((int) ($STATCOUNTER_PROJECT_ID ?? '')) ?>;
+ var sc_invisible=1;
+ var sc_security=<?= json_encode($STATCOUNTER_SECURITY_ID ?? '') ?>;
+ var sc_https=1;
+ var scJsHost = (("https:" == document.location.protocol) ? "https://secure." : "http://www.");
+ document.write("<sc"+"ript type='text/javascript' async src='" + scJsHost + "statcounter.com/counter/counter_xhtml.js'></"+"script>");
+</script>
+
diff --git a/resources/views/admin/control-panel.phtml b/resources/views/admin/control-panel.phtml
index 6f2277a342..b575b41729 100644
--- a/resources/views/admin/control-panel.phtml
+++ b/resources/views/admin/control-panel.phtml
@@ -64,8 +64,8 @@
</a>
</li>
<li>
- <span class="fa-li"><?= view('icons/preferences') ?></span>
- <a href="<?= e(route('admin-site-analytics')) ?>">
+ <span class="fa-li"><?= view('icons/wizard') ?></span>
+ <a href="<?= e(route('analytics')) ?>">
<?= I18N::translate('Tracking and analytics') ?>
</a>
</li>
diff --git a/resources/views/admin/site-analytics.phtml b/resources/views/admin/site-analytics.phtml
deleted file mode 100644
index 3a6b4ab472..0000000000
--- a/resources/views/admin/site-analytics.phtml
+++ /dev/null
@@ -1,136 +0,0 @@
-<?php use Fisharebest\Webtrees\I18N; ?>
-<?php use Fisharebest\Webtrees\Site; ?>
-
-<?= view('components/breadcrumbs', ['links' => [route('admin-control-panel') => I18N::translate('Control panel'), $title]]) ?>
-
-<h1><?= $title ?></h1>
-
-<form method="post" class="form-horizontal">
- <?= csrf_field() ?>
-
- <p>
- <?= I18N::translate('If you use one of the following tracking and analytics services, webtrees can add the tracking codes automatically.') ?>
- </p>
-
- <h2><a href="https://www.bing.com/toolbox/webmaster/">Bing Webmaster Tools</a></h2>
-
- <!-- BING_WEBMASTER_ID -->
- <div class="row form-group">
- <label for="BING_WEBMASTER_ID" class="col-sm-3 col-form-label">
- <?= /* I18N: A configuration setting */ I18N::translate('Site verification code') ?>
- <span class="sr-only">Google Webmaster Tools</span>
- </label>
- <div class="col-sm-9">
- <input
- type="text" class="form-control"
- id="BING_WEBMASTER_ID" name="BING_WEBMASTER_ID" <?= dirname(parse_url(WT_BASE_URL, PHP_URL_PATH)) === '/' ? '' : 'disabled' ?>
- value="<?= e(Site::getPreference('BING_WEBMASTER_ID')) ?>"
- maxlength="255" pattern="[0-9a-zA-Z+=/_:.!-]*"
- >
- <p class="small text-muted">
- <?= /* I18N: Help text for the "Site verification code for Google Webmaster Tools" site configuration setting */ I18N::translate('Site verification codes do not work when webtrees is installed in a subfolder.') ?>
- </p>
- </div>
- </div>
-
- <h2><a href="https://www.google.com/webmasters/">Google Webmaster Tools</a></h2>
-
- <!-- GOOGLE_WEBMASTER_ID -->
- <div class="row form-group">
- <label for="GOOGLE_WEBMASTER_ID" class="col-sm-3 col-form-label">
- <?= /* I18N: A configuration setting */ I18N::translate('Site verification code') ?>
- <span class="sr-only">Google Webmaster Tools</span>
- </label>
- <div class="col-sm-9">
- <input
- type="text" class="form-control"
- id="GOOGLE_WEBMASTER_ID" name="GOOGLE_WEBMASTER_ID" <?= dirname(parse_url(WT_BASE_URL, PHP_URL_PATH)) === '/' ? '' : 'disabled' ?>
- value="<?= e(Site::getPreference('GOOGLE_WEBMASTER_ID')) ?>"
- maxlength="255" pattern="[0-9a-zA-Z+=/_:.!-]*"
- >
- <p class="small text-muted">
- <?= /* I18N: Help text for the "Site verification code for Google Webmaster Tools" site configuration setting */ I18N::translate('Site verification codes do not work when webtrees is installed in a subfolder.') ?>
- </p>
- </div>
- </div>
-
- <h2><a href="https://www.google.com/analytics/">Google Analytics</a></h2>
-
- <!-- GOOGLE_ANALYTICS_ID -->
- <div class="row form-group">
- <label for="GOOGLE_ANALYTICS_ID" class="col-sm-3 col-form-label">
- <?= /* I18N: A configuration setting */ I18N::translate('Site identification code') ?>
- <span class="sr-only">Google Analytics</span>
- </label>
- <div class="col-sm-9">
- <input type="text" class="form-control" id="GOOGLE_ANALYTICS_ID" name="GOOGLE_ANALYTICS_ID" value="<?= e(Site::getPreference('GOOGLE_ANALYTICS_ID')) ?>" placeholder="UA-12345-6" maxlength="255" pattern="UA-[0-9]+-[0-9]+">
- <p class="small text-muted">
- <?= I18N::translate('Tracking and analytics are not added to the control panel.') ?>
- </p>
- </div>
- </div>
-
- <h2><a href="https://piwik.org/">Piwik</a></h2>
-
- <!-- PIWIK_SITE_ID -->
- <div class="row form-group">
- <label for="PIWIK_SITE_ID" class="col-sm-3 col-form-label">
- <?= /* I18N: A configuration setting */ I18N::translate('Site identification code') ?>
- </label>
- <div class="col-sm-9">
- <input type="text" class="form-control" id="PIWIK_SITE_ID" name="PIWIK_SITE_ID" value="<?= e(Site::getPreference('PIWIK_SITE_ID')) ?>" maxlength="255" pattern="[0-9]+">
- </div>
- </div>
-
- <!-- PIWIK_URL -->
- <div class="row form-group">
- <label for="PIWIK_URL" class="col-sm-3 col-form-label">
- <?= /* I18N: A configuration setting */ I18N::translate('URL') ?>
- </label>
- <div class="col-sm-9">
- <input type="text" class="form-control" id="PIWIK_URL" name="PIWIK_URL" value="<?= e(Site::getPreference('PIWIK_URL')) ?>" placeholder="example.com/piwik" maxlength="255">
- <p class="small text-muted">
- <?= I18N::translate('Tracking and analytics are not added to the control panel.') ?>
- </p>
- </div>
- </div>
-
- <h2><a href="https://statcounter.com/">StatCounter</a></h2>
-
- <!-- STATCOUNTER_PROJECT_ID -->
- <div class="row form-group">
- <label for="STATCOUNTER_PROJECT_ID" class="col-sm-3 col-form-label">
- <?= /* I18N: A configuration setting */ I18N::translate('Site identification code') ?>
- </label>
- <div class="col-sm-9">
- <input type="text" class="form-control" id="STATCOUNTER_PROJECT_ID" name="STATCOUNTER_PROJECT_ID" value="<?= e(Site::getPreference('STATCOUNTER_PROJECT_ID')) ?>" maxlength="255" pattern="[0-9]+">
- </div>
- </div>
-
- <!-- STATCOUNTER_SECURITY_ID -->
- <div class="row form-group">
- <label for="STATCOUNTER_SECURITY_ID" class="col-sm-3 col-form-label">
- <?= /* I18N: A configuration setting */ I18N::translate('Security code') ?>
- </label>
- <div class="col-sm-9">
- <input type="text" class="form-control" id="STATCOUNTER_SECURITY_ID" name="STATCOUNTER_SECURITY_ID" value="<?= e(Site::getPreference('STATCOUNTER_SECURITY_ID')) ?>" maxlength="255" pattern="[0-9a-zA-Z]+">
- <p class="small text-muted">
- <?= I18N::translate('Tracking and analytics are not added to the control panel.') ?>
- </p>
- </div>
- </div>
-
- <div class="row form-group">
- <div class="offset-sm-3 col-sm-9">
- <button type="submit" class="btn btn-primary">
- <?= view('icons/save') ?>
- <?= I18N::translate('save') ?>
- </button>
-
- <a href="<?= e(route('admin-control-panel')) ?>" class="btn btn-secondary">
- <?= view('icons/cancel') ?>
- <?= I18N::translate('cancel') ?>
- </a>
- </div>
- </div>
-</form>
diff --git a/resources/views/icons/help.phtml b/resources/views/icons/help.phtml
index 3d2e8cd1ca..cab0e99de6 100644
--- a/resources/views/icons/help.phtml
+++ b/resources/views/icons/help.phtml
@@ -1 +1 @@
-<i class="fas fa-info-circle fa-fw wt-icon-help" aria-hidden="true"></i>
+<i class="fas fa-question-circle fa-fw wt-icon-help" aria-hidden="true"></i>
diff --git a/resources/views/icons/information.phtml b/resources/views/icons/information.phtml
new file mode 100644
index 0000000000..9152155dc8
--- /dev/null
+++ b/resources/views/icons/information.phtml
@@ -0,0 +1 @@
+<i class="fas fa-info-circle fa-fw wt-icon-information" aria-hidden="true"></i>
diff --git a/resources/views/layouts/default.phtml b/resources/views/layouts/default.phtml
index e4c018135c..236c6f3358 100644
--- a/resources/views/layouts/default.phtml
+++ b/resources/views/layouts/default.phtml
@@ -2,6 +2,8 @@
<?php use Fisharebest\Webtrees\DebugBar; ?>
<?php use Fisharebest\Webtrees\FlashMessages; ?>
<?php use Fisharebest\Webtrees\I18N; ?>
+<?php use Fisharebest\Webtrees\Module; ?>
+<?php use Fisharebest\Webtrees\Module\ModuleAnalyticsInterface; ?>
<?php use Fisharebest\Webtrees\Theme; ?>
<?php use Fisharebest\Webtrees\View; ?>
<?php use Fisharebest\Webtrees\Webtrees; ?>
@@ -41,7 +43,7 @@
<?= View::stack('styles') ?>
- <?= Theme::theme()->analytics() ?>
+ <?= Module::findByInterface(ModuleAnalyticsInterface::class)->filter(function (ModuleAnalyticsInterface $module): bool { return $module->analyticsCanShow(); })->map(function (ModuleAnalyticsInterface $module): string { return $module->analyticsSnippet($module->analyticsParameters()); })->implode('') ?>
<?= DebugBar::renderHead() ?>
</head>
diff --git a/routes/web.php b/routes/web.php
index 9f62f447c3..44acaba735 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -24,6 +24,9 @@ $routes = [];
// Admin routes.
if (Auth::isAdmin()) {
$routes += [
+ 'GET:analytics' => 'Admin\\AnalyticsController@list',
+ 'GET:analytics-edit' => 'Admin\\AnalyticsController@edit',
+ 'POST:analytics-edit' => 'Admin\\AnalyticsController@save',
'GET:admin-blocks' => 'AdminController@blocks',
'GET:admin-charts' => 'AdminController@charts',
'GET:admin-control-panel' => 'AdminController@controlPanel',
@@ -56,8 +59,6 @@ if (Auth::isAdmin()) {
'POST:admin-site-registration' => 'AdminSiteController@registrationSave',
'GET:admin-site-languages' => 'AdminSiteController@languagesForm',
'POST:admin-site-languages' => 'AdminSiteController@languagesSave',
- 'GET:admin-site-analytics' => 'AdminSiteController@analyticsForm',
- 'POST:admin-site-analytics' => 'AdminSiteController@analyticsSave',
'GET:admin-site-logs' => 'AdminSiteController@logs',
'GET:admin-site-logs-data' => 'AdminSiteController@logsData',
'POST:admin-site-logs-delete' => 'AdminSiteController@logsDelete',