| Total Complexity | 229 |
| Total Lines | 1740 |
| Duplicated Lines | 0 % |
| Changes | 0 | ||
Complex classes like AbstractTheme often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use AbstractTheme, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 58 | abstract class AbstractTheme { |
||
| 59 | /** |
||
| 60 | * Where are our CSS, JS and other assets? |
||
| 61 | */ |
||
| 62 | const THEME_DIR = '_common'; |
||
| 63 | const ASSET_DIR = 'themes/' . self::THEME_DIR . '/css-2.0.0/'; |
||
| 64 | const STYLESHEET = self::ASSET_DIR . 'style.css'; |
||
| 65 | |||
| 66 | // Icons are created using <i class="..."></i> |
||
| 67 | const ICONS = [ |
||
| 68 | // Icons for GEDCOM records |
||
| 69 | 'family' => 'fas fa-users', |
||
| 70 | 'individual' => 'far fa-user', |
||
| 71 | 'media' => 'far fa-file-image', |
||
| 72 | 'note' => 'far fa-sticky-note', |
||
| 73 | 'repository' => 'fas fa-university', |
||
| 74 | 'source' => 'far fa-file-alt', |
||
| 75 | 'submission' => 'fas fa-upload', |
||
| 76 | 'submitter' => 'fas fa-user-plus', |
||
| 77 | |||
| 78 | // Icons for sexes |
||
| 79 | 'F' => 'fas fa-venus', |
||
| 80 | 'M' => 'fas fa-mars', |
||
| 81 | 'U' => 'fas fa-genderless', |
||
| 82 | |||
| 83 | // Icons for editing |
||
| 84 | 'add' => 'fas fa-plus', |
||
| 85 | 'config' => 'far fa-cogs', |
||
| 86 | 'copy' => 'far fa-copy', |
||
| 87 | 'create' => 'fas fa-plus', |
||
| 88 | 'delete' => 'fas fa-trash-alt', |
||
| 89 | 'edit' => 'fas fa-pencil-alt', |
||
| 90 | 'link' => 'fas fa-link', |
||
| 91 | 'unlink' => 'fas fa-unlink', |
||
| 92 | |||
| 93 | // Icons for arrows |
||
| 94 | 'arrow-down' => 'fas fa-arrow-down', |
||
| 95 | 'arrow-left' => 'fas fa-arrow-left', |
||
| 96 | 'arrow-right' => 'fas fa-arrow-right', |
||
| 97 | 'arrow-up' => 'fas fa-arrow-up', |
||
| 98 | |||
| 99 | // Status icons |
||
| 100 | 'error' => 'fas fa-exclamation-triangle', |
||
| 101 | 'info' => 'fas fa-info-circle', |
||
| 102 | 'warning' => 'fas fa-exclamation-circle', |
||
| 103 | |||
| 104 | // Icons for file types |
||
| 105 | 'mime-application-pdf' => '', |
||
| 106 | 'mime-text-html' => '', |
||
| 107 | |||
| 108 | // Other icons |
||
| 109 | 'mail' => 'far fa-envelope', |
||
| 110 | 'help' => 'fas fa-info-circle', |
||
| 111 | 'search' => 'fas fa-search', |
||
| 112 | ]; |
||
| 113 | |||
| 114 | /** @var Request */ |
||
| 115 | protected $request; |
||
| 116 | |||
| 117 | /** @var Tree */ |
||
| 118 | protected $tree; |
||
| 119 | |||
| 120 | /** @var int The number of times this page has been shown */ |
||
| 121 | protected $page_views; |
||
| 122 | |||
| 123 | /** |
||
| 124 | * Custom themes should place their initialization code in the function hookAfterInit(), not in |
||
| 125 | * the constructor, as all themes get constructed - whether they are used or not. |
||
| 126 | */ |
||
| 127 | final public function __construct() { |
||
| 128 | } |
||
| 129 | |||
| 130 | /** |
||
| 131 | * Create accessibility links for the header. |
||
| 132 | * |
||
| 133 | * "Skip to content" allows keyboard only users to navigate over the headers without |
||
| 134 | * pressing TAB many times. |
||
| 135 | * |
||
| 136 | * @return string |
||
| 137 | */ |
||
| 138 | public function accessibilityLinks() { |
||
| 139 | return |
||
| 140 | '<div class="wt-accessibility-links">' . |
||
| 141 | '<a class="sr-only sr-only-focusable btn btn-info btn-sm" href="#content">' . |
||
| 142 | /* I18N: Skip over the headers and menus, to the main content of the page */ I18N::translate('Skip to content') . |
||
| 143 | '</a>' . |
||
| 144 | '</div>'; |
||
| 145 | } |
||
| 146 | |||
| 147 | /** |
||
| 148 | * Create scripts for analytics and tracking. |
||
| 149 | * |
||
| 150 | * @return string |
||
| 151 | */ |
||
| 152 | public function analytics() { |
||
| 153 | if ($this->themeId() === '_administration' || !empty($_SERVER['HTTP_DNT'])) { |
||
| 154 | return ''; |
||
| 155 | } else { |
||
| 156 | return |
||
| 157 | $this->analyticsBingWebmaster( |
||
| 158 | Site::getPreference('BING_WEBMASTER_ID') |
||
| 159 | ) . |
||
| 160 | $this->analyticsGoogleWebmaster( |
||
| 161 | Site::getPreference('GOOGLE_WEBMASTER_ID') |
||
| 162 | ) . |
||
| 163 | $this->analyticsGoogleTracker( |
||
| 164 | Site::getPreference('GOOGLE_ANALYTICS_ID') |
||
| 165 | ) . |
||
| 166 | $this->analyticsPiwikTracker( |
||
| 167 | Site::getPreference('PIWIK_URL'), |
||
| 168 | Site::getPreference('PIWIK_SITE_ID') |
||
| 169 | ) . |
||
| 170 | $this->analyticsStatcounterTracker( |
||
| 171 | Site::getPreference('STATCOUNTER_PROJECT_ID'), |
||
| 172 | Site::getPreference('STATCOUNTER_SECURITY_ID') |
||
| 173 | ); |
||
| 174 | } |
||
| 175 | } |
||
| 176 | |||
| 177 | /** |
||
| 178 | * Create the verification code for Google Webmaster Tools. |
||
| 179 | * |
||
| 180 | * @param string $verification_id |
||
| 181 | * |
||
| 182 | * @return string |
||
| 183 | */ |
||
| 184 | public function analyticsBingWebmaster($verification_id) { |
||
| 185 | // Only need to add this to the home page. |
||
| 186 | if (WT_SCRIPT_NAME === 'index.php' && $verification_id) { |
||
| 187 | return '<meta name="msvalidate.01" content="' . $verification_id . '">'; |
||
| 188 | } else { |
||
| 189 | return ''; |
||
| 190 | } |
||
| 191 | } |
||
| 192 | |||
| 193 | /** |
||
| 194 | * Create the verification code for Google Webmaster Tools. |
||
| 195 | * |
||
| 196 | * @param string $verification_id |
||
| 197 | * |
||
| 198 | * @return string |
||
| 199 | */ |
||
| 200 | public function analyticsGoogleWebmaster($verification_id) { |
||
| 201 | // Only need to add this to the home page. |
||
| 202 | if (WT_SCRIPT_NAME === 'index.php' && $verification_id) { |
||
| 203 | return '<meta name="google-site-verification" content="' . $verification_id . '">'; |
||
| 204 | } else { |
||
| 205 | return ''; |
||
| 206 | } |
||
| 207 | } |
||
| 208 | |||
| 209 | /** |
||
| 210 | * Create the tracking code for Google Analytics. |
||
| 211 | * |
||
| 212 | * See https://developers.google.com/analytics/devguides/collection/analyticsjs/advanced |
||
| 213 | * |
||
| 214 | * @param string $analytics_id |
||
| 215 | * |
||
| 216 | * @return string |
||
| 217 | */ |
||
| 218 | public function analyticsGoogleTracker($analytics_id) { |
||
| 219 | if ($analytics_id) { |
||
| 220 | // Add extra dimensions (i.e. filtering categories) |
||
| 221 | $dimensions = (object) [ |
||
| 222 | 'dimension1' => $this->tree ? $this->tree->getName() : '-', |
||
| 223 | 'dimension2' => $this->tree ? Auth::accessLevel($this->tree) : '-', |
||
| 224 | ]; |
||
| 225 | |||
| 226 | return |
||
| 227 | '<script async src="https://www.google-analytics.com/analytics.js"></script>' . |
||
| 228 | '<script>' . |
||
| 229 | 'window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;' . |
||
| 230 | 'ga("create","' . $analytics_id . '","auto");' . |
||
| 231 | 'ga("send", "pageview", ' . json_encode($dimensions) . ');' . |
||
| 232 | '</script>'; |
||
| 233 | } else { |
||
| 234 | return ''; |
||
| 235 | } |
||
| 236 | } |
||
| 237 | |||
| 238 | /** |
||
| 239 | * Create the tracking code for Piwik Analytics. |
||
| 240 | * |
||
| 241 | * @param string $url - The domain/path to Piwik |
||
| 242 | * @param string $site_id - The Piwik site identifier |
||
| 243 | * |
||
| 244 | * @return string |
||
| 245 | */ |
||
| 246 | public function analyticsPiwikTracker($url, $site_id) { |
||
| 263 | } |
||
| 264 | } |
||
| 265 | |||
| 266 | /** |
||
| 267 | * Create the tracking code for Statcounter. |
||
| 268 | * |
||
| 269 | * @param string $project_id - The statcounter project ID |
||
| 270 | * @param string $security_id - The statcounter security ID |
||
| 271 | * |
||
| 272 | * @return string |
||
| 273 | */ |
||
| 274 | public function analyticsStatcounterTracker($project_id, $security_id) { |
||
| 284 | } |
||
| 285 | } |
||
| 286 | |||
| 287 | /** |
||
| 288 | * Where are our CSS, JS and other assets? |
||
| 289 | * |
||
| 290 | * @deprecated - use the constant directly |
||
| 291 | * |
||
| 292 | * @return string A relative path, such as "themes/foo/" |
||
| 293 | */ |
||
| 294 | public function assetUrl() { |
||
| 296 | } |
||
| 297 | |||
| 298 | /** |
||
| 299 | * Create a contact link for a user. |
||
| 300 | * |
||
| 301 | * @param User $user |
||
| 302 | * |
||
| 303 | * @return string |
||
| 304 | */ |
||
| 305 | public function contactLink(User $user) { |
||
| 306 | $method = $user->getPreference('contactmethod'); |
||
| 307 | |||
| 308 | switch ($method) { |
||
| 309 | case 'none': |
||
| 310 | return ''; |
||
| 311 | case 'mailto': |
||
| 312 | return '<a href="mailto:' . e($user->getEmail()) . '">' . e($user->getRealName()) . '</a>'; |
||
| 313 | default: |
||
| 314 | $url = route(Auth::check() ? 'message' : 'contact', [ |
||
| 315 | 'ged' => $this->tree->getName(), |
||
| 316 | 'to' => $user->getUserName(), |
||
| 317 | 'url' => $this->request->getRequestUri(), |
||
| 318 | ]); |
||
| 319 | |||
| 320 | return '<a href="' . e($url) . '">' . e($user->getRealName()) . '</a>'; |
||
| 321 | } |
||
| 322 | } |
||
| 323 | |||
| 324 | /** |
||
| 325 | * Create contact link for both technical and genealogy support. |
||
| 326 | * |
||
| 327 | * @param User $user |
||
| 328 | * |
||
| 329 | * @return string |
||
| 330 | */ |
||
| 331 | public function contactLinkEverything(User $user) { |
||
| 332 | return I18N::translate('For technical support or genealogy questions contact %s.', $this->contactLink($user)); |
||
| 333 | } |
||
| 334 | |||
| 335 | /** |
||
| 336 | * Create contact link for genealogy support. |
||
| 337 | * |
||
| 338 | * @param User $user |
||
| 339 | * |
||
| 340 | * @return string |
||
| 341 | */ |
||
| 342 | public function contactLinkGenealogy(User $user) { |
||
| 343 | return I18N::translate('For help with genealogy questions contact %s.', $this->contactLink($user)); |
||
| 344 | } |
||
| 345 | |||
| 346 | /** |
||
| 347 | * Create contact link for technical support. |
||
| 348 | * |
||
| 349 | * @param User $user |
||
| 350 | * |
||
| 351 | * @return string |
||
| 352 | */ |
||
| 353 | public function contactLinkTechnical(User $user) { |
||
| 354 | return I18N::translate('For technical support and information contact %s.', $this->contactLink($user)); |
||
| 355 | } |
||
| 356 | |||
| 357 | /** |
||
| 358 | * Create contact links for the page footer. |
||
| 359 | * |
||
| 360 | * @return string |
||
| 361 | */ |
||
| 362 | public function contactLinks() { |
||
| 363 | $contact_user = User::find($this->tree->getPreference('CONTACT_USER_ID')); |
||
| 364 | $webmaster_user = User::find($this->tree->getPreference('WEBMASTER_USER_ID')); |
||
| 365 | |||
| 366 | if ($contact_user && $contact_user === $webmaster_user) { |
||
| 367 | return $this->contactLinkEverything($contact_user); |
||
| 368 | } elseif ($contact_user && $webmaster_user) { |
||
| 369 | return $this->contactLinkGenealogy($contact_user) . '<br>' . $this->contactLinkTechnical($webmaster_user); |
||
| 370 | } elseif ($contact_user) { |
||
| 371 | return $this->contactLinkGenealogy($contact_user); |
||
| 372 | } elseif ($webmaster_user) { |
||
| 373 | return $this->contactLinkTechnical($webmaster_user); |
||
| 374 | } else { |
||
| 375 | return ''; |
||
| 376 | } |
||
| 377 | } |
||
| 378 | |||
| 379 | /** |
||
| 380 | * Create a cookie warning. |
||
| 381 | * |
||
| 382 | * @return string |
||
| 383 | */ |
||
| 384 | public function cookieWarning() { |
||
| 385 | if ( |
||
| 386 | empty($_SERVER['HTTP_DNT']) && |
||
| 387 | empty($_COOKIE['cookie']) && |
||
| 388 | (Site::getPreference('GOOGLE_ANALYTICS_ID') === '1' || Site::getPreference('PIWIK_SITE_ID') === '1' || Site::getPreference('STATCOUNTER_PROJECT_ID') === '1') |
||
| 389 | ) { |
||
| 390 | return |
||
| 391 | '<div class="wt-cookie-warning">' . |
||
| 392 | I18N::translate('Cookies') . ' - ' . |
||
| 393 | I18N::translate('This website uses cookies to learn about visitor behaviour.') . ' ' . |
||
| 394 | '<button onclick="document.cookie=\'cookie=1\'; this.parentNode.classList.add(\'hidden\');">' . I18N::translate('continue') . '</button>' . |
||
| 395 | '</div>'; |
||
| 396 | } else { |
||
| 397 | return ''; |
||
| 398 | } |
||
| 399 | } |
||
| 400 | |||
| 401 | /** |
||
| 402 | * Add markup to the contact links. |
||
| 403 | * |
||
| 404 | * @return string |
||
| 405 | */ |
||
| 406 | public function formatContactLinks() { |
||
| 407 | if ($this->tree) { |
||
| 408 | return '<div class="wt-contact-links">' . $this->contactLinks() . '</div>'; |
||
| 409 | } else { |
||
| 410 | return ''; |
||
| 411 | } |
||
| 412 | } |
||
| 413 | |||
| 414 | /** |
||
| 415 | * Add markup to the hit counter. |
||
| 416 | * |
||
| 417 | * @param int $count |
||
| 418 | * |
||
| 419 | * @return string |
||
| 420 | */ |
||
| 421 | public function formatPageViews($count) { |
||
| 422 | if ($count > 0) { |
||
| 423 | return |
||
| 424 | '<div class="wt-page-views">' . |
||
| 425 | I18N::plural('This page has been viewed %s time.', 'This page has been viewed %s times.', $count, |
||
| 426 | '<span class="odometer">' . I18N::digits($count) . '</span>') . |
||
| 427 | '</div>'; |
||
| 428 | } else { |
||
| 429 | return ''; |
||
| 430 | } |
||
| 431 | } |
||
| 432 | |||
| 433 | /** |
||
| 434 | * Create a pending changes link for the page footer. |
||
| 435 | * |
||
| 436 | * @return string |
||
| 437 | */ |
||
| 438 | public function formatPendingChangesLink() { |
||
| 439 | if ($this->pendingChangesExist()) { |
||
| 440 | return '<div class="pending-changes-link">' . $this->pendingChangesLink() . '</div>'; |
||
| 441 | } else { |
||
| 442 | return ''; |
||
| 443 | } |
||
| 444 | } |
||
| 445 | |||
| 446 | /** |
||
| 447 | * Add markup to the secondary menu. |
||
| 448 | * |
||
| 449 | * @return string |
||
| 450 | */ |
||
| 451 | public function formatSecondaryMenu() { |
||
| 452 | return |
||
| 453 | '<ul class="nav wt-secondary-menu">' . |
||
| 454 | implode('', array_map(function (Menu $menu) { |
||
| 455 | return $this->formatSecondaryMenuItem($menu); |
||
| 456 | }, $this->secondaryMenu())) . |
||
| 457 | '</ul>'; |
||
| 458 | } |
||
| 459 | |||
| 460 | /** |
||
| 461 | * Add markup to an item in the secondary menu. |
||
| 462 | * |
||
| 463 | * @param Menu $menu |
||
| 464 | * |
||
| 465 | * @return string |
||
| 466 | */ |
||
| 467 | public function formatSecondaryMenuItem(Menu $menu) { |
||
| 468 | return $menu->bootstrap4(); |
||
| 469 | } |
||
| 470 | |||
| 471 | /** |
||
| 472 | * Create resources for the colors theme. |
||
| 473 | */ |
||
| 474 | public function hookAfterInit() { |
||
| 475 | } |
||
| 476 | |||
| 477 | /** |
||
| 478 | * Allow themes to add extra scripts to the page footer. |
||
| 479 | * |
||
| 480 | * @return string |
||
| 481 | */ |
||
| 482 | public function hookFooterExtraJavascript() { |
||
| 483 | return ''; |
||
| 484 | } |
||
| 485 | |||
| 486 | /** |
||
| 487 | * Display an icon for this fact. |
||
| 488 | * |
||
| 489 | * @param Fact $fact |
||
| 490 | * |
||
| 491 | * @return string |
||
| 492 | */ |
||
| 493 | public function icon(Fact $fact) { |
||
| 494 | $icon = 'images/facts/' . $fact->getTag() . '.png'; |
||
| 495 | if (file_exists(self::ASSET_DIR . $icon)) { |
||
| 496 | return '<img src="' . self::ASSET_DIR . $icon . '" title="' . GedcomTag::getLabel($fact->getTag()) . '">'; |
||
| 497 | } elseif (file_exists(self::ASSET_DIR . 'images/facts/NULL.png')) { |
||
| 498 | // Spacer image - for alignment - until we move to a sprite. |
||
| 499 | return '<img src="' . Theme::theme()->assetUrl() . 'images/facts/NULL.png">'; |
||
| 500 | } else { |
||
| 501 | return ''; |
||
| 502 | } |
||
| 503 | } |
||
| 504 | |||
| 505 | /** |
||
| 506 | * Decorative icons are used in addition to text. |
||
| 507 | * They need additional markup to hide them from assistive technologies. |
||
| 508 | * |
||
| 509 | * Semantic icons are used in place of text. |
||
| 510 | * They need additional markup to convey their meaning to assistive technologies. |
||
| 511 | * |
||
| 512 | * @link http://fontawesome.io/accessibility |
||
| 513 | * |
||
| 514 | * @param string $icon |
||
| 515 | * @param string $text |
||
| 516 | * |
||
| 517 | * @return string |
||
| 518 | */ |
||
| 519 | public function replacementIconFunction($icon, $text = '') { |
||
| 520 | if (array_key_exists($icon, self::ICONS)) { |
||
| 521 | if ($text === '') { |
||
| 522 | // Decorative icon. Hiden from assistive technology. |
||
| 523 | return '<i class="' . self::ICONS[$icon] . '" aria-hidden="true"></i>'; |
||
| 524 | } else { |
||
| 525 | // Semantic icon. Label for assistive technology. |
||
| 526 | return |
||
| 527 | '<i class="' . self::ICONS[$icon] . '" title="' . $text . '"></i>' . |
||
| 528 | '<span class="sr-only">' . $text . '</span>'; |
||
| 529 | } |
||
| 530 | } else { |
||
| 531 | return $text; |
||
| 532 | } |
||
| 533 | } |
||
| 534 | |||
| 535 | |||
| 536 | /** |
||
| 537 | * Display an individual in a box - for charts, etc. |
||
| 538 | * |
||
| 539 | * @param Individual $individual |
||
| 540 | * |
||
| 541 | * @return string |
||
| 542 | */ |
||
| 543 | public function individualBox(Individual $individual) { |
||
| 544 | $personBoxClass = array_search($individual->getSex(), ['person_box' => 'M', 'person_boxF' => 'F', 'person_boxNN' => 'U']); |
||
| 545 | if ($individual->canShow() && $individual->getTree()->getPreference('SHOW_HIGHLIGHT_IMAGES')) { |
||
| 546 | $thumbnail = $individual->displayImage(40, 50, 'crop', []); |
||
| 547 | } else { |
||
| 548 | $thumbnail = ''; |
||
| 549 | } |
||
| 550 | |||
| 551 | $content = '<span class="namedef name1">' . $individual->getFullName() . '</span>'; |
||
| 552 | $icons = ''; |
||
| 553 | if ($individual->canShow()) { |
||
| 554 | $content = '<a href="' . e($individual->url()) . '">' . $content . '</a>' . |
||
| 555 | '<div class="namedef name1">' . $individual->getAddName() . '</div>'; |
||
| 556 | $icons = '<div class="icons">' . |
||
| 557 | '<span class="iconz icon-zoomin" title="' . I18N::translate('Zoom in/out on this box.') . '"></span>' . |
||
| 558 | '<div class="itr"><i class="icon-pedigree"></i><div class="popup">' . |
||
| 559 | '<ul class="' . $personBoxClass . '">' . implode('', array_map(function (Menu $menu) { |
||
| 560 | return $menu->bootstrap4(); |
||
| 561 | }, $this->individualBoxMenu($individual))) . '</ul>' . |
||
| 562 | '</div>' . |
||
| 563 | '</div>' . |
||
| 564 | '</div>'; |
||
| 565 | } |
||
| 566 | |||
| 567 | return |
||
| 568 | '<div data-xref="' . e($individual->getXref()) . '" data-tree="' . e($individual->getTree()->getName()) . '" class="person_box_template ' . $personBoxClass . ' box-style1" style="width: ' . $this->parameter('chart-box-x') . 'px; height: ' . $this->parameter('chart-box-y') . 'px">' . |
||
| 569 | $icons . |
||
| 570 | '<div class="chart_textbox" style="max-height:' . $this->parameter('chart-box-y') . 'px;">' . |
||
| 571 | $thumbnail . |
||
| 572 | $content . |
||
| 573 | '<div class="inout2 details1">' . $this->individualBoxFacts($individual) . '</div>' . |
||
| 574 | '</div>' . |
||
| 575 | '<div class="inout"></div>' . |
||
| 576 | '</div>'; |
||
| 577 | } |
||
| 578 | |||
| 579 | /** |
||
| 580 | * Display an empty box - for a missing individual in a chart. |
||
| 581 | * |
||
| 582 | * @return string |
||
| 583 | */ |
||
| 584 | public function individualBoxEmpty() { |
||
| 585 | return '<div class="person_box_template person_boxNN box-style1" style="width: ' . $this->parameter('chart-box-x') . 'px; min-height: ' . $this->parameter('chart-box-y') . 'px"></div>'; |
||
| 586 | } |
||
| 587 | |||
| 588 | /** |
||
| 589 | * Display an individual in a box - for charts, etc. |
||
| 590 | * |
||
| 591 | * @param Individual $individual |
||
| 592 | * |
||
| 593 | * @return string |
||
| 594 | */ |
||
| 595 | public function individualBoxLarge(Individual $individual) { |
||
| 596 | $personBoxClass = array_search($individual->getSex(), ['person_box' => 'M', 'person_boxF' => 'F', 'person_boxNN' => 'U']); |
||
| 597 | if ($individual->getTree()->getPreference('SHOW_HIGHLIGHT_IMAGES')) { |
||
| 598 | $thumbnail = $individual->displayImage(40, 50, 'crop', []); |
||
| 599 | } else { |
||
| 600 | $thumbnail = ''; |
||
| 601 | } |
||
| 602 | |||
| 603 | $content = '<span class="namedef name1">' . $individual->getFullName() . '</span>'; |
||
| 604 | $icons = ''; |
||
| 605 | if ($individual->canShow()) { |
||
| 606 | $content = '<a href="' . e($individual->url()) . '">' . $content . '</a>' . |
||
| 607 | '<div class="namedef name2">' . $individual->getAddName() . '</div>'; |
||
| 608 | $icons = '<div class="icons">' . |
||
| 609 | '<span class="iconz icon-zoomin" title="' . I18N::translate('Zoom in/out on this box.') . '"></span>' . |
||
| 610 | '<div class="itr"><i class="icon-pedigree"></i><div class="popup">' . |
||
| 611 | '<ul class="' . $personBoxClass . '">' . implode('', array_map(function (Menu $menu) { |
||
| 612 | return $menu->bootstrap4(); |
||
| 613 | }, $this->individualBoxMenu($individual))) . '</ul>' . |
||
| 614 | '</div>' . |
||
| 615 | '</div>' . |
||
| 616 | '</div>'; |
||
| 617 | } |
||
| 618 | |||
| 619 | return |
||
| 620 | '<div data-xref="' . e($individual->getXref()) . '" data-tree="' . e($individual->getTree()->getName()) . '" class="person_box_template ' . $personBoxClass . ' box-style2">' . |
||
| 621 | $icons . |
||
| 622 | '<div class="chart_textbox" style="max-height:' . $this->parameter('chart-box-y') . 'px;">' . |
||
| 623 | $thumbnail . |
||
| 624 | $content . |
||
| 625 | '<div class="inout2 details2">' . $this->individualBoxFacts($individual) . '</div>' . |
||
| 626 | '</div>' . |
||
| 627 | '<div class="inout"></div>' . |
||
| 628 | '</div>'; |
||
| 629 | } |
||
| 630 | |||
| 631 | /** |
||
| 632 | * Display an individual in a box - for charts, etc. |
||
| 633 | * |
||
| 634 | * @param Individual $individual |
||
| 635 | * |
||
| 636 | * @return string |
||
| 637 | */ |
||
| 638 | public function individualBoxSmall(Individual $individual) { |
||
| 639 | $personBoxClass = array_search($individual->getSex(), ['person_box' => 'M', 'person_boxF' => 'F', 'person_boxNN' => 'U']); |
||
| 640 | if ($individual->getTree()->getPreference('SHOW_HIGHLIGHT_IMAGES')) { |
||
| 641 | $thumbnail = $individual->displayImage(40, 50, 'crop', []); |
||
| 642 | } else { |
||
| 643 | $thumbnail = ''; |
||
| 644 | } |
||
| 645 | |||
| 646 | return |
||
| 647 | '<div data-xref="' . $individual->getXref() . '" class="person_box_template ' . $personBoxClass . ' iconz box-style0" style="width: ' . $this->parameter('compact-chart-box-x') . 'px; min-height: ' . $this->parameter('compact-chart-box-y') . 'px">' . |
||
| 648 | '<div class="compact_view">' . |
||
| 649 | $thumbnail . |
||
| 650 | '<a href="' . e($individual->url()) . '">' . |
||
| 651 | '<span class="namedef name0">' . $individual->getFullName() . '</span>' . |
||
| 652 | '</a>' . |
||
| 653 | '<div class="inout2 details0">' . $individual->getLifeSpan() . '</div>' . |
||
| 654 | '</div>' . |
||
| 655 | '<div class="inout"></div>' . |
||
| 656 | '</div>'; |
||
| 657 | } |
||
| 658 | |||
| 659 | /** |
||
| 660 | * Display an individual in a box - for charts, etc. |
||
| 661 | * |
||
| 662 | * @return string |
||
| 663 | */ |
||
| 664 | public function individualBoxSmallEmpty() { |
||
| 665 | return '<div class="person_box_template person_boxNN box-style1" style="width: ' . $this->parameter('compact-chart-box-x') . 'px; min-height: ' . $this->parameter('compact-chart-box-y') . 'px"></div>'; |
||
| 666 | } |
||
| 667 | |||
| 668 | /** |
||
| 669 | * Generate the facts, for display in charts. |
||
| 670 | * |
||
| 671 | * @param Individual $individual |
||
| 672 | * |
||
| 673 | * @return string |
||
| 674 | */ |
||
| 675 | public function individualBoxFacts(Individual $individual) { |
||
| 676 | $html = ''; |
||
| 677 | |||
| 678 | $opt_tags = preg_split('/\W/', $individual->getTree()->getPreference('CHART_BOX_TAGS'), 0, PREG_SPLIT_NO_EMPTY); |
||
| 679 | // Show BIRT or equivalent event |
||
| 680 | foreach (explode('|', WT_EVENTS_BIRT) as $birttag) { |
||
| 681 | if (!in_array($birttag, $opt_tags)) { |
||
| 682 | $event = $individual->getFirstFact($birttag); |
||
| 683 | if ($event) { |
||
| 684 | $html .= $event->summary(); |
||
| 685 | break; |
||
| 686 | } |
||
| 687 | } |
||
| 688 | } |
||
| 689 | // Show optional events (before death) |
||
| 690 | foreach ($opt_tags as $key => $tag) { |
||
| 691 | if (!preg_match('/^(' . WT_EVENTS_DEAT . ')$/', $tag)) { |
||
| 692 | $event = $individual->getFirstFact($tag); |
||
| 693 | if (!is_null($event)) { |
||
| 694 | $html .= $event->summary(); |
||
| 695 | unset($opt_tags[$key]); |
||
| 696 | } |
||
| 697 | } |
||
| 698 | } |
||
| 699 | // Show DEAT or equivalent event |
||
| 700 | foreach (explode('|', WT_EVENTS_DEAT) as $deattag) { |
||
| 701 | $event = $individual->getFirstFact($deattag); |
||
| 702 | if ($event) { |
||
| 703 | $html .= $event->summary(); |
||
| 704 | if (in_array($deattag, $opt_tags)) { |
||
| 705 | unset($opt_tags[array_search($deattag, $opt_tags)]); |
||
| 706 | } |
||
| 707 | break; |
||
| 708 | } |
||
| 709 | } |
||
| 710 | // Show remaining optional events (after death) |
||
| 711 | foreach ($opt_tags as $tag) { |
||
| 712 | $event = $individual->getFirstFact($tag); |
||
| 713 | if ($event) { |
||
| 714 | $html .= $event->summary(); |
||
| 715 | } |
||
| 716 | } |
||
| 717 | |||
| 718 | return $html; |
||
| 719 | } |
||
| 720 | |||
| 721 | /** |
||
| 722 | * Generate the LDS summary, for display in charts. |
||
| 723 | * |
||
| 724 | * @param Individual $individual |
||
| 725 | * |
||
| 726 | * @return string |
||
| 727 | */ |
||
| 728 | public function individualBoxLdsSummary(Individual $individual) { |
||
| 729 | if ($individual->getTree()->getPreference('SHOW_LDS_AT_GLANCE')) { |
||
| 730 | $BAPL = $individual->getFacts('BAPL') ? 'B' : '_'; |
||
| 731 | $ENDL = $individual->getFacts('ENDL') ? 'E' : '_'; |
||
| 732 | $SLGC = $individual->getFacts('SLGC') ? 'C' : '_'; |
||
| 733 | $SLGS = '_'; |
||
| 734 | |||
| 735 | foreach ($individual->getSpouseFamilies() as $family) { |
||
| 736 | if ($family->getFacts('SLGS')) { |
||
| 737 | $SLGS = ''; |
||
| 738 | } |
||
| 739 | } |
||
| 740 | |||
| 741 | return $BAPL . $ENDL . $SLGS . $SLGC; |
||
| 742 | } else { |
||
| 743 | return ''; |
||
| 744 | } |
||
| 745 | } |
||
| 746 | |||
| 747 | /** |
||
| 748 | * Links, to show in chart boxes; |
||
| 749 | * |
||
| 750 | * @param Individual $individual |
||
| 751 | * |
||
| 752 | * @return Menu[] |
||
| 753 | */ |
||
| 754 | public function individualBoxMenu(Individual $individual) { |
||
| 755 | $menus = array_merge( |
||
| 756 | $this->individualBoxMenuCharts($individual), |
||
| 757 | $this->individualBoxMenuFamilyLinks($individual) |
||
| 758 | ); |
||
| 759 | |||
| 760 | return $menus; |
||
| 761 | } |
||
| 762 | |||
| 763 | /** |
||
| 764 | * Chart links, to show in chart boxes; |
||
| 765 | * |
||
| 766 | * @param Individual $individual |
||
| 767 | * |
||
| 768 | * @return Menu[] |
||
| 769 | */ |
||
| 770 | public function individualBoxMenuCharts(Individual $individual) { |
||
| 771 | $menus = []; |
||
| 772 | foreach (Module::getActiveCharts($this->tree) as $chart) { |
||
| 773 | $menu = $chart->getBoxChartMenu($individual); |
||
| 774 | if ($menu) { |
||
| 775 | $menus[] = $menu; |
||
| 776 | } |
||
| 777 | } |
||
| 778 | |||
| 779 | usort($menus, function (Menu $x, Menu $y) { |
||
| 780 | return I18N::strcasecmp($x->getLabel(), $y->getLabel()); |
||
| 781 | }); |
||
| 782 | |||
| 783 | return $menus; |
||
| 784 | } |
||
| 785 | |||
| 786 | /** |
||
| 787 | * Family links, to show in chart boxes. |
||
| 788 | * |
||
| 789 | * @param Individual $individual |
||
| 790 | * |
||
| 791 | * @return Menu[] |
||
| 792 | */ |
||
| 793 | public function individualBoxMenuFamilyLinks(Individual $individual) { |
||
| 794 | $menus = []; |
||
| 795 | |||
| 796 | foreach ($individual->getSpouseFamilies() as $family) { |
||
| 797 | $menus[] = new Menu('<strong>' . I18N::translate('Family with spouse') . '</strong>', e($family->url())); |
||
| 798 | $spouse = $family->getSpouse($individual); |
||
| 799 | if ($spouse && $spouse->canShowName()) { |
||
| 800 | $menus[] = new Menu($spouse->getFullName(), e($spouse->url())); |
||
| 801 | } |
||
| 802 | foreach ($family->getChildren() as $child) { |
||
| 803 | if ($child->canShowName()) { |
||
| 804 | $menus[] = new Menu($child->getFullName(), e($child->url())); |
||
| 805 | } |
||
| 806 | } |
||
| 807 | } |
||
| 808 | |||
| 809 | return $menus; |
||
| 810 | } |
||
| 811 | |||
| 812 | /** |
||
| 813 | * Create part of an individual box |
||
| 814 | * |
||
| 815 | * @param Individual $individual |
||
| 816 | * |
||
| 817 | * @return string |
||
| 818 | */ |
||
| 819 | public function individualBoxSexSymbol(Individual $individual) { |
||
| 820 | if ($individual->getTree()->getPreference('PEDIGREE_SHOW_GENDER')) { |
||
| 821 | return $individual->sexImage('large'); |
||
| 822 | } else { |
||
| 823 | return ''; |
||
| 824 | } |
||
| 825 | } |
||
| 826 | |||
| 827 | /** |
||
| 828 | * Initialise the theme. We cannot pass these in a constructor, as the construction |
||
| 829 | * happens in a theme file, and we need to be able to change it. |
||
| 830 | * |
||
| 831 | * @param Tree|null $tree The current tree (if there is one). |
||
| 832 | */ |
||
| 833 | final public function init(Tree $tree = null) { |
||
| 834 | $this->request = Request::createFromGlobals(); |
||
| 835 | $this->tree = $tree; |
||
| 836 | |||
| 837 | $this->hookAfterInit(); |
||
| 838 | } |
||
| 839 | |||
| 840 | /** |
||
| 841 | * A small "powered by webtrees" logo for the footer. |
||
| 842 | * |
||
| 843 | * @return string |
||
| 844 | */ |
||
| 845 | public function logoPoweredBy() { |
||
| 846 | return '<a href="' . WT_WEBTREES_URL . '" class="wt-powered-by-webtrees" title="' . WT_WEBTREES_URL . '" dir="ltr">' . WT_WEBTREES_URL . '</a>'; |
||
| 847 | } |
||
| 848 | |||
| 849 | /** |
||
| 850 | * A menu for the day/month/year calendar views. |
||
| 851 | * |
||
| 852 | * @return Menu |
||
| 853 | */ |
||
| 854 | public function menuCalendar() { |
||
| 855 | return new Menu(I18N::translate('Calendar'), '#', 'menu-calendar', ['rel' => 'nofollow'], [ |
||
| 856 | // Day view |
||
| 857 | new Menu(I18N::translate('Day'), e(route('calendar', ['view' => 'day', 'ged' => $this->tree->getName()])), 'menu-calendar-day', ['rel' => 'nofollow']), |
||
| 858 | // Month view |
||
| 859 | new Menu(I18N::translate('Month'), e(route('calendar', ['view' => 'month', 'ged' => $this->tree->getName()])), 'menu-calendar-month', ['rel' => 'nofollow']), |
||
| 860 | //Year view |
||
| 861 | new Menu(I18N::translate('Year'), e(route('calendar', ['view' => 'year', 'ged' => $this->tree->getName()])), 'menu-calendar-year', ['rel' => 'nofollow']), |
||
| 862 | ]); |
||
| 863 | } |
||
| 864 | |||
| 865 | /** |
||
| 866 | * Generate a menu item to change the blocks on the current (index.php) page. |
||
| 867 | * |
||
| 868 | * @return Menu|null |
||
| 869 | */ |
||
| 870 | public function menuChangeBlocks() { |
||
| 871 | if (WT_SCRIPT_NAME === 'index.php' && Auth::check() && Filter::get('route') === 'user-page') { |
||
| 872 | return new Menu(I18N::translate('Customize this page'), route('user-page-edit', ['ged' => $this->tree->getName()]), 'menu-change-blocks'); |
||
| 873 | } elseif (WT_SCRIPT_NAME === 'index.php' && Auth::isManager($this->tree) && Filter::get('route') === 'tree-page') { |
||
| 874 | return new Menu(I18N::translate('Customize this page'), route('tree-page-edit', ['ged' => $this->tree->getName()]), 'menu-change-blocks'); |
||
| 875 | } else { |
||
| 876 | return null; |
||
| 877 | } |
||
| 878 | } |
||
| 879 | |||
| 880 | /** |
||
| 881 | * Generate a menu for each of the different charts. |
||
| 882 | * |
||
| 883 | * @param Individual $individual |
||
| 884 | * |
||
| 885 | * @return Menu|null |
||
| 886 | */ |
||
| 887 | public function menuChart(Individual $individual) { |
||
| 888 | $submenus = []; |
||
| 889 | foreach (Module::getActiveCharts($this->tree) as $chart) { |
||
| 890 | $menu = $chart->getChartMenu($individual); |
||
| 891 | if ($menu) { |
||
| 892 | $submenus[] = $menu; |
||
| 893 | } |
||
| 894 | } |
||
| 895 | |||
| 896 | if (empty($submenus)) { |
||
| 897 | return null; |
||
| 898 | } else { |
||
| 899 | usort($submenus, function (Menu $x, Menu $y) { |
||
| 900 | return I18N::strcasecmp($x->getLabel(), $y->getLabel()); |
||
| 901 | }); |
||
| 902 | |||
| 903 | return new Menu(I18N::translate('Charts'), '#', 'menu-chart', ['rel' => 'nofollow'], $submenus); |
||
| 904 | } |
||
| 905 | } |
||
| 906 | |||
| 907 | /** |
||
| 908 | * Generate a menu item for the ancestors chart. |
||
| 909 | * |
||
| 910 | * @param Individual $individual |
||
| 911 | * |
||
| 912 | * @return Menu|null |
||
| 913 | * |
||
| 914 | * @deprecated |
||
| 915 | */ |
||
| 916 | public function menuChartAncestors(Individual $individual) { |
||
| 917 | $chart = new AncestorsChartModule(WT_ROOT . WT_MODULES_DIR . 'ancestors_chart'); |
||
| 918 | |||
| 919 | return $chart->getChartMenu($individual); |
||
| 920 | } |
||
| 921 | |||
| 922 | /** |
||
| 923 | * Generate a menu item for the compact tree. |
||
| 924 | * |
||
| 925 | * @param Individual $individual |
||
| 926 | * |
||
| 927 | * @return Menu|null |
||
| 928 | * |
||
| 929 | * @deprecated |
||
| 930 | */ |
||
| 931 | public function menuChartCompact(Individual $individual) { |
||
| 935 | } |
||
| 936 | |||
| 937 | /** |
||
| 938 | * Generate a menu item for the descendants chart. |
||
| 939 | * |
||
| 940 | * @param Individual $individual |
||
| 941 | * |
||
| 942 | * @return Menu|null |
||
| 943 | * |
||
| 944 | * @deprecated |
||
| 945 | */ |
||
| 946 | public function menuChartDescendants(Individual $individual) { |
||
| 947 | $chart = new DescendancyChartModule(WT_ROOT . WT_MODULES_DIR . 'descendancy_chart'); |
||
| 948 | |||
| 949 | return $chart->getChartMenu($individual); |
||
| 950 | } |
||
| 951 | |||
| 952 | /** |
||
| 953 | * Generate a menu item for the family-book chart. |
||
| 954 | * |
||
| 955 | * @param Individual $individual |
||
| 956 | * |
||
| 957 | * @return Menu|null |
||
| 958 | * |
||
| 959 | * @deprecated |
||
| 960 | */ |
||
| 961 | public function menuChartFamilyBook(Individual $individual) { |
||
| 962 | $chart = new FamilyBookChartModule(WT_ROOT . WT_MODULES_DIR . 'family_book_chart'); |
||
| 963 | |||
| 964 | return $chart->getChartMenu($individual); |
||
| 965 | } |
||
| 966 | |||
| 967 | /** |
||
| 968 | * Generate a menu item for the fan chart. |
||
| 969 | * |
||
| 970 | * We can only do this if the GD2 library is installed with TrueType support. |
||
| 971 | * |
||
| 972 | * @param Individual $individual |
||
| 973 | * |
||
| 974 | * @return Menu|null |
||
| 975 | * |
||
| 976 | * @deprecated |
||
| 977 | */ |
||
| 978 | public function menuChartFanChart(Individual $individual) { |
||
| 979 | $chart = new FanChartModule(WT_ROOT . WT_MODULES_DIR . 'fan_chart'); |
||
| 980 | |||
| 981 | return $chart->getChartMenu($individual); |
||
| 982 | } |
||
| 983 | |||
| 984 | /** |
||
| 985 | * Generate a menu item for the interactive tree. |
||
| 986 | * |
||
| 987 | * @param Individual $individual |
||
| 988 | * |
||
| 989 | * @return Menu|null |
||
| 990 | * |
||
| 991 | * @deprecated |
||
| 992 | */ |
||
| 993 | public function menuChartInteractiveTree(Individual $individual) { |
||
| 994 | $chart = new InteractiveTreeModule(WT_ROOT . WT_MODULES_DIR . 'tree'); |
||
| 995 | |||
| 996 | return $chart->getChartMenu($individual); |
||
| 997 | } |
||
| 998 | |||
| 999 | /** |
||
| 1000 | * Generate a menu item for the hourglass chart. |
||
| 1001 | * |
||
| 1002 | * @param Individual $individual |
||
| 1003 | * |
||
| 1004 | * @return Menu|null |
||
| 1005 | * |
||
| 1006 | * @deprecated |
||
| 1007 | */ |
||
| 1008 | public function menuChartHourglass(Individual $individual) { |
||
| 1009 | $chart = new HourglassChartModule(WT_ROOT . WT_MODULES_DIR . 'hourglass_chart'); |
||
| 1010 | |||
| 1011 | return $chart->getChartMenu($individual); |
||
| 1012 | } |
||
| 1013 | |||
| 1014 | /** |
||
| 1015 | * Generate a menu item for the lifepsan chart. |
||
| 1016 | * |
||
| 1017 | * @param Individual $individual |
||
| 1018 | * |
||
| 1019 | * @return Menu|null |
||
| 1020 | * |
||
| 1021 | * @deprecated |
||
| 1022 | */ |
||
| 1023 | public function menuChartLifespan(Individual $individual) { |
||
| 1024 | $chart = new LifespansChartModule(WT_ROOT . WT_MODULES_DIR . 'lifespans_chart'); |
||
| 1025 | |||
| 1026 | return $chart->getChartMenu($individual); |
||
| 1027 | } |
||
| 1028 | |||
| 1029 | /** |
||
| 1030 | * Generate a menu item for the pedigree chart. |
||
| 1031 | * |
||
| 1032 | * @param Individual $individual |
||
| 1033 | * |
||
| 1034 | * @return Menu|null |
||
| 1035 | * |
||
| 1036 | * @deprecated |
||
| 1037 | */ |
||
| 1038 | public function menuChartPedigree(Individual $individual) { |
||
| 1039 | $chart = new PedigreeChartModule(WT_ROOT . WT_MODULES_DIR . 'pedigree_chart'); |
||
| 1040 | |||
| 1041 | return $chart->getChartMenu($individual); |
||
| 1042 | } |
||
| 1043 | |||
| 1044 | /** |
||
| 1045 | * Generate a menu item for the pedigree map. |
||
| 1046 | * |
||
| 1047 | * @param Individual $individual |
||
| 1048 | * |
||
| 1049 | * @return Menu|null |
||
| 1050 | * |
||
| 1051 | * @deprecated |
||
| 1052 | */ |
||
| 1053 | public function menuChartPedigreeMap(Individual $individual) { |
||
| 1054 | $chart = new GoogleMapsModule(WT_ROOT . WT_MODULES_DIR . 'googlemap'); |
||
| 1055 | |||
| 1056 | return $chart->getChartMenu($individual); |
||
| 1057 | } |
||
| 1058 | |||
| 1059 | /** |
||
| 1060 | * Generate a menu item for the relationship chart. |
||
| 1061 | * |
||
| 1062 | * @param Individual $individual |
||
| 1063 | * |
||
| 1064 | * @return Menu|null |
||
| 1065 | * |
||
| 1066 | * @deprecated |
||
| 1067 | */ |
||
| 1068 | public function menuChartRelationship(Individual $individual) { |
||
| 1069 | $chart = new RelationshipsChartModule(WT_ROOT . WT_MODULES_DIR . 'relationships_chart'); |
||
| 1070 | |||
| 1071 | return $chart->getChartMenu($individual); |
||
| 1072 | } |
||
| 1073 | |||
| 1074 | /** |
||
| 1075 | * Generate a menu item for the statistics charts. |
||
| 1076 | * |
||
| 1077 | * @return Menu|null |
||
| 1078 | * |
||
| 1079 | * @deprecated |
||
| 1080 | */ |
||
| 1081 | public function menuChartStatistics() { |
||
| 1082 | $chart = new StatisticsChartModule(WT_ROOT . WT_MODULES_DIR . 'statistics_chart'); |
||
| 1083 | |||
| 1084 | return $chart->getChartMenu(null); |
||
| 1085 | } |
||
| 1086 | |||
| 1087 | /** |
||
| 1088 | * Generate a menu item for the timeline chart. |
||
| 1089 | * |
||
| 1090 | * @param Individual $individual |
||
| 1091 | * |
||
| 1092 | * @return Menu|null |
||
| 1093 | * |
||
| 1094 | * @deprecated |
||
| 1095 | */ |
||
| 1096 | public function menuChartTimeline(Individual $individual) { |
||
| 1097 | $chart = new TimelineChartModule(WT_ROOT . WT_MODULES_DIR . 'timeline_chart'); |
||
| 1098 | |||
| 1099 | return $chart->getChartMenu($individual); |
||
| 1100 | } |
||
| 1101 | |||
| 1102 | /** |
||
| 1103 | * Generate a menu item for the control panel. |
||
| 1104 | * |
||
| 1105 | * @return Menu|null |
||
| 1106 | */ |
||
| 1107 | public function menuControlPanel() { |
||
| 1108 | if (Auth::isAdmin()) { |
||
| 1109 | return new Menu(I18N::translate('Control panel'), route('admin-control-panel'), 'menu-admin'); |
||
| 1110 | } elseif (Auth::isManager($this->tree)) { |
||
| 1111 | return new Menu(I18N::translate('Control panel'), route('admin-control-panel-manager'), 'menu-admin'); |
||
| 1112 | } else { |
||
| 1113 | return null; |
||
| 1114 | } |
||
| 1115 | } |
||
| 1116 | |||
| 1117 | /** |
||
| 1118 | * Favorites menu. |
||
| 1119 | * |
||
| 1120 | * @return Menu|null |
||
| 1121 | */ |
||
| 1122 | public function menuFavorites() { |
||
| 1177 | } |
||
| 1178 | } |
||
| 1179 | |||
| 1180 | /** |
||
| 1181 | * A menu for the home (family tree) pages. |
||
| 1182 | * |
||
| 1183 | * @return Menu |
||
| 1184 | */ |
||
| 1185 | public function menuHomePage() { |
||
| 1186 | if (count(Tree::getAll()) === 1 || Site::getPreference('ALLOW_CHANGE_GEDCOM') !== '1') { |
||
| 1187 | return new Menu(I18N::translate('Family tree'), route('tree-page', ['ged' => $this->tree->getName()]), 'menu-tree'); |
||
| 1188 | } else { |
||
| 1189 | $submenus = []; |
||
| 1190 | foreach (Tree::getAll() as $tree) { |
||
| 1191 | if ($tree == $this->tree) { |
||
| 1192 | $active = 'active '; |
||
| 1193 | } else { |
||
| 1194 | $active = ''; |
||
| 1195 | } |
||
| 1196 | $submenus[] = new Menu(e($tree->getTitle()), route('tree-page', ['ged' => $tree->getName()]), $active . 'menu-tree-' . $tree->getTreeId()); |
||
| 1197 | } |
||
| 1198 | |||
| 1199 | return new Menu(I18N::translate('Family trees'), '#', 'menu-tree', [], $submenus); |
||
| 1200 | } |
||
| 1201 | } |
||
| 1202 | |||
| 1203 | /** |
||
| 1204 | * A menu to show a list of available languages. |
||
| 1205 | * |
||
| 1206 | * @return Menu|null |
||
| 1207 | */ |
||
| 1208 | public function menuLanguages() { |
||
| 1209 | $menu = new Menu(I18N::translate('Language'), '#', 'menu-language'); |
||
| 1210 | |||
| 1211 | foreach (I18N::activeLocales() as $locale) { |
||
| 1212 | $language_tag = $locale->languageTag(); |
||
| 1213 | $class = 'menu-language-' . $language_tag . (WT_LOCALE === $language_tag ? ' active' : ''); |
||
| 1214 | $menu->addSubmenu(new Menu($locale->endonym(), '#', $class, [ |
||
| 1215 | 'onclick' => 'return false;', |
||
| 1216 | 'data-language' => $language_tag, |
||
| 1217 | ])); |
||
| 1218 | } |
||
| 1219 | |||
| 1220 | if (count($menu->getSubmenus()) > 1) { |
||
| 1221 | return $menu; |
||
| 1222 | } else { |
||
| 1223 | return null; |
||
| 1224 | } |
||
| 1225 | } |
||
| 1226 | |||
| 1227 | /** |
||
| 1228 | * Create a menu to show lists of individuals, families, sources, etc. |
||
| 1229 | * |
||
| 1230 | * @param string $surname The significant surname on the page |
||
| 1231 | * |
||
| 1232 | * @return Menu |
||
| 1233 | */ |
||
| 1234 | public function menuLists($surname) { |
||
| 1235 | // Do not show empty lists |
||
| 1236 | $row = Database::prepare( |
||
| 1237 | "SELECT SQL_CACHE" . |
||
| 1238 | " EXISTS(SELECT 1 FROM `##sources` WHERE s_file = ?) AS sour," . |
||
| 1239 | " EXISTS(SELECT 1 FROM `##other` WHERE o_file = ? AND o_type='REPO') AS repo," . |
||
| 1240 | " EXISTS(SELECT 1 FROM `##other` WHERE o_file = ? AND o_type='NOTE') AS note," . |
||
| 1241 | " EXISTS(SELECT 1 FROM `##media` WHERE m_file = ?) AS obje" |
||
| 1242 | )->execute([ |
||
| 1243 | $this->tree->getTreeId(), |
||
| 1244 | $this->tree->getTreeId(), |
||
| 1245 | $this->tree->getTreeId(), |
||
| 1246 | $this->tree->getTreeId(), |
||
| 1247 | ])->fetchOneRow(); |
||
| 1248 | |||
| 1249 | $submenus = [ |
||
| 1250 | $this->menuListsIndividuals($surname), |
||
| 1251 | $this->menuListsFamilies($surname), |
||
| 1252 | $this->menuListsBranches($surname), |
||
| 1253 | $this->menuListsPlaces(), |
||
| 1254 | ]; |
||
| 1255 | if ($row->obje) { |
||
| 1256 | $submenus[] = $this->menuListsMedia(); |
||
| 1257 | } |
||
| 1258 | if ($row->repo) { |
||
| 1259 | $submenus[] = $this->menuListsRepositories(); |
||
| 1260 | } |
||
| 1261 | if ($row->sour) { |
||
| 1262 | $submenus[] = $this->menuListsSources(); |
||
| 1263 | } |
||
| 1264 | if ($row->note) { |
||
| 1265 | $submenus[] = $this->menuListsNotes(); |
||
| 1266 | } |
||
| 1267 | |||
| 1268 | uasort($submenus, function (Menu $x, Menu $y) { |
||
| 1269 | return I18N::strcasecmp($x->getLabel(), $y->getLabel()); |
||
| 1270 | }); |
||
| 1271 | |||
| 1272 | return new Menu(I18N::translate('Lists'), '#', 'menu-list', [], $submenus); |
||
| 1273 | } |
||
| 1274 | |||
| 1275 | /** |
||
| 1276 | * A menu for the list of branches |
||
| 1277 | * |
||
| 1278 | * @param string $surname The significant surname on the page |
||
| 1279 | * |
||
| 1280 | * @return Menu |
||
| 1281 | */ |
||
| 1282 | public function menuListsBranches($surname) { |
||
| 1283 | return new Menu(I18N::translate('Branches'), e(route('branches', ['ged' => $this->tree->getName(), 'surname' => $surname])), 'menu-branches', ['rel' => 'nofollow']); |
||
| 1284 | } |
||
| 1285 | |||
| 1286 | /** |
||
| 1287 | * A menu for the list of families |
||
| 1288 | * |
||
| 1289 | * @param string $surname The significant surname on the page |
||
| 1290 | * |
||
| 1291 | * @return Menu |
||
| 1292 | */ |
||
| 1293 | public function menuListsFamilies($surname) { |
||
| 1294 | return new Menu(I18N::translate('Families'), e(route('family-list', ['ged' => $this->tree->getName(), 'surname' => $surname])), 'menu-list-indi'); |
||
| 1295 | } |
||
| 1296 | |||
| 1297 | /** |
||
| 1298 | * A menu for the list of individuals |
||
| 1299 | * |
||
| 1300 | * @param string $surname The significant surname on the page |
||
| 1301 | * |
||
| 1302 | * @return Menu |
||
| 1303 | */ |
||
| 1304 | public function menuListsIndividuals($surname) { |
||
| 1306 | } |
||
| 1307 | |||
| 1308 | /** |
||
| 1309 | * A menu for the list of media objects |
||
| 1310 | * |
||
| 1311 | * @return Menu |
||
| 1312 | */ |
||
| 1313 | public function menuListsMedia() { |
||
| 1314 | return new Menu(I18N::translate('Media objects'), e(route('media-list', ['ged' => $this->tree->getName()])), 'menu-list-obje', ['rel' => 'nofollow']); |
||
| 1315 | } |
||
| 1316 | |||
| 1317 | /** |
||
| 1318 | * A menu for the list of notes |
||
| 1319 | * |
||
| 1320 | * @return Menu |
||
| 1321 | */ |
||
| 1322 | public function menuListsNotes() { |
||
| 1323 | return new Menu(I18N::translate('Shared notes'), e(route('note-list', ['ged' => $this->tree->getName()])), 'menu-list-note', ['rel' => 'nofollow']); |
||
| 1324 | } |
||
| 1325 | |||
| 1326 | /** |
||
| 1327 | * A menu for the list of individuals |
||
| 1328 | * |
||
| 1329 | * @return Menu |
||
| 1330 | */ |
||
| 1331 | protected function menuListsPlaces() { |
||
| 1332 | return new Menu(I18N::translate('Place hierarchy'), e(route('place-hierarchy', ['ged' => $this->tree->getName()])), 'menu-list-plac', ['rel' => 'nofollow']); |
||
| 1333 | } |
||
| 1334 | |||
| 1335 | /** |
||
| 1336 | * A menu for the list of repositories |
||
| 1337 | * |
||
| 1338 | * @return Menu |
||
| 1339 | */ |
||
| 1340 | public function menuListsRepositories() { |
||
| 1341 | return new Menu(I18N::translate('Repositories'), e(route('repository-list', ['ged' => $this->tree->getName()])), 'menu-list-repo', ['rel' => 'nofollow']); |
||
| 1342 | } |
||
| 1343 | |||
| 1344 | /** |
||
| 1345 | * A menu for the list of sources |
||
| 1346 | * |
||
| 1347 | * @return Menu |
||
| 1348 | */ |
||
| 1349 | public function menuListsSources() { |
||
| 1350 | return new Menu( |
||
| 1351 | I18N::translate('Sources'), e(route('source-list', ['ged' => $this->tree->getName()])), 'menu-list-sour', ['rel' => 'nofollow']); |
||
| 1352 | } |
||
| 1353 | |||
| 1354 | /** |
||
| 1355 | * A login menu option (or null if we are already logged in). |
||
| 1356 | * |
||
| 1357 | * @return Menu|null |
||
| 1358 | */ |
||
| 1359 | public function menuLogin() { |
||
| 1360 | if (Auth::check()) { |
||
| 1361 | return null; |
||
| 1362 | } else { |
||
| 1363 | // Return to this page after login... |
||
| 1364 | $url = Functions::getQueryUrl(); |
||
| 1365 | // ...but switch from the tree-page to the user-page |
||
| 1366 | $url = str_replace('route=tree-page', 'route=user-page', $url); |
||
| 1367 | |||
| 1368 | return new Menu(I18N::translate('Sign in'), e(route('login', ['url' => $url])), 'menu-login', ['rel' => 'nofollow']); |
||
| 1369 | } |
||
| 1370 | } |
||
| 1371 | |||
| 1372 | /** |
||
| 1373 | * A logout menu option (or null if we are already logged out). |
||
| 1374 | * |
||
| 1375 | * @return Menu|null |
||
| 1376 | */ |
||
| 1377 | public function menuLogout() { |
||
| 1378 | if (Auth::check()) { |
||
| 1379 | return new Menu(I18N::translate('Sign out'), e(route('logout')), 'menu-logout'); |
||
| 1380 | } else { |
||
| 1381 | return null; |
||
| 1382 | } |
||
| 1383 | } |
||
| 1384 | |||
| 1385 | /** |
||
| 1386 | * Get the additional menus created by each of the modules |
||
| 1387 | * |
||
| 1388 | * @return Menu[] |
||
| 1389 | */ |
||
| 1390 | public function menuModules() { |
||
| 1391 | $menus = []; |
||
| 1392 | foreach (Module::getActiveMenus($this->tree) as $module) { |
||
| 1393 | $menus[] = $module->getMenu(); |
||
| 1394 | } |
||
| 1395 | |||
| 1396 | return array_filter($menus); |
||
| 1397 | } |
||
| 1398 | |||
| 1399 | /** |
||
| 1400 | * A link to allow users to edit their account settings. |
||
| 1401 | * |
||
| 1402 | * @return Menu|null |
||
| 1403 | */ |
||
| 1404 | public function menuMyAccount() { |
||
| 1405 | if (Auth::check()) { |
||
| 1406 | return new Menu(I18N::translate('My account'), e(route('my-account', []))); |
||
| 1407 | } else { |
||
| 1408 | return null; |
||
| 1409 | } |
||
| 1410 | } |
||
| 1411 | |||
| 1412 | /** |
||
| 1413 | * A link to the user's individual record (individual.php). |
||
| 1414 | * |
||
| 1415 | * @return Menu|null |
||
| 1416 | */ |
||
| 1417 | public function menuMyIndividualRecord() { |
||
| 1418 | $record = Individual::getInstance($this->tree->getUserPreference(Auth::user(), 'gedcomid'), $this->tree); |
||
| 1419 | |||
| 1420 | if ($record) { |
||
| 1421 | return new Menu(I18N::translate('My individual record'), e($record->url()), 'menu-myrecord'); |
||
| 1422 | } else { |
||
| 1423 | return null; |
||
| 1424 | } |
||
| 1425 | } |
||
| 1426 | |||
| 1427 | /** |
||
| 1428 | * A link to the user's personal home page. |
||
| 1429 | * |
||
| 1430 | * @return Menu |
||
| 1431 | */ |
||
| 1432 | public function menuMyPage() { |
||
| 1433 | return new Menu(I18N::translate('My page'), route('user-page'), 'menu-mypage'); |
||
| 1434 | } |
||
| 1435 | |||
| 1436 | /** |
||
| 1437 | * A menu for the user's personal pages. |
||
| 1438 | * |
||
| 1439 | * @return Menu|null |
||
| 1440 | */ |
||
| 1441 | public function menuMyPages() { |
||
| 1453 | } |
||
| 1454 | } |
||
| 1455 | |||
| 1456 | /** |
||
| 1457 | * A link to the user's individual record. |
||
| 1458 | * |
||
| 1459 | * @return Menu|null |
||
| 1460 | */ |
||
| 1461 | public function menuMyPedigree() { |
||
| 1462 | $gedcomid = $this->tree->getUserPreference(Auth::user(), 'gedcomid'); |
||
| 1463 | |||
| 1464 | if ($gedcomid && Module::isActiveChart($this->tree, 'pedigree_chart')) { |
||
| 1465 | return new Menu( |
||
| 1466 | I18N::translate('My pedigree'), |
||
| 1467 | e(route('pedigree', ['xref' => $gedcomid, 'ged' => $this->tree->getName()])), |
||
| 1468 | 'menu-mypedigree' |
||
| 1469 | ); |
||
| 1470 | } else { |
||
| 1471 | return null; |
||
| 1472 | } |
||
| 1473 | } |
||
| 1474 | |||
| 1475 | /** |
||
| 1476 | * Create a pending changes menu. |
||
| 1477 | * |
||
| 1478 | * @return Menu|null |
||
| 1479 | */ |
||
| 1480 | public function menuPendingChanges() { |
||
| 1481 | if ($this->pendingChangesExist()) { |
||
| 1482 | $url = route('show-pending', [ |
||
| 1483 | 'ged' => $this->tree ? $this->tree->getName() : '', |
||
| 1484 | 'url' => $this->request->getRequestUri() |
||
| 1485 | ]); |
||
| 1486 | |||
| 1487 | return new Menu(I18N::translate('Pending changes'), e($url), 'menu-pending'); |
||
| 1488 | } else { |
||
| 1489 | return null; |
||
| 1490 | } |
||
| 1491 | } |
||
| 1492 | |||
| 1493 | /** |
||
| 1494 | * A menu with a list of reports. |
||
| 1495 | * |
||
| 1496 | * @return Menu|null |
||
| 1497 | */ |
||
| 1498 | public function menuReports() { |
||
| 1499 | $submenus = []; |
||
| 1500 | foreach (Module::getActiveReports($this->tree) as $report) { |
||
| 1501 | $submenus[] = $report->getReportMenu($this->tree); |
||
| 1502 | } |
||
| 1503 | |||
| 1504 | if (empty($submenus)) { |
||
| 1505 | return null; |
||
| 1506 | } else { |
||
| 1507 | return new Menu(I18N::translate('Reports'), '#', 'menu-report', ['rel' => 'nofollow'], $submenus); |
||
| 1508 | } |
||
| 1509 | } |
||
| 1510 | |||
| 1511 | /** |
||
| 1512 | * Create the search menu. |
||
| 1513 | * |
||
| 1514 | * @return Menu |
||
| 1515 | */ |
||
| 1516 | public function menuSearch() { |
||
| 1517 | return new Menu(I18N::translate('Search'), '#', 'menu-search', ['rel' => 'nofollow'], array_filter([ |
||
| 1518 | $this->menuSearchGeneral(), |
||
| 1519 | $this->menuSearchPhonetic(), |
||
| 1520 | $this->menuSearchAdvanced(), |
||
| 1521 | $this->menuSearchAndReplace(), |
||
| 1522 | ])); |
||
| 1523 | } |
||
| 1524 | |||
| 1525 | /** |
||
| 1526 | * Create the general search sub-menu. |
||
| 1527 | * |
||
| 1528 | * @return Menu |
||
| 1529 | */ |
||
| 1530 | public function menuSearchGeneral() { |
||
| 1531 | return new Menu(I18N::translate('General search'), e(route('search-general', ['ged' => $this->tree->getName()])), 'menu-search-general', ['rel' => 'nofollow']); |
||
| 1532 | } |
||
| 1533 | |||
| 1534 | /** |
||
| 1535 | * Create the phonetic search sub-menu. |
||
| 1536 | * |
||
| 1537 | * @return Menu |
||
| 1538 | */ |
||
| 1539 | public function menuSearchPhonetic() { |
||
| 1540 | return new Menu(/* I18N: search using “sounds like”, rather than exact spelling */ I18N::translate('Phonetic search'), e(route('search-phonetic', ['ged' => $this->tree->getName(), 'action' => 'soundex'])), 'menu-search-soundex', ['rel' => 'nofollow']); |
||
| 1541 | } |
||
| 1542 | |||
| 1543 | /** |
||
| 1544 | * Create the advanced search sub-menu. |
||
| 1545 | * |
||
| 1546 | * @return Menu |
||
| 1547 | */ |
||
| 1548 | public function menuSearchAdvanced() { |
||
| 1549 | return new Menu(I18N::translate('Advanced search'), e(route('search-advanced', ['ged' => $this->tree->getName()])), 'menu-search-advanced', ['rel' => 'nofollow']); |
||
| 1550 | } |
||
| 1551 | |||
| 1552 | /** |
||
| 1553 | * Create the advanced search sub-menu. |
||
| 1554 | * |
||
| 1555 | * @return Menu |
||
| 1556 | */ |
||
| 1557 | public function menuSearchAndReplace() { |
||
| 1558 | if (Auth::isEditor($this->tree)) { |
||
| 1559 | return new Menu(I18N::translate('Search and replace'), e(route('search-replace', ['ged' => $this->tree->getName(), 'action' => 'replace'])), 'menu-search-replace'); |
||
| 1560 | } else { |
||
| 1561 | return null; |
||
| 1562 | } |
||
| 1563 | } |
||
| 1564 | |||
| 1565 | /** |
||
| 1566 | * Themes menu. |
||
| 1567 | * |
||
| 1568 | * @return Menu|null |
||
| 1569 | */ |
||
| 1570 | public function menuThemes() { |
||
| 1571 | if ($this->tree !== null && Site::getPreference('ALLOW_USER_THEMES') === '1' && $this->tree->getPreference('ALLOW_THEME_DROPDOWN') === '1') { |
||
| 1572 | $submenus = []; |
||
| 1573 | foreach (Theme::installedThemes() as $theme) { |
||
| 1574 | $class = 'menu-theme-' . $theme->themeId() . ($theme === $this ? ' active' : ''); |
||
| 1575 | $submenus[] = new Menu($theme->themeName(), '#', $class, [ |
||
| 1576 | 'onclick' => 'return false;', |
||
| 1577 | 'data-theme' => $theme->themeId(), |
||
| 1578 | ]); |
||
| 1579 | } |
||
| 1580 | |||
| 1581 | usort($submenus, function (Menu $x, Menu $y) { |
||
| 1582 | return I18N::strcasecmp($x->getLabel(), $y->getLabel()); |
||
| 1583 | }); |
||
| 1584 | |||
| 1585 | $menu = new Menu(I18N::translate('Theme'), '#', 'menu-theme', [], $submenus); |
||
| 1586 | |||
| 1587 | return $menu; |
||
| 1588 | } else { |
||
| 1589 | return null; |
||
| 1590 | } |
||
| 1591 | } |
||
| 1592 | |||
| 1593 | /** |
||
| 1594 | * How many times has the current page been shown? |
||
| 1595 | * |
||
| 1596 | * @param PageController $controller |
||
| 1597 | * |
||
| 1598 | * @return int Number of views, or zero for pages that aren't logged. |
||
| 1599 | */ |
||
| 1600 | public function pageViews(PageController $controller) { |
||
| 1601 | if ($this->tree && $this->tree->getPreference('SHOW_COUNTER')) { |
||
| 1602 | if (isset($controller->record) && $controller->record instanceof GedcomRecord) { |
||
| 1603 | return HitCounter::countHit($this->tree, WT_SCRIPT_NAME, $controller->record->getXref()); |
||
| 1604 | } elseif (isset($controller->root) && $controller->root instanceof GedcomRecord) { |
||
| 1605 | return HitCounter::countHit($this->tree, WT_SCRIPT_NAME, $controller->root->getXref()); |
||
| 1606 | } elseif (WT_SCRIPT_NAME === 'index.php') { |
||
| 1607 | if (Auth::check() && Filter::get('ctype') !== 'gedcom') { |
||
| 1608 | return HitCounter::countHit($this->tree, WT_SCRIPT_NAME, 'user:' . Auth::id()); |
||
| 1609 | } else { |
||
| 1610 | return HitCounter::countHit($this->tree, WT_SCRIPT_NAME, 'gedcom:' . $this->tree->getTreeId()); |
||
| 1611 | } |
||
| 1612 | } |
||
| 1613 | } |
||
| 1614 | |||
| 1615 | return 0; |
||
| 1616 | } |
||
| 1617 | |||
| 1618 | /** |
||
| 1619 | * Misecellaneous dimensions, fonts, styles, etc. |
||
| 1620 | * |
||
| 1621 | * @param string $parameter_name |
||
| 1622 | * |
||
| 1623 | * @return string|int|float |
||
| 1624 | */ |
||
| 1625 | public function parameter($parameter_name) { |
||
| 1626 | $parameters = [ |
||
| 1627 | 'chart-background-f' => 'dddddd', |
||
| 1628 | 'chart-background-m' => 'cccccc', |
||
| 1629 | 'chart-background-u' => 'eeeeee', |
||
| 1630 | 'chart-box-x' => 250, |
||
| 1631 | 'chart-box-y' => 80, |
||
| 1632 | 'chart-font-color' => '000000', |
||
| 1633 | 'chart-spacing-x' => 5, |
||
| 1634 | 'chart-spacing-y' => 10, |
||
| 1635 | 'compact-chart-box-x' => 240, |
||
| 1636 | 'compact-chart-box-y' => 50, |
||
| 1637 | 'distribution-chart-high-values' => '555555', |
||
| 1638 | 'distribution-chart-low-values' => 'cccccc', |
||
| 1639 | 'distribution-chart-no-values' => 'ffffff', |
||
| 1640 | 'distribution-chart-x' => 440, |
||
| 1641 | 'distribution-chart-y' => 220, |
||
| 1642 | 'line-width' => 1.5, |
||
| 1643 | 'shadow-blur' => 0, |
||
| 1644 | 'shadow-color' => '', |
||
| 1645 | 'shadow-offset-x' => 0, |
||
| 1646 | 'shadow-offset-y' => 0, |
||
| 1647 | 'stats-small-chart-x' => 440, |
||
| 1648 | 'stats-small-chart-y' => 125, |
||
| 1649 | 'stats-large-chart-x' => 900, |
||
| 1650 | 'image-dline' => static::ASSET_DIR . 'images/dline.png', |
||
| 1651 | 'image-dline2' => static::ASSET_DIR . 'images/dline2.png', |
||
| 1652 | 'image-hline' => static::ASSET_DIR . 'images/hline.png', |
||
| 1653 | 'image-spacer' => static::ASSET_DIR . 'images/spacer.png', |
||
| 1654 | 'image-vline' => static::ASSET_DIR . 'images/vline.png', |
||
| 1655 | 'image-minus' => static::ASSET_DIR . 'images/minus.png', |
||
| 1656 | 'image-plus' => static::ASSET_DIR . 'images/plus.png', |
||
| 1657 | ]; |
||
| 1658 | |||
| 1659 | if (array_key_exists($parameter_name, $parameters)) { |
||
| 1660 | return $parameters[$parameter_name]; |
||
| 1661 | } else { |
||
| 1662 | throw new \InvalidArgumentException($parameter_name); |
||
| 1663 | } |
||
| 1664 | } |
||
| 1665 | |||
| 1666 | /** |
||
| 1667 | * Are there any pending changes for us to approve? |
||
| 1668 | * |
||
| 1669 | * @return bool |
||
| 1670 | */ |
||
| 1671 | public function pendingChangesExist() { |
||
| 1672 | return $this->tree && $this->tree->hasPendingEdit() && Auth::isModerator($this->tree); |
||
| 1673 | } |
||
| 1674 | |||
| 1675 | /** |
||
| 1676 | * Create a pending changes link. Some themes prefer an alert/banner to a menu. |
||
| 1677 | * |
||
| 1678 | * @return string |
||
| 1679 | */ |
||
| 1680 | public function pendingChangesLink() { |
||
| 1682 | } |
||
| 1683 | |||
| 1684 | /** |
||
| 1685 | * Text to use in the pending changes link. |
||
| 1686 | * |
||
| 1687 | * @return string |
||
| 1688 | */ |
||
| 1689 | public function pendingChangesLinkText() { |
||
| 1690 | return I18N::translate('There are pending changes for you to moderate.'); |
||
| 1691 | } |
||
| 1692 | |||
| 1693 | /** |
||
| 1694 | * Generate a list of items for the main menu. |
||
| 1695 | * |
||
| 1696 | * @return Menu[] |
||
| 1697 | */ |
||
| 1698 | public function primaryMenu() { |
||
| 1699 | global $controller; |
||
| 1700 | |||
| 1701 | if ($this->tree) { |
||
| 1702 | $individual = $controller->getSignificantIndividual(); |
||
| 1703 | |||
| 1704 | return array_filter(array_merge([ |
||
| 1705 | $this->menuHomePage(), |
||
| 1706 | $this->menuChart($individual), |
||
| 1707 | $this->menuLists($controller->getSignificantSurname()), |
||
| 1708 | $this->menuCalendar(), |
||
| 1709 | $this->menuReports(), |
||
| 1710 | $this->menuSearch(), |
||
| 1711 | ], $this->menuModules())); |
||
| 1712 | } else { |
||
| 1713 | // No public trees? No genealogy menu! |
||
| 1714 | return []; |
||
| 1715 | } |
||
| 1716 | } |
||
| 1717 | |||
| 1718 | /** |
||
| 1719 | * Create the primary menu. |
||
| 1720 | * |
||
| 1721 | * @param Menu[] $menus |
||
| 1722 | * |
||
| 1723 | * @return string |
||
| 1724 | */ |
||
| 1725 | public function primaryMenuContent(array $menus) { |
||
| 1726 | return implode('', array_map(function (Menu $menu) { |
||
| 1727 | return $menu->bootstrap4(); |
||
| 1728 | }, $menus)); |
||
| 1729 | } |
||
| 1730 | |||
| 1731 | /** |
||
| 1732 | * Generate a list of items for the user menu. |
||
| 1733 | * |
||
| 1734 | * @return Menu[] |
||
| 1735 | */ |
||
| 1736 | public function secondaryMenu() { |
||
| 1737 | return array_filter([ |
||
| 1738 | $this->menuPendingChanges(), |
||
| 1739 | $this->menuMyPages(), |
||
| 1740 | $this->menuFavorites(), |
||
| 1741 | $this->menuThemes(), |
||
| 1742 | $this->menuLanguages(), |
||
| 1743 | $this->menuLogin(), |
||
| 1744 | $this->menuLogout(), |
||
| 1745 | ]); |
||
| 1746 | } |
||
| 1747 | |||
| 1748 | /** |
||
| 1749 | * Format the secondary menu. |
||
| 1750 | * |
||
| 1751 | * @param Menu[] $menus |
||
| 1752 | * |
||
| 1753 | * @return string |
||
| 1754 | */ |
||
| 1755 | public function secondaryMenuContent(array $menus) { |
||
| 1759 | } |
||
| 1760 | |||
| 1761 | /** |
||
| 1762 | * A list of CSS files to include for this page. |
||
| 1763 | * |
||
| 1764 | * @return string[] |
||
| 1765 | */ |
||
| 1766 | public function stylesheets() { |
||
| 1767 | |||
| 1768 | if (I18N::direction() === 'rtl') { |
||
| 1769 | $stylesheets = [ |
||
| 1770 | WT_ASSETS_URL . 'css/vendor-rtl.css', |
||
| 1771 | self::STYLESHEET, |
||
| 1772 | ]; |
||
| 1773 | } else { |
||
| 1774 | $stylesheets = [ |
||
| 1775 | WT_ASSETS_URL . 'css/vendor.css', |
||
| 1776 | self::STYLESHEET, |
||
| 1777 | ]; |
||
| 1778 | } |
||
| 1779 | |||
| 1780 | return $stylesheets; |
||
| 1781 | } |
||
| 1782 | |||
| 1783 | /** |
||
| 1784 | * A fixed string to identify this theme, in settings, etc. |
||
| 1785 | * |
||
| 1786 | * @return string |
||
| 1787 | */ |
||
| 1788 | public function themeId() { |
||
| 1789 | return static::THEME_DIR; |
||
| 1790 | } |
||
| 1791 | |||
| 1792 | /** |
||
| 1793 | * What is this theme called? |
||
| 1794 | * |
||
| 1795 | * @return string |
||
| 1796 | */ |
||
| 1797 | abstract public function themeName(); |
||
| 1798 | |||
| 1799 | } |
||
| 1800 |
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths