. */ 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 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-1.7.8/'; 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 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 ''; } 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
' . '
' . ''; } /** * Create the contents of the