1<?php 2/** 3 * Copyright (c) 2021. ComboStrap, Inc. and its affiliates. All Rights Reserved. 4 * 5 * This source code is licensed under the GPL license found in the 6 * COPYING file in the root directory of this source tree. 7 * 8 * @license GPL 3 (https://www.gnu.org/licenses/gpl-3.0.en.html) 9 * @author ComboStrap <support@combostrap.com> 10 * 11 */ 12 13namespace ComboStrap; 14 15 16use Exception; 17 18class Site 19{ 20 21 const STRAP_TEMPLATE_NAME = "strap"; 22 23 const SVG_LOGO_IDS = array( 24 ':wiki:logo.svg', 25 ':logo.svg' 26 ); 27 28 const PNG_LOGO_IDS = array( 29 ':logo.png', 30 ':wiki:logo.png', 31 ':favicon-32×32.png', 32 ':favicon-16×16.png', 33 ':apple-touch-icon.png', 34 ':android-chrome-192x192.png' 35 ); 36 37 38 /** 39 * @return Image[] 40 */ 41 public static function getLogoImages(): array 42 { 43 $logosPaths = PluginUtility::mergeAttributes(self::PNG_LOGO_IDS, self::SVG_LOGO_IDS); 44 $logos = []; 45 foreach ($logosPaths as $logoPath) { 46 $dokuPath = DokuPath::createMediaPathFromId($logoPath); 47 if (FileSystems::exists($dokuPath)) { 48 try { 49 $logos[] = Image::createImageFromPath($dokuPath); 50 } catch (Exception $e) { 51 // The image is not valid 52 LogUtility::msg("The logo ($logoPath) is not a valid image. {$e->getMessage()}"); 53 } 54 } 55 } 56 return $logos; 57 } 58 59 60 /** 61 * @return string|null 62 */ 63 public static function getLogoUrlAsSvg(): ?string 64 { 65 66 67 $url = null; 68 foreach (self::SVG_LOGO_IDS as $svgLogo) { 69 70 $svgLogoFN = mediaFN($svgLogo); 71 if (file_exists($svgLogoFN)) { 72 $url = ml($svgLogo, '', true, '', true); 73 break; 74 } 75 } 76 return $url; 77 } 78 79 public static function getLogoAsSvgImage(): ?ImageSvg 80 { 81 foreach (self::SVG_LOGO_IDS as $svgLogo) { 82 83 try { 84 $image = ImageSvg::createImageFromId($svgLogo); 85 } catch (ExceptionCombo $e) { 86 LogUtility::msg("The svg ($svgLogo) returns an error. {$e->getMessage()}"); 87 continue; 88 } 89 if ($image->exists()) { 90 return $image; 91 } 92 } 93 return null; 94 } 95 96 public static function getLogoUrlAsPng() 97 { 98 99 $url = null; 100 foreach (self::PNG_LOGO_IDS as $svgLogo) { 101 102 $svgLogoFN = mediaFN($svgLogo); 103 104 if (file_exists($svgLogoFN)) { 105 $url = ml($svgLogo, '', true, '', true); 106 break; 107 }; 108 } 109 return $url; 110 } 111 112 /** 113 * @return mixed 114 * @deprecated use {@link Site::getName()} instead 115 * https://www.dokuwiki.org/config:title 116 */ 117 public static function getTitle() 118 { 119 global $conf; 120 return $conf['title']; 121 } 122 123 /** 124 * https://www.dokuwiki.org/config:title 125 */ 126 public static function setName($name) 127 { 128 global $conf; 129 $conf['title'] = $name; 130 } 131 132 /** 133 * @param string $sep - the separator - generally ("-") but not always 134 * @return string 135 * 136 * Locale always canonicalizes to upper case. 137 */ 138 public static function getLocale(string $sep = "-"): ?string 139 { 140 141 $locale = null; 142 143 $lang = self::getLang(); 144 if ($lang != null) { 145 $country = self::getLanguageRegion(); 146 if ($country != null) { 147 $locale = strtolower($lang) . $sep . strtoupper($country); 148 } 149 } 150 151 return $locale; 152 } 153 154 /** 155 * 156 * ISO 3166 alpha-2 country code 157 * 158 */ 159 public static function getLanguageRegion() 160 { 161 $region = PluginUtility::getConfValue(Region::CONF_SITE_LANGUAGE_REGION); 162 if (!empty($region)) { 163 return $region; 164 } else { 165 166 if (extension_loaded("intl")) { 167 $locale = locale_get_default(); 168 $localeParts = preg_split("/_/", $locale, 2); 169 if (sizeof($localeParts) === 2) { 170 return $localeParts[1]; 171 } 172 } 173 174 return null; 175 } 176 177 } 178 179 /** 180 * @return mixed|null 181 * Wrapper around https://www.dokuwiki.org/config:lang 182 */ 183 public static function getLang() 184 { 185 186 global $conf; 187 $lang = $conf['lang']; 188 return ($lang ?: null); 189 } 190 191 public static function getBaseUrl(): string 192 { 193 194 /** 195 * In a {@link PluginUtility::isDevOrTest()} dev environment, 196 * don't set the 197 * https://www.dokuwiki.org/config:baseurl 198 * to be able to test the metadata / social integration 199 * via a tunnel 200 * 201 * Same as {@link getBaseURL()} ?? 202 * Same as {@link wl()} without nothing 203 */ 204 205 return DOKU_URL; 206 207 } 208 209 public static function getTag() 210 { 211 global $conf; 212 $tag = $conf['tag']; 213 return ($tag ? $tag : null); 214 } 215 216 /** 217 * @return string - the name of the sidebar page 218 */ 219 public static function getSidebarName() 220 { 221 global $conf; 222 return $conf["sidebar"]; 223 } 224 225 public static function setTemplate($template) 226 { 227 global $conf; 228 $conf['template'] = $template; 229 } 230 231 public static function setCacheXhtmlOn() 232 { 233 // ensure the value is not -1, which disables caching 234 // https://www.dokuwiki.org/config:cachetime 235 global $conf; 236 $conf['cachetime'] = 60 * 60; 237 } 238 239 public static function debugIsOn() 240 { 241 global $conf; 242 return $conf['allowdebug']; 243 } 244 245 public static function setTemplateToStrap() 246 { 247 global $conf; 248 $conf['template'] = self::STRAP_TEMPLATE_NAME; 249 } 250 251 public static function setTemplateToDefault() 252 { 253 global $conf; 254 $conf['template'] = 'dokuwiki'; 255 } 256 257 public static function setCacheDefault() 258 { 259 // The value is -1, which disables caching 260 // https://www.dokuwiki.org/config:cachetime 261 global $conf; 262 $conf['cachetime'] = -1; 263 } 264 265 public static function useHeadingAsTitle() 266 { 267 // https://www.dokuwiki.org/config:useheading 268 global $conf; 269 $conf['useheading'] = 1; 270 } 271 272 public static function useHeadingDefault() 273 { 274 // https://www.dokuwiki.org/config:useheading 275 global $conf; 276 $conf['useheading'] = 0; 277 } 278 279 public static function getTemplate() 280 { 281 global $conf; 282 return $conf['template']; 283 284 } 285 286 public static function isStrapTemplate() 287 { 288 global $conf; 289 return $conf['template'] == self::STRAP_TEMPLATE_NAME; 290 } 291 292 public static function getAjaxUrl(): string 293 { 294 return self::getBaseUrl() . "lib/exe/ajax.php"; 295 } 296 297 public static function getPageDirectory() 298 { 299 global $conf; 300 /** 301 * Data dir is the pages dir (savedir is the data dir) 302 */ 303 $pageDirectory = $conf['datadir']; 304 if ($pageDirectory === null) { 305 throw new ExceptionComboRuntime("The page directory ($pageDirectory) is null"); 306 } 307 return LocalPath::createFromPath($pageDirectory); 308 } 309 310 public static function disableHeadingSectionEditing() 311 { 312 global $conf; 313 $conf['maxseclevel'] = 0; 314 } 315 316 public static function setBreadCrumbOn() 317 { 318 global $conf; 319 $conf['youarehere'] = 1; 320 } 321 322 public static function isHtmlRenderCacheOn(): bool 323 { 324 global $conf; 325 return $conf['cachetime'] !== -1; 326 } 327 328 public static function getDataDirectory(): LocalPath 329 { 330 global $conf; 331 $dataDirectory = $conf['savedir']; 332 if ($dataDirectory === null) { 333 throw new ExceptionComboRuntime("The data directory ($dataDirectory) is null"); 334 } 335 return LocalPath::createFromPath($dataDirectory); 336 } 337 338 public static function isLowQualityProtectionEnable(): bool 339 { 340 return PluginUtility::getConfValue(LowQualityPage::CONF_LOW_QUALITY_PAGE_PROTECTION_ENABLE) === 1; 341 } 342 343 public static function getHomePageName() 344 { 345 global $conf; 346 return $conf["start"]; 347 } 348 349 /** 350 * @return mixed - Application / Website name 351 */ 352 public static function getName() 353 { 354 return self::getTitle(); 355 } 356 357 public static function getTagLine() 358 { 359 global $conf; 360 return $conf['tagline']; 361 } 362 363 /** 364 * @return int|null 365 */ 366 public static function getCacheTime(): ?int 367 { 368 global $conf; 369 $cacheTime = $conf['cachetime']; 370 if ($cacheTime === null) { 371 return null; 372 } 373 if (is_numeric($cacheTime)) { 374 return intval($cacheTime); 375 } 376 return null; 377 } 378 379 /** 380 * Absolute vs Relative URL 381 * https://www.dokuwiki.org/config:canonical 382 */ 383 public static function shouldUrlBeAbsolute(): bool 384 { 385 global $conf; 386 $value = $conf['canonical']; 387 if ($value === 1) { 388 return true; 389 } 390 return false; 391 } 392 393 /** 394 * @param string $description 395 * Same as {@link Site::setDescription()} 396 */ 397 public static function setTagLine(string $description) 398 { 399 global $conf; 400 $conf['tagline'] = $description; 401 } 402 403 /** 404 * @param string $description 405 * 406 */ 407 public static function setDescription(string $description) 408 { 409 self::setTagLine($description); 410 } 411 412 public static function setPrimaryColor(string $primaryColorValue) 413 { 414 PluginUtility::setConf(ColorRgb::PRIMARY_COLOR_CONF, $primaryColorValue); 415 } 416 417 public static function getPrimaryColor($default = null): ?ColorRgb 418 { 419 $value = self::getPrimaryColorValue($default); 420 if ( 421 $value === null || 422 (trim($value) === "")) { 423 return null; 424 } 425 try { 426 return ColorRgb::createFromString($value); 427 } catch 428 (ExceptionCombo $e) { 429 LogUtility::msg("The primary color value configuration ($value) is not valid. Error: {$e->getMessage()}"); 430 return null; 431 } 432 } 433 434 public static function getSecondaryColor($default = null): ?ColorRgb 435 { 436 $value = Site::getSecondaryColorValue($default); 437 if ($value === null) { 438 return null; 439 } 440 try { 441 return ColorRgb::createFromString($value); 442 } catch (ExceptionCombo $e) { 443 LogUtility::msg("The secondary color value configuration ($value) is not valid. Error: {$e->getMessage()}"); 444 return null; 445 } 446 } 447 448 public static function setSecondaryColor(string $secondaryColorValue) 449 { 450 PluginUtility::setConf(ColorRgb::SECONDARY_COLOR_CONF, $secondaryColorValue); 451 } 452 453 public static function unsetPrimaryColor() 454 { 455 PluginUtility::setConf(ColorRgb::PRIMARY_COLOR_CONF, null); 456 } 457 458 459 public static function isBrandingColorInheritanceEnabled(): bool 460 { 461 return PluginUtility::getConfValue(ColorRgb::BRANDING_COLOR_INHERITANCE_ENABLE_CONF, ColorRgb::BRANDING_COLOR_INHERITANCE_ENABLE_CONF_DEFAULT) === 1; 462 } 463 464 public static function getRem(): int 465 { 466 $defaultRem = 16; 467 if (Site::getTemplate() === self::STRAP_TEMPLATE_NAME) { 468 $loaded = self::loadStrapUtilityTemplateIfPresentAndSameVersion(); 469 if ($loaded) { 470 $value = TplUtility::getRem(); 471 if ($value === null) { 472 return $defaultRem; 473 } 474 try { 475 return DataType::toInteger($value); 476 } catch (ExceptionCombo $e) { 477 LogUtility::msg("The rem configuration value ($value) is not a integer. Error: {$e->getMessage()}"); 478 } 479 } 480 } 481 return $defaultRem; 482 } 483 484 public static function enableBrandingColorInheritance() 485 { 486 PluginUtility::setConf(ColorRgb::BRANDING_COLOR_INHERITANCE_ENABLE_CONF, 1); 487 } 488 489 public static function setBrandingColorInheritanceToDefault() 490 { 491 PluginUtility::setConf(ColorRgb::BRANDING_COLOR_INHERITANCE_ENABLE_CONF, ColorRgb::BRANDING_COLOR_INHERITANCE_ENABLE_CONF_DEFAULT); 492 } 493 494 public static function getPrimaryColorForText(string $default = null): ?ColorRgb 495 { 496 $primaryColor = self::getPrimaryColor($default); 497 if ($primaryColor === null) { 498 return null; 499 } 500 try { 501 return $primaryColor 502 ->toHsl() 503 ->setSaturation(30) 504 ->setLightness(40) 505 ->toRgb() 506 ->toMinimumContrastRatioAgainstWhite(); 507 } catch (ExceptionCombo $e) { 508 LogUtility::msg("Error while calculating the primary text color. {$e->getMessage()}"); 509 return null; 510 } 511 } 512 513 /** 514 * More lightness than the text 515 * @return ColorRgb|null 516 */ 517 public static function getPrimaryColorTextHover(): ?ColorRgb 518 { 519 520 $primaryColor = self::getPrimaryColor(); 521 if ($primaryColor === null) { 522 return null; 523 } 524 try { 525 return $primaryColor 526 ->toHsl() 527 ->setSaturation(88) 528 ->setLightness(53) 529 ->toRgb() 530 ->toMinimumContrastRatioAgainstWhite(); 531 } catch (ExceptionCombo $e) { 532 LogUtility::msg("Error while calculating the secondary text color. {$e->getMessage()}"); 533 return null; 534 } 535 536 } 537 538 539 public static function getSecondarySlotNames(): array 540 { 541 542 try { 543 return [ 544 Site::getSidebarName(), 545 Site::getHeaderSlotPageName(), 546 Site::getFooterSlotPageName(), 547 Site::getMainHeaderSlotName(), 548 Site::getMainFooterSlotName() 549 ]; 550 } catch (ExceptionCombo $e) { 551 // We known at least this one 552 return [Site::getSidebarName()]; 553 } 554 555 556 } 557 558 559 /** 560 * @throws ExceptionCombo if the strap template is not installed or could not be loaded 561 */ 562 public static function getMainHeaderSlotName(): ?string 563 { 564 self::loadStrapUtilityTemplateIfPresentAndSameVersion(); 565 return TplUtility::getMainHeaderSlotName(); 566 } 567 568 /** 569 * Strap is loaded only if this is the same version 570 * to avoid function, class, or members that does not exist 571 * @throws ExceptionCombo if strap template utility class could not be loaded 572 */ 573 public static function loadStrapUtilityTemplateIfPresentAndSameVersion(): void 574 { 575 576 if (class_exists("ComboStrap\TplUtility")) { 577 return; 578 } 579 580 $templateUtilityFile = __DIR__ . '/../../../tpl/strap/class/TplUtility.php'; 581 if (file_exists($templateUtilityFile)) { 582 /** 583 * Check the version 584 */ 585 $templateInfo = confToHash(__DIR__ . '/../../../tpl/strap/template.info.txt'); 586 $templateVersion = $templateInfo['version']; 587 $comboVersion = PluginUtility::$INFO_PLUGIN['version']; 588 if ($templateVersion != $comboVersion) { 589 $strapName = "Strap"; 590 $comboName = "Combo"; 591 $strapLink = "<a href=\"https://www.dokuwiki.org/template:strap\">$strapName</a>"; 592 $comboLink = "<a href=\"https://www.dokuwiki.org/plugin:combo\">$comboName</a>"; 593 if ($comboVersion > $templateVersion) { 594 $upgradeTarget = $strapName; 595 } else { 596 $upgradeTarget = $comboName; 597 } 598 $upgradeLink = "<a href=\"" . wl() . "&do=admin&page=extension" . "\">upgrade <b>$upgradeTarget</b> via the extension manager</a>"; 599 $message = "You should $upgradeLink to the latest version to get a fully functional experience. The version of $comboLink is ($comboVersion) while the version of $strapLink is ($templateVersion)."; 600 LogUtility::msg($message); 601 throw new ExceptionCombo($message); 602 } else { 603 /** @noinspection PhpIncludeInspection */ 604 require_once($templateUtilityFile); 605 606 } 607 } 608 609 if (Site::getTemplate() !== self::STRAP_TEMPLATE_NAME) { 610 $message = "The strap template is not installed"; 611 } else { 612 $message = "The file ($templateUtilityFile) was not found"; 613 } 614 throw new ExceptionCombo($message); 615 616 } 617 618 /** 619 * @throws ExceptionCombo 620 */ 621 public static function getSideKickSlotPageName() 622 { 623 624 Site::loadStrapUtilityTemplateIfPresentAndSameVersion(); 625 return TplUtility::getSideKickSlotPageName(); 626 627 } 628 629 /** 630 * @throws ExceptionCombo 631 */ 632 public static function getFooterSlotPageName() 633 { 634 Site::loadStrapUtilityTemplateIfPresentAndSameVersion(); 635 return TplUtility::getFooterSlotPageName(); 636 } 637 638 /** 639 * @throws ExceptionCombo 640 */ 641 public static function getHeaderSlotPageName() 642 { 643 Site::loadStrapUtilityTemplateIfPresentAndSameVersion(); 644 return TplUtility::getHeaderSlotPageName(); 645 } 646 647 /** 648 * @throws ExceptionCombo 649 */ 650 public static function setConfStrapTemplate($name, $value) 651 { 652 Site::loadStrapUtilityTemplateIfPresentAndSameVersion(); 653 TplUtility::setConf($name, $value); 654 655 } 656 657 /** 658 * @throws ExceptionCombo 659 */ 660 public static function getMainFooterSlotName(): string 661 { 662 self::loadStrapUtilityTemplateIfPresentAndSameVersion(); 663 return TplUtility::getMainFooterSlotName(); 664 } 665 666 public static function getPrimaryColorValue($default = null) 667 { 668 $value = PluginUtility::getConfValue(ColorRgb::PRIMARY_COLOR_CONF, $default); 669 if ($value !== null && trim($value) !== "") { 670 return $value; 671 } 672 if (PluginUtility::isTest()) { 673 // too much trouble 674 // the load of styles is not consistent 675 return null; 676 } 677 $styles = ColorRgb::getDokuWikiStyles(); 678 return $styles["replacements"]["__theme_color__"]; 679 680 } 681 682 public static function getSecondaryColorValue($default = null) 683 { 684 $value = PluginUtility::getConfValue(ColorRgb::SECONDARY_COLOR_CONF, $default); 685 if ($value === null || trim($value) === "") { 686 return null; 687 } 688 return $value; 689 } 690 691 public static function setCanonicalUrlType(string $value) 692 { 693 PluginUtility::setConf(PageUrlType::CONF_CANONICAL_URL_TYPE, $value); 694 } 695 696 public static function setCanonicalUrlTypeToDefault() 697 { 698 PluginUtility::setConf(PageUrlType::CONF_CANONICAL_URL_TYPE, null); 699 } 700 701 public static function isBrandingColorInheritanceFunctional(): bool 702 { 703 return self::isBrandingColorInheritanceEnabled() && Site::getPrimaryColorValue() !== null; 704 } 705 706 public static function getMediaDirectory(): LocalPath 707 { 708 global $conf; 709 $mediaDirectory = $conf['mediadir']; 710 if ($mediaDirectory === null) { 711 throw new ExceptionComboRuntime("The media directory ($mediaDirectory) is null"); 712 } 713 return LocalPath::createFromPath($mediaDirectory); 714 } 715 716 public static function getCacheDirectory(): LocalPath 717 { 718 global $conf; 719 $cacheDirectory = $conf['cachedir']; 720 if ($cacheDirectory === null) { 721 throw new ExceptionComboRuntime("The cache directory ($cacheDirectory) is null"); 722 } 723 return LocalPath::createFromPath($cacheDirectory); 724 } 725 726 727 public static function getComboHome(): LocalPath 728 { 729 return LocalPath::create(DOKU_PLUGIN . PluginUtility::PLUGIN_BASE_NAME); 730 } 731 732 public static function getComboImagesDirectory(): LocalPath 733 { 734 return self::getComboResourcesDirectory()->resolve("images"); 735 } 736 737 public static function getComboResourcesDirectory(): LocalPath 738 { 739 return Site::getComboHome()->resolve("resources"); 740 } 741 742 public static function getComboDictionaryDirectory(): LocalPath 743 { 744 return Site::getComboResourcesDirectory()->resolve("dictionary"); 745 } 746 747 public static function getComboResourceSnippetDirectory(): LocalPath 748 { 749 return Site::getComboResourcesDirectory()->resolve("snippet"); 750 } 751 752 public static function getLogoHtml(): ?string 753 { 754 755 $tagAttributes = TagAttributes::createEmpty("identity"); 756 $tagAttributes->addComponentAttributeValue(Dimension::WIDTH_KEY, "72"); 757 $tagAttributes->addComponentAttributeValue(Dimension::HEIGHT_KEY, "72"); 758 $tagAttributes->addComponentAttributeValue(TagAttributes::TYPE_KEY, SvgDocument::ICON_TYPE); 759 $tagAttributes->addClassName("logo"); 760 761 762 /** 763 * Logo 764 */ 765 $logoImages = Site::getLogoImages(); 766 foreach ($logoImages as $logoImage) { 767 $path = $logoImage->getPath(); 768 $mediaLink = MediaLink::createMediaLinkFromPath($path, $tagAttributes) 769 ->setLazyLoad(false); 770 try { 771 return $mediaLink->renderMediaTag(); 772 } catch (ExceptionCombo $e) { 773 LogUtility::msg("Error while rendering the logo $logoImage"); 774 } 775 } 776 777 return null; 778 } 779 780 781} 782