.
*/
namespace Fisharebest\Webtrees\Theme;
use Fisharebest\Webtrees\Auth;
use Fisharebest\Webtrees\Controller\PageController;
use Fisharebest\Webtrees\Database;
use Fisharebest\Webtrees\Fact;
use Fisharebest\Webtrees\Filter;
use Fisharebest\Webtrees\FlashMessages;
use Fisharebest\Webtrees\Functions\Functions;
use Fisharebest\Webtrees\GedcomRecord;
use Fisharebest\Webtrees\GedcomTag;
use Fisharebest\Webtrees\HitCounter;
use Fisharebest\Webtrees\Html;
use Fisharebest\Webtrees\I18N;
use Fisharebest\Webtrees\Individual;
use Fisharebest\Webtrees\Menu;
use Fisharebest\Webtrees\Module;
use Fisharebest\Webtrees\Module\AncestorsChartModule;
use Fisharebest\Webtrees\Module\CompactTreeChartModule;
use Fisharebest\Webtrees\Module\DescendancyChartModule;
use Fisharebest\Webtrees\Module\FamilyBookChartModule;
use Fisharebest\Webtrees\Module\FamilyTreeFavoritesModule;
use Fisharebest\Webtrees\Module\FanChartModule;
use Fisharebest\Webtrees\Module\GoogleMapsModule;
use Fisharebest\Webtrees\Module\HourglassChartModule;
use Fisharebest\Webtrees\Module\InteractiveTreeModule;
use Fisharebest\Webtrees\Module\LifespansChartModule;
use Fisharebest\Webtrees\Module\PedigreeChartModule;
use Fisharebest\Webtrees\Module\RelationshipsChartModule;
use Fisharebest\Webtrees\Module\StatisticsChartModule;
use Fisharebest\Webtrees\Module\TimelineChartModule;
use Fisharebest\Webtrees\Module\UserFavoritesModule;
use Fisharebest\Webtrees\Site;
use Fisharebest\Webtrees\Theme;
use Fisharebest\Webtrees\Tree;
use Fisharebest\Webtrees\User;
use Symfony\Component\HttpFoundation\Request;
use stdClass;
/**
* Common functions for all themes.
*/
abstract class AbstractTheme {
/**
* Where are our CSS, JS and other assets?
*/
const THEME_DIR = '_common';
const ASSET_DIR = 'themes/' . self::THEME_DIR . '/css-2.0.0/';
const STYLESHEET = self::ASSET_DIR . 'style.css';
// Icons are created using
const ICONS = [
// Icons for GEDCOM records
'family' => 'fa fa-users',
'individual' => 'fa fa-user',
'media' => 'fa fa-file-image-o',
'note' => 'fa fa-sticky-note-o',
'repository' => 'fa fa-institution',
'source' => 'fa fa-file-text-o',
'submission' => 'fa fa-upload',
'submitter' => 'fa fa-user-plus',
// Icons for sexes
'F' => 'fa fa-venus',
'M' => 'fa fa-mars',
'U' => 'fa fa-genderless',
// Icons for editing
'add' => 'fa fa-plus',
'config' => 'fa fa-cogs',
'copy' => 'fa fa-copy',
'create' => 'fa fa-plus',
'delete' => 'fa fa-trash',
'edit' => 'fa fa-pencil',
'link' => 'fa fa-link',
'unlink' => 'fa fa-unlink',
// Icons for arrows
'arrow-down' => 'fa fa-arrow-down',
'arrow-left' => 'fa fa-arrow-left',
'arrow-right' => 'fa fa-arrow-right',
'arrow-up' => 'fa fa-arrow-up',
// Status icons
'error' => 'fa fa-exclamation-triangle',
'info' => 'fa fa-info-circle',
'warning' => 'fa fa-exclamation-circle',
// Icons for file types
'mime-application-pdf' => '',
'mime-text-html' => '',
// Other icons
'mail' => 'fa fa-envelope-o',
'help' => 'fa fa-info-circle',
'search' => 'fa fa-search',
];
/** @var Request */
protected $request;
/** @var Tree */
protected $tree;
/** @var string An escaped version of the "ged=XXX" URL parameter */
protected $tree_url;
/** @var int The number of times this page has been shown */
protected $page_views;
/**
* Custom themes should place their initialization code in the function hookAfterInit(), not in
* the constructor, as all themes get constructed - whether they are used or not.
*/
final public function __construct() {
}
/**
* Create accessibility links for the header.
*
* "Skip to content" allows keyboard only users to navigate over the headers without
* pressing TAB many times.
*
* @return string
*/
protected function accessibilityLinks() {
return
'
';
}
/**
* Create scripts for analytics and tracking.
*
* @return string
*/
protected function analytics() {
if ($this->themeId() === '_administration' || !empty($_SERVER['HTTP_DNT'])) {
return '';
} else {
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
*/
protected function analyticsBingWebmaster($verification_id) {
// Only need to add this to the home page.
if (WT_SCRIPT_NAME === 'index.php' && $verification_id) {
return '';
} else {
return '';
}
}
/**
* Create the verification code for Google Webmaster Tools.
*
* @param string $verification_id
*
* @return string
*/
protected function analyticsGoogleWebmaster($verification_id) {
// Only need to add this to the home page.
if (WT_SCRIPT_NAME === 'index.php' && $verification_id) {
return '';
} else {
return '';
}
}
/**
* Create the tracking code for Google Analytics.
*
* See https://developers.google.com/analytics/devguides/collection/analyticsjs/advanced
*
* @param string $analytics_id
*
* @return string
*/
protected function analyticsGoogleTracker($analytics_id) {
if ($analytics_id) {
// Add extra dimensions (i.e. filtering categories)
$dimensions = (object) [
'dimension1' => $this->tree ? $this->tree->getName() : '-',
'dimension2' => $this->tree ? Auth::accessLevel($this->tree) : '-',
];
return
'' .
'';
} else {
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
*/
protected function analyticsPiwikTracker($url, $site_id) {
$url = preg_replace(['/^https?:\/\//', '/\/$/'], '', $url);
if ($url && $site_id) {
return
'';
} else {
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
*/
protected function analyticsStatcounterTracker($project_id, $security_id) {
if ($project_id && $security_id) {
return
'';
} else {
return '';
}
}
/**
* Where are our CSS, JS and other assets?
*
* @deprecated - use the constant directly
*
* @return string A relative path, such as "themes/foo/"
*/
public function assetUrl() {
return self::ASSET_DIR;
}
/**
* Create the top of the .
*
* @return string
*/
public function bodyHeader() {
return
'' .
'' .
'
' .
'
' .
$this->headerContent() .
'
' .
'
' .
'' .
'' .
'
' .
$this->flashMessagesContainer(FlashMessages::getMessages());
}
/**
* Create a contact link for a user.
*
* @param User $user
*
* @return string
*/
public function contactLink(User $user) {
$method = $user->getPreference('contactmethod');
switch ($method) {
case 'none':
return '';
case 'mailto':
return '' . $user->getRealNameHtml() . '';
default:
return '' . $user->getRealNameHtml() . '';
}
}
/**
* Create contact link for both technical and genealogy support.
*
* @param User $user
*
* @return string
*/
protected function contactLinkEverything(User $user) {
return I18N::translate('For technical support or genealogy questions contact %s.', $this->contactLink($user));
}
/**
* Create contact link for genealogy support.
*
* @param User $user
*
* @return string
*/
protected function contactLinkGenealogy(User $user) {
return I18N::translate('For help with genealogy questions contact %s.', $this->contactLink($user));
}
/**
* Create contact link for technical support.
*
* @param User $user
*
* @return string
*/
protected function contactLinkTechnical(User $user) {
return I18N::translate('For technical support and information contact %s.', $this->contactLink($user));
}
/**
* Create contact links for the page footer.
*
* @return string
*/
protected function contactLinks() {
$contact_user = User::find($this->tree->getPreference('CONTACT_USER_ID'));
$webmaster_user = User::find($this->tree->getPreference('WEBMASTER_USER_ID'));
if ($contact_user && $contact_user === $webmaster_user) {
return $this->contactLinkEverything($contact_user);
} elseif ($contact_user && $webmaster_user) {
return $this->contactLinkGenealogy($contact_user) . ' ' . $this->contactLinkTechnical($webmaster_user);
} elseif ($contact_user) {
return $this->contactLinkGenealogy($contact_user);
} elseif ($webmaster_user) {
return $this->contactLinkTechnical($webmaster_user);
} else {
return '';
}
}
/**
* Create a cookie warning.
*
* @return string
*/
public function cookieWarning() {
if (
empty($_SERVER['HTTP_DNT']) &&
empty($_COOKIE['cookie']) &&
(Site::getPreference('GOOGLE_ANALYTICS_ID') === '1' || Site::getPreference('PIWIK_SITE_ID') === '1' || Site::getPreference('STATCOUNTER_PROJECT_ID') === '1')
) {
return
'
' .
I18N::translate('Cookies') . ' - ' .
I18N::translate('This website uses cookies to learn about visitor behaviour.') . ' ' .
'' .
'
';
} else {
return '';
}
}
/**
* Create the tag.
*
* @return string
*/
public function doctype() {
return '';
}
/**
* HTML link to a "favorites icon".
*
* @return string
*/
protected function favicon() {
return
'' .
'' .
'';
}
/**
* Add markup to a flash message.
*
* @param stdClass $message
*
* @return string
*/
protected function flashMessageContainer(stdClass $message) {
return $this->htmlAlert($message->text, $message->status, true);
}
/**
* Create a container for messages that are "flashed" to the session
* on one request, and displayed on another. If there are many messages,
* the container may need a max-height and scroll-bar.
*
* @param stdClass[] $messages
*
* @return string
*/
protected function flashMessagesContainer(array $messages) {
$html = '';
foreach ($messages as $message) {
$html .= $this->flashMessageContainer($message);
}
if ($html) {
return '
' . $html . '
';
} else {
return '';
}
}
/**
* Close the main content and create the