1*04fd306cSNickeau<?php 2*04fd306cSNickeau/** 3*04fd306cSNickeau * Copyright (c) 2020. ComboStrap, Inc. and its affiliates. All Rights Reserved. 4*04fd306cSNickeau * 5*04fd306cSNickeau * This source code is licensed under the GPL license found in the 6*04fd306cSNickeau * COPYING file in the root directory of this source tree. 7*04fd306cSNickeau * 8*04fd306cSNickeau * @license GPL 3 (https://www.gnu.org/licenses/gpl-3.0.en.html) 9*04fd306cSNickeau * @author ComboStrap <support@combostrap.com> 10*04fd306cSNickeau * 11*04fd306cSNickeau */ 12*04fd306cSNickeau 13*04fd306cSNickeaunamespace ComboStrap; 14*04fd306cSNickeau 15*04fd306cSNickeaurequire_once(__DIR__ . '/PluginUtility.php'); 16*04fd306cSNickeau 17*04fd306cSNickeau 18*04fd306cSNickeau/** 19*04fd306cSNickeau * Class Icon 20*04fd306cSNickeau * @package ComboStrap 21*04fd306cSNickeau * @see https://combostrap.com/icon 22*04fd306cSNickeau * 23*04fd306cSNickeau * 24*04fd306cSNickeau * Material design does not have a repository structure where we can extract the location 25*04fd306cSNickeau * from the name 26*04fd306cSNickeau * https://material.io/resources/icons https://google.github.io/material-design-icons/ 27*04fd306cSNickeau * 28*04fd306cSNickeau * Injection via javascript to avoid problem with the php svgsimple library 29*04fd306cSNickeau * https://www.npmjs.com/package/svg-injector 30*04fd306cSNickeau */ 31*04fd306cSNickeauclass IconDownloader 32*04fd306cSNickeau{ 33*04fd306cSNickeau 34*04fd306cSNickeau const CONF_ICONS_MEDIA_NAMESPACE = "icons_namespace"; 35*04fd306cSNickeau const CONF_ICONS_MEDIA_NAMESPACE_DEFAULT = ":" . PluginUtility::COMBOSTRAP_NAMESPACE_NAME . ":icons"; 36*04fd306cSNickeau 37*04fd306cSNickeau 38*04fd306cSNickeau const ICON_LIBRARY_URLS = array( 39*04fd306cSNickeau self::ANT_DESIGN => "https://raw.githubusercontent.com/ant-design/ant-design-icons/master/packages/icons-svg/svg", 40*04fd306cSNickeau self::BOOTSTRAP => "https://raw.githubusercontent.com/twbs/icons/main/icons", 41*04fd306cSNickeau self::CARBON => "https://raw.githubusercontent.com/carbon-design-system/carbon/main/packages/icons/src/svg/32", 42*04fd306cSNickeau self::CLARITY => "https://raw.githubusercontent.com/vmware/clarity-assets/master/icons/essential", 43*04fd306cSNickeau self::CODE_ICON => "https://raw.githubusercontent.com/microsoft/vscode-codicons/main/src/icons", 44*04fd306cSNickeau self::ELEGANT_THEME => "https://raw.githubusercontent.com/pprince/etlinefont-bower/master/images/svg/individual_icons", 45*04fd306cSNickeau self::ENTYPO => "https://raw.githubusercontent.com/hypermodules/entypo/master/src/Entypo", 46*04fd306cSNickeau self::ENTYPO_SOCIAL => "https://raw.githubusercontent.com/hypermodules/entypo/master/src/Entypo%20Social%20Extension", 47*04fd306cSNickeau self::EVA => "https://raw.githubusercontent.com/akveo/eva-icons/master/package/icons", 48*04fd306cSNickeau self::FEATHER => "https://raw.githubusercontent.com/feathericons/feather/master/icons", 49*04fd306cSNickeau self::FAD => "https://raw.githubusercontent.com/fefanto/fontaudio/master/svgs", 50*04fd306cSNickeau self::ICONSCOUT => "https://raw.githubusercontent.com/Iconscout/unicons/master/svg/line", 51*04fd306cSNickeau self::LOGOS => "https://raw.githubusercontent.com/gilbarbara/logos/master/logos", 52*04fd306cSNickeau self::MATERIAL_DESIGN => "https://raw.githubusercontent.com/Templarian/MaterialDesign/master/svg", 53*04fd306cSNickeau self::OCTICON => "https://raw.githubusercontent.com/primer/octicons/main/icons", 54*04fd306cSNickeau self::TWEET_EMOJI => "https://raw.githubusercontent.com/twitter/twemoji/master/assets/svg", 55*04fd306cSNickeau self::SIMPLE_LINE => "https://raw.githubusercontent.com/thesabbir/simple-line-icons/master/src/svgs", 56*04fd306cSNickeau self::ICOMOON => "https://raw.githubusercontent.com/Keyamoon/IcoMoon-Free/master/SVG", 57*04fd306cSNickeau self::DASHICONS => "https://raw.githubusercontent.com/WordPress/dashicons/master/svg-min", 58*04fd306cSNickeau self::ICONOIR => "https://raw.githubusercontent.com/lucaburgio/iconoir/master/icons", 59*04fd306cSNickeau self::BOX_ICON => "https://raw.githubusercontent.com/atisawd/boxicons/master/svg", 60*04fd306cSNickeau self::LINE_AWESOME => "https://raw.githubusercontent.com/icons8/line-awesome/master/svg", 61*04fd306cSNickeau self::FONT_AWESOME_SOLID => "https://raw.githubusercontent.com/FortAwesome/Font-Awesome/master/svgs/solid", 62*04fd306cSNickeau self::FONT_AWESOME_BRANDS => "https://raw.githubusercontent.com/FortAwesome/Font-Awesome/master/svgs/brands", 63*04fd306cSNickeau self::FONT_AWESOME_REGULAR => "https://raw.githubusercontent.com/FortAwesome/Font-Awesome/master/svgs/regular", 64*04fd306cSNickeau self::VAADIN => "https://raw.githubusercontent.com/vaadin/vaadin-icons/master/assets/svg", 65*04fd306cSNickeau self::CORE_UI_BRAND => "https://raw.githubusercontent.com/coreui/coreui-icons/master/svg/brand", 66*04fd306cSNickeau self::FLAT_COLOR_ICON => "https://raw.githubusercontent.com/icons8/flat-color-icons/master/svg", 67*04fd306cSNickeau self::PHOSPHOR_ICONS => "https://raw.githubusercontent.com/phosphor-icons/phosphor-icons/master/assets", 68*04fd306cSNickeau self::VSCODE => "https://raw.githubusercontent.com/vscode-icons/vscode-icons/master/icons", 69*04fd306cSNickeau self::SI_GLYPH => "https://raw.githubusercontent.com/frexy/glyph-iconset/master/svg", 70*04fd306cSNickeau self::AKAR_ICONS => "https://raw.githubusercontent.com/artcoholic/akar-icons/master/src/svg", 71*04fd306cSNickeau self::ARCTICONS => "https://raw.githubusercontent.com/Donnnno/Arcticons/main/icons/black", 72*04fd306cSNickeau self::HEALTH_ICONS => "https://raw.githubusercontent.com/resolvetosavelives/healthicons/main/public/icons/svg" 73*04fd306cSNickeau ); 74*04fd306cSNickeau 75*04fd306cSNickeau const ICON_LIBRARY_WEBSITE_URLS = array( 76*04fd306cSNickeau self::BOOTSTRAP => "https://icons.getbootstrap.com/", 77*04fd306cSNickeau self::MATERIAL_DESIGN => "https://materialdesignicons.com/", 78*04fd306cSNickeau self::FEATHER => "https://feathericons.com/", 79*04fd306cSNickeau self::CODE_ICON => "https://microsoft.github.io/vscode-codicons/", 80*04fd306cSNickeau self::LOGOS => "https://svgporn.com/", 81*04fd306cSNickeau self::CARBON => "https://www.carbondesignsystem.com/guidelines/icons/library/", 82*04fd306cSNickeau self::TWEET_EMOJI => "https://twemoji.twitter.com/", 83*04fd306cSNickeau self::ANT_DESIGN => "https://ant.design/components/icon/", 84*04fd306cSNickeau self::CLARITY => "https://clarity.design/foundation/icons/", 85*04fd306cSNickeau self::OCTICON => "https://primer.style/octicons/", 86*04fd306cSNickeau self::ICONSCOUT => "https://iconscout.com/unicons/explore/line", 87*04fd306cSNickeau self::ELEGANT_THEME => "https://github.com/pprince/etlinefont-bower", 88*04fd306cSNickeau self::EVA => "https://akveo.github.io/eva-icons/", 89*04fd306cSNickeau self::ENTYPO_SOCIAL => "http://www.entypo.com", 90*04fd306cSNickeau self::ENTYPO => "http://www.entypo.com", 91*04fd306cSNickeau self::SIMPLE_LINE => "https://thesabbir.github.io/simple-line-icons", 92*04fd306cSNickeau self::ICOMOON => "https://icomoon.io/", 93*04fd306cSNickeau self::DASHICONS => "https://developer.wordpress.org/resource/dashicons/", 94*04fd306cSNickeau self::ICONOIR => "https://iconoir.com", 95*04fd306cSNickeau self::BOX_ICON => "https://boxicons.com", 96*04fd306cSNickeau self::LINE_AWESOME => "https://icons8.com/line-awesome", 97*04fd306cSNickeau self::FONT_AWESOME => "https://fontawesome.com/", 98*04fd306cSNickeau self::FONT_AWESOME_SOLID => "https://fontawesome.com/", 99*04fd306cSNickeau self::FONT_AWESOME_BRANDS => "https://fontawesome.com/", 100*04fd306cSNickeau self::FONT_AWESOME_REGULAR => "https://fontawesome.com/", 101*04fd306cSNickeau self::VAADIN => "https://vaadin.com/icons", 102*04fd306cSNickeau self::CORE_UI_BRAND => "https://coreui.io/icons/", 103*04fd306cSNickeau self::FLAT_COLOR_ICON => "https://icons8.com/icons/color", 104*04fd306cSNickeau self::PHOSPHOR_ICONS => "https://phosphoricons.com/", 105*04fd306cSNickeau self::VSCODE => "https://marketplace.visualstudio.com/items?itemName=vscode-icons-team.vscode-icons", 106*04fd306cSNickeau self::SI_GLYPH => "https://glyph.smarticons.co/", 107*04fd306cSNickeau self::AKAR_ICONS => "https://akaricons.com/", 108*04fd306cSNickeau self::ARCTICONS => "https://arcticons.com/", 109*04fd306cSNickeau self::HEALTH_ICONS => "https://healthicons.org/", 110*04fd306cSNickeau self::MATERIAL_DESIGN_ACRONYM => "https://materialdesignicons.com/", 111*04fd306cSNickeau self::COMBO => "" 112*04fd306cSNickeau ); 113*04fd306cSNickeau 114*04fd306cSNickeau const CONF_DEFAULT_ICON_LIBRARY = "defaultIconLibrary"; 115*04fd306cSNickeau const CONF_DEFAULT_ICON_LIBRARY_DEFAULT = self::MATERIAL_DESIGN_ACRONYM; 116*04fd306cSNickeau 117*04fd306cSNickeau /** 118*04fd306cSNickeau * Deprecated library acronym / name 119*04fd306cSNickeau */ 120*04fd306cSNickeau const DEPRECATED_LIBRARY_ACRONYM = array( 121*04fd306cSNickeau "bs" => self::BOOTSTRAP, // old one (deprecated) - the good acronym is bi (seen also in the class) 122*04fd306cSNickeau "md" => self::MATERIAL_DESIGN 123*04fd306cSNickeau ); 124*04fd306cSNickeau 125*04fd306cSNickeau /** 126*04fd306cSNickeau * Public known acronym / name (Used in the configuration) 127*04fd306cSNickeau */ 128*04fd306cSNickeau const PUBLIC_LIBRARY_ACRONYM = array( 129*04fd306cSNickeau "bi" => self::BOOTSTRAP, 130*04fd306cSNickeau self::MATERIAL_DESIGN_ACRONYM => self::MATERIAL_DESIGN, 131*04fd306cSNickeau "fe" => self::FEATHER, 132*04fd306cSNickeau "codicon" => self::CODE_ICON, 133*04fd306cSNickeau "logos" => self::LOGOS, 134*04fd306cSNickeau "carbon" => self::CARBON, 135*04fd306cSNickeau "twemoji" => self::TWEET_EMOJI, 136*04fd306cSNickeau "ant-design" => self::ANT_DESIGN, 137*04fd306cSNickeau "fad" => self::FAD, 138*04fd306cSNickeau "clarity" => self::CLARITY, 139*04fd306cSNickeau "octicon" => self::OCTICON, 140*04fd306cSNickeau "uit" => self::ICONSCOUT, 141*04fd306cSNickeau "et" => self::ELEGANT_THEME, 142*04fd306cSNickeau "eva" => self::EVA, 143*04fd306cSNickeau "entypo-social" => self::ENTYPO_SOCIAL, 144*04fd306cSNickeau "entypo" => self::ENTYPO, 145*04fd306cSNickeau "simple-line-icons" => self::SIMPLE_LINE, 146*04fd306cSNickeau "icomoon-free" => self::ICOMOON, 147*04fd306cSNickeau "dashicons" => self::DASHICONS, 148*04fd306cSNickeau "iconoir" => self::ICONOIR, 149*04fd306cSNickeau "bx" => self::BOX_ICON, 150*04fd306cSNickeau "la" => self::LINE_AWESOME, 151*04fd306cSNickeau "fa-solid" => self::FONT_AWESOME_SOLID, 152*04fd306cSNickeau "fa-brands" => self::FONT_AWESOME_BRANDS, 153*04fd306cSNickeau "fa-regular" => self::FONT_AWESOME_REGULAR, 154*04fd306cSNickeau "vaadin" => self::VAADIN, 155*04fd306cSNickeau "cib" => self::CORE_UI_BRAND, 156*04fd306cSNickeau "flat-color-icons" => self::FLAT_COLOR_ICON, 157*04fd306cSNickeau "ph" => self::PHOSPHOR_ICONS, 158*04fd306cSNickeau "vscode-icons" => self::VSCODE, 159*04fd306cSNickeau "si-glyph" => self::SI_GLYPH, 160*04fd306cSNickeau "akar-icons" => self::AKAR_ICONS, 161*04fd306cSNickeau "arcticons" => self::ARCTICONS, 162*04fd306cSNickeau "healthicons" => self::HEALTH_ICONS, 163*04fd306cSNickeau "combo" => self::COMBO 164*04fd306cSNickeau ); 165*04fd306cSNickeau 166*04fd306cSNickeau const FEATHER = "feather"; 167*04fd306cSNickeau const BOOTSTRAP = "bootstrap"; 168*04fd306cSNickeau const MATERIAL_DESIGN = "material-design"; 169*04fd306cSNickeau const CODE_ICON = "codicon"; 170*04fd306cSNickeau const LOGOS = "logos"; 171*04fd306cSNickeau const CARBON = "carbon"; 172*04fd306cSNickeau const MATERIAL_DESIGN_ACRONYM = "mdi"; 173*04fd306cSNickeau const TWEET_EMOJI = "twemoji"; 174*04fd306cSNickeau const ANT_DESIGN = "ant-design"; 175*04fd306cSNickeau const FAD = "fad"; 176*04fd306cSNickeau const CLARITY = "clarity"; 177*04fd306cSNickeau const OCTICON = "octicon"; 178*04fd306cSNickeau const ICONSCOUT = "iconscout"; 179*04fd306cSNickeau const ELEGANT_THEME = "elegant-theme"; 180*04fd306cSNickeau const EVA = "eva"; 181*04fd306cSNickeau const ENTYPO_SOCIAL = "entypo-social"; 182*04fd306cSNickeau const ENTYPO = "entypo"; 183*04fd306cSNickeau const SIMPLE_LINE = "simple-line"; 184*04fd306cSNickeau const ICOMOON = "icomoon"; 185*04fd306cSNickeau const DASHICONS = " dashicons"; 186*04fd306cSNickeau const ICONOIR = "iconoir"; 187*04fd306cSNickeau const BOX_ICON = "box-icon"; 188*04fd306cSNickeau const LINE_AWESOME = "line-awesome"; 189*04fd306cSNickeau const FONT_AWESOME_SOLID = "font-awesome-solid"; 190*04fd306cSNickeau const FONT_AWESOME_BRANDS = "font-awesome-brands"; 191*04fd306cSNickeau const FONT_AWESOME_REGULAR = "font-awesome-regular"; 192*04fd306cSNickeau const FONT_AWESOME = "font-awesome"; 193*04fd306cSNickeau const VAADIN = "vaadin"; 194*04fd306cSNickeau const CORE_UI_BRAND = "cib"; 195*04fd306cSNickeau const FLAT_COLOR_ICON = "flat-color-icons"; 196*04fd306cSNickeau const PHOSPHOR_ICONS = "ph"; 197*04fd306cSNickeau const VSCODE = "vscode"; 198*04fd306cSNickeau const SI_GLYPH = "si-glyph"; 199*04fd306cSNickeau const COMBO = WikiPath::COMBO_DRIVE; 200*04fd306cSNickeau const AKAR_ICONS = "akar-icons"; 201*04fd306cSNickeau const ARCTICONS = "articons"; 202*04fd306cSNickeau const HEALTH_ICONS = "healthicons"; 203*04fd306cSNickeau 204*04fd306cSNickeau 205*04fd306cSNickeau /** 206*04fd306cSNickeau * The icon library 207*04fd306cSNickeau * @var mixed|null 208*04fd306cSNickeau */ 209*04fd306cSNickeau private $library; 210*04fd306cSNickeau /** 211*04fd306cSNickeau * @var false|string 212*04fd306cSNickeau */ 213*04fd306cSNickeau private $iconName; 214*04fd306cSNickeau private WikiPath $path; 215*04fd306cSNickeau 216*04fd306cSNickeau 217*04fd306cSNickeau /** 218*04fd306cSNickeau * @throws ExceptionBadArgument|ExceptionFileSystem 219*04fd306cSNickeau */ 220*04fd306cSNickeau public function __construct(string $name) 221*04fd306cSNickeau { 222*04fd306cSNickeau 223*04fd306cSNickeau $iconNameSpace = SiteConfig::getConfValue(self::CONF_ICONS_MEDIA_NAMESPACE, self::CONF_ICONS_MEDIA_NAMESPACE_DEFAULT); 224*04fd306cSNickeau if (substr($iconNameSpace, 0, 1) != WikiPath::NAMESPACE_SEPARATOR_DOUBLE_POINT) { 225*04fd306cSNickeau $iconNameSpace = WikiPath::NAMESPACE_SEPARATOR_DOUBLE_POINT . $iconNameSpace; 226*04fd306cSNickeau } 227*04fd306cSNickeau if (substr($iconNameSpace, -1) != WikiPath::NAMESPACE_SEPARATOR_DOUBLE_POINT) { 228*04fd306cSNickeau $iconNameSpace = $iconNameSpace . ":"; 229*04fd306cSNickeau } 230*04fd306cSNickeau $mediaPathId = $iconNameSpace . $name . ".svg";; 231*04fd306cSNickeau $this->path = WikiPath::createMediaPathFromPath($mediaPathId); 232*04fd306cSNickeau // Bug: null file created when the stream could not get any byte 233*04fd306cSNickeau // We delete them 234*04fd306cSNickeau if (FileSystems::exists($this->path)) { 235*04fd306cSNickeau if (FileSystems::getSize($this->path) === 0) { 236*04fd306cSNickeau FileSystems::delete($this->path); 237*04fd306cSNickeau } 238*04fd306cSNickeau } 239*04fd306cSNickeau 240*04fd306cSNickeau /** 241*04fd306cSNickeau * Name parsing to extract the library name and icon name 242*04fd306cSNickeau */ 243*04fd306cSNickeau // default 244*04fd306cSNickeau $confValue = SiteConfig::getConfValue(self::CONF_DEFAULT_ICON_LIBRARY, self::CONF_DEFAULT_ICON_LIBRARY_DEFAULT); 245*04fd306cSNickeau $this->setLibrary($confValue); 246*04fd306cSNickeau $this->setIconName($name); 247*04fd306cSNickeau // parse 248*04fd306cSNickeau $sepPosition = strpos($name, ":"); 249*04fd306cSNickeau if ($sepPosition !== false) { 250*04fd306cSNickeau $libraryName = substr($name, 0, $sepPosition); 251*04fd306cSNickeau $this->setLibrary($libraryName); 252*04fd306cSNickeau $iconName = substr($name, $sepPosition + 1); 253*04fd306cSNickeau $this->setIconName($iconName); 254*04fd306cSNickeau 255*04fd306cSNickeau /** 256*04fd306cSNickeau * Special case, internal library 257*04fd306cSNickeau */ 258*04fd306cSNickeau if ($this->getLibrary() === self::COMBO) { 259*04fd306cSNickeau $this->path = WikiPath::createComboResource($iconName . ".svg"); 260*04fd306cSNickeau } 261*04fd306cSNickeau } 262*04fd306cSNickeau 263*04fd306cSNickeau 264*04fd306cSNickeau } 265*04fd306cSNickeau 266*04fd306cSNickeau 267*04fd306cSNickeau public static 268*04fd306cSNickeau function isInIconDirectory(Path $path): bool 269*04fd306cSNickeau { 270*04fd306cSNickeau $iconNameSpace = SiteConfig::getConfValue(IconDownloader::CONF_ICONS_MEDIA_NAMESPACE, IconDownloader::CONF_ICONS_MEDIA_NAMESPACE_DEFAULT); 271*04fd306cSNickeau if (strpos($path->toAbsoluteId(), $iconNameSpace) !== false) { 272*04fd306cSNickeau return true; 273*04fd306cSNickeau } 274*04fd306cSNickeau return false; 275*04fd306cSNickeau } 276*04fd306cSNickeau 277*04fd306cSNickeau 278*04fd306cSNickeau /** 279*04fd306cSNickeau * @throws ExceptionBadArgument - if the icon library is not supported 280*04fd306cSNickeau * @throws ExceptionFileSystem 281*04fd306cSNickeau */ 282*04fd306cSNickeau public static function createFromName(string $name): IconDownloader 283*04fd306cSNickeau { 284*04fd306cSNickeau return new IconDownloader($name); 285*04fd306cSNickeau } 286*04fd306cSNickeau 287*04fd306cSNickeau /** 288*04fd306cSNickeau * @throws ExceptionCompile 289*04fd306cSNickeau */ 290*04fd306cSNickeau private static function getPhysicalNameFromDictionary(string $logicalName, string $library) 291*04fd306cSNickeau { 292*04fd306cSNickeau 293*04fd306cSNickeau $jsonArray = Dictionary::getFrom("$library-icons"); 294*04fd306cSNickeau $physicalName = $jsonArray[$logicalName]; 295*04fd306cSNickeau if ($physicalName === null) { 296*04fd306cSNickeau LogUtility::msg("The icon ($logicalName) is unknown for the library ($library)"); 297*04fd306cSNickeau // by default, just lowercase 298*04fd306cSNickeau return strtolower($logicalName); 299*04fd306cSNickeau } 300*04fd306cSNickeau return $physicalName; 301*04fd306cSNickeau 302*04fd306cSNickeau } 303*04fd306cSNickeau 304*04fd306cSNickeau public function getIconName(): string 305*04fd306cSNickeau { 306*04fd306cSNickeau return $this->iconName; 307*04fd306cSNickeau } 308*04fd306cSNickeau 309*04fd306cSNickeau /** 310*04fd306cSNickeau * @throws ExceptionCompile 311*04fd306cSNickeau */ 312*04fd306cSNickeau public function getDownloadUrl(): string 313*04fd306cSNickeau { 314*04fd306cSNickeau 315*04fd306cSNickeau /** 316*04fd306cSNickeau * The test of the supported library 317*04fd306cSNickeau * happens lately because the user may install them manually 318*04fd306cSNickeau */ 319*04fd306cSNickeau $library = $this->library; 320*04fd306cSNickeau if (!in_array($library, array_keys($this->getLibraries()))) { 321*04fd306cSNickeau throw new ExceptionBadArgument("The library ($library) is not a icon library supported"); 322*04fd306cSNickeau } 323*04fd306cSNickeau 324*04fd306cSNickeau // Get the qualified library name 325*04fd306cSNickeau $acronymLibraries = self::getLibraries(); 326*04fd306cSNickeau if (isset($acronymLibraries[$library])) { 327*04fd306cSNickeau $library = $acronymLibraries[$library]; 328*04fd306cSNickeau } 329*04fd306cSNickeau 330*04fd306cSNickeau // Get the url 331*04fd306cSNickeau $iconLibraries = self::ICON_LIBRARY_URLS; 332*04fd306cSNickeau if (!isset($iconLibraries[$library])) { 333*04fd306cSNickeau throw new ExceptionCompile("The icon library ($library) is unknown. The icon could not be downloaded.", Icon::ICON_CANONICAL_NAME); 334*04fd306cSNickeau } else { 335*04fd306cSNickeau $iconBaseUrl = $iconLibraries[$library]; 336*04fd306cSNickeau } 337*04fd306cSNickeau 338*04fd306cSNickeau /** 339*04fd306cSNickeau * Name processing 340*04fd306cSNickeau */ 341*04fd306cSNickeau $iconName = $this->iconName; 342*04fd306cSNickeau switch ($library) { 343*04fd306cSNickeau 344*04fd306cSNickeau case self::VSCODE: 345*04fd306cSNickeau case self::FLAT_COLOR_ICON: 346*04fd306cSNickeau $iconName = str_replace("-", "_", $iconName); 347*04fd306cSNickeau break; 348*04fd306cSNickeau case self::TWEET_EMOJI: 349*04fd306cSNickeau try { 350*04fd306cSNickeau $iconName = self::getEmojiCodePoint($iconName); 351*04fd306cSNickeau } catch (ExceptionCompile $e) { 352*04fd306cSNickeau throw new ExceptionCompile("The emoji name $iconName is unknown. The emoji could not be downloaded.", Icon::ICON_CANONICAL_NAME, 0, $e); 353*04fd306cSNickeau } 354*04fd306cSNickeau break; 355*04fd306cSNickeau case self::ANT_DESIGN: 356*04fd306cSNickeau // table-outlined where table is the svg, outlined the category 357*04fd306cSNickeau // ordered-list-outlined where ordered-list is the svg, outlined the category 358*04fd306cSNickeau [$iconName, $iconType] = self::explodeInTwoPartsByLastPosition($iconName, "-"); 359*04fd306cSNickeau $iconBaseUrl .= "/$iconType"; 360*04fd306cSNickeau break; 361*04fd306cSNickeau case self::CARBON: 362*04fd306cSNickeau /** 363*04fd306cSNickeau * Iconify normalized the name of the carbon library (making them lowercase) 364*04fd306cSNickeau * 365*04fd306cSNickeau * For instance, CSV is csv (https://icon-sets.iconify.design/carbon/csv/) 366*04fd306cSNickeau * 367*04fd306cSNickeau * This dictionary reproduce it. 368*04fd306cSNickeau */ 369*04fd306cSNickeau $iconName = self::getPhysicalNameFromDictionary($iconName, self::CARBON); 370*04fd306cSNickeau break; 371*04fd306cSNickeau case self::FAD: 372*04fd306cSNickeau $iconName = self::getPhysicalNameFromDictionary($iconName, self::FAD); 373*04fd306cSNickeau break; 374*04fd306cSNickeau case self::ICOMOON: 375*04fd306cSNickeau $iconName = self::getPhysicalNameFromDictionary($iconName, self::ICOMOON); 376*04fd306cSNickeau break; 377*04fd306cSNickeau case self::CORE_UI_BRAND: 378*04fd306cSNickeau $iconName = self::getPhysicalNameFromDictionary($iconName, self::CORE_UI_BRAND); 379*04fd306cSNickeau break; 380*04fd306cSNickeau case self::EVA: 381*04fd306cSNickeau // Eva 382*04fd306cSNickeau // example: eva:facebook-fill 383*04fd306cSNickeau [$iconName, $iconType] = self::explodeInTwoPartsByLastPosition($iconName, "-"); 384*04fd306cSNickeau $iconBaseUrl .= "/$iconType/svg"; 385*04fd306cSNickeau if ($iconType === "outline") { 386*04fd306cSNickeau // for whatever reason, the name of outline icon has outline at the end 387*04fd306cSNickeau // and not for the fill icon 388*04fd306cSNickeau $iconName .= "-$iconType"; 389*04fd306cSNickeau } 390*04fd306cSNickeau break; 391*04fd306cSNickeau case self::PHOSPHOR_ICONS: 392*04fd306cSNickeau // example: activity-light 393*04fd306cSNickeau [$iconShortName, $iconType] = self::explodeInTwoPartsByLastPosition($iconName, "-"); 394*04fd306cSNickeau $iconBaseUrl .= "/$iconType"; 395*04fd306cSNickeau break; 396*04fd306cSNickeau case self::SIMPLE_LINE: 397*04fd306cSNickeau // Bug 398*04fd306cSNickeau if ($iconName === "social-pinterest") { 399*04fd306cSNickeau $iconName = "social-pintarest"; 400*04fd306cSNickeau } 401*04fd306cSNickeau break; 402*04fd306cSNickeau case self::BOX_ICON: 403*04fd306cSNickeau [$iconType, $extractedIconName] = self::explodeInTwoPartsByLastPosition($iconName, "-"); 404*04fd306cSNickeau switch ($iconType) { 405*04fd306cSNickeau case "bxl": 406*04fd306cSNickeau $iconBaseUrl .= "/logos"; 407*04fd306cSNickeau break; 408*04fd306cSNickeau case "bx": 409*04fd306cSNickeau $iconBaseUrl .= "/regular"; 410*04fd306cSNickeau break; 411*04fd306cSNickeau case "bxs": 412*04fd306cSNickeau $iconBaseUrl .= "/solid"; 413*04fd306cSNickeau break; 414*04fd306cSNickeau default: 415*04fd306cSNickeau throw new ExceptionCompile("The box-icon icon ($iconName) has a type ($iconType) that is unknown, we can't determine the location of the icon to download"); 416*04fd306cSNickeau } 417*04fd306cSNickeau break; 418*04fd306cSNickeau case self::SI_GLYPH: 419*04fd306cSNickeau $iconName = "si-glyph-" . $iconName; 420*04fd306cSNickeau break; 421*04fd306cSNickeau case self::HEALTH_ICONS: 422*04fd306cSNickeau [$extractedIconName, $iconType] = self::explodeInTwoPartsByLastPosition($iconName, "-"); 423*04fd306cSNickeau switch ($iconType) { 424*04fd306cSNickeau case "outline": 425*04fd306cSNickeau case "negative": 426*04fd306cSNickeau $iconBaseUrl .= "/$iconType"; 427*04fd306cSNickeau $iconName = $extractedIconName; 428*04fd306cSNickeau break; 429*04fd306cSNickeau default: 430*04fd306cSNickeau // no 431*04fd306cSNickeau $iconBaseUrl .= "/filled"; 432*04fd306cSNickeau } 433*04fd306cSNickeau $iconName = self::getPhysicalNameFromDictionary($iconName, self::HEALTH_ICONS); 434*04fd306cSNickeau break; 435*04fd306cSNickeau 436*04fd306cSNickeau } 437*04fd306cSNickeau 438*04fd306cSNickeau 439*04fd306cSNickeau // The url 440*04fd306cSNickeau return "$iconBaseUrl/$iconName.svg"; 441*04fd306cSNickeau 442*04fd306cSNickeau } 443*04fd306cSNickeau 444*04fd306cSNickeau /** 445*04fd306cSNickeau * @throws ExceptionCompile 446*04fd306cSNickeau */ 447*04fd306cSNickeau public function download() 448*04fd306cSNickeau { 449*04fd306cSNickeau 450*04fd306cSNickeau 451*04fd306cSNickeau $libraryName = $this->getLibrary(); 452*04fd306cSNickeau $mediaDokuPath = $this->path; 453*04fd306cSNickeau 454*04fd306cSNickeau /** 455*04fd306cSNickeau * Create the target directory if it does not exist 456*04fd306cSNickeau */ 457*04fd306cSNickeau $iconDir = $mediaDokuPath->getParent(); 458*04fd306cSNickeau if (!FileSystems::exists($iconDir)) { 459*04fd306cSNickeau try { 460*04fd306cSNickeau FileSystems::createDirectory($iconDir); 461*04fd306cSNickeau } catch (ExceptionCompile $e) { 462*04fd306cSNickeau throw new ExceptionCompile("The icon directory ($iconDir) could not be created.", Icon::ICON_CANONICAL_NAME, 0, $e); 463*04fd306cSNickeau } 464*04fd306cSNickeau } 465*04fd306cSNickeau 466*04fd306cSNickeau /** 467*04fd306cSNickeau * Download the icon 468*04fd306cSNickeau * The `@` delete the E_WARNING upon failure 469*04fd306cSNickeau * 470*04fd306cSNickeau * https://www.php.net/manual/en/function.fopen.php 471*04fd306cSNickeau */ 472*04fd306cSNickeau $downloadUrl = $this->getDownloadUrl(); 473*04fd306cSNickeau ErrorHandler::phpErrorAsException(); 474*04fd306cSNickeau try { 475*04fd306cSNickeau $filePointer = fopen($downloadUrl, 'r'); 476*04fd306cSNickeau } catch (\Exception $e) { 477*04fd306cSNickeau // (ie no icon file found at ($downloadUrl) 478*04fd306cSNickeau $message = "We couldn't find the <a href=\"$downloadUrl\">icon $this->iconName</a>) from the"; 479*04fd306cSNickeau try { 480*04fd306cSNickeau $urlLibrary = $this->getLibraryUrl(); 481*04fd306cSNickeau $message = "$message <a href=\"$urlLibrary\">library $libraryName</a>"; 482*04fd306cSNickeau } catch (ExceptionNotFound $e) { 483*04fd306cSNickeau if (PluginUtility::isDevOrTest()) { 484*04fd306cSNickeau throw $e; 485*04fd306cSNickeau } 486*04fd306cSNickeau $message = "$message library $libraryName"; 487*04fd306cSNickeau } 488*04fd306cSNickeau $message = "$message. Error: {$e->getMessage()}"; 489*04fd306cSNickeau throw new ExceptionCompile($message, Icon::ICON_CANONICAL_NAME); 490*04fd306cSNickeau } finally { 491*04fd306cSNickeau ErrorHandler::restore(); 492*04fd306cSNickeau } 493*04fd306cSNickeau 494*04fd306cSNickeau $numberOfByte = file_put_contents($mediaDokuPath->toLocalPath()->toAbsolutePath()->toAbsoluteId(), $filePointer); 495*04fd306cSNickeau if ($numberOfByte !== false) { 496*04fd306cSNickeau LogUtility::msg("The icon ($this) from the library ($libraryName) was downloaded to ($mediaDokuPath)", LogUtility::LVL_MSG_INFO, Icon::ICON_CANONICAL_NAME); 497*04fd306cSNickeau } else { 498*04fd306cSNickeau LogUtility::msg("Internal error: The icon ($this) from the library ($libraryName) could no be written to ($mediaDokuPath)", LogUtility::LVL_MSG_ERROR, Icon::ICON_CANONICAL_NAME); 499*04fd306cSNickeau } 500*04fd306cSNickeau 501*04fd306cSNickeau 502*04fd306cSNickeau } 503*04fd306cSNickeau 504*04fd306cSNickeau /** 505*04fd306cSNickeau * @param $iconName 506*04fd306cSNickeau * @param $mediaFilePath 507*04fd306cSNickeau * @deprecated Old code to download icon from the material design api 508*04fd306cSNickeau */ 509*04fd306cSNickeau public 510*04fd306cSNickeau static function downloadIconFromMaterialDesignApi($iconName, $mediaFilePath) 511*04fd306cSNickeau { 512*04fd306cSNickeau // Try the official API 513*04fd306cSNickeau // Read the icon meta of 514*04fd306cSNickeau // Meta Json file got all icons 515*04fd306cSNickeau // 516*04fd306cSNickeau // * Available at: https://raw.githubusercontent.com/Templarian/MaterialDesign/master/meta.json 517*04fd306cSNickeau // * See doc: https://github.com/Templarian/MaterialDesign-Site/blob/master/src/content/api.md) 518*04fd306cSNickeau $arrayFormat = true; 519*04fd306cSNickeau $iconMetaJson = json_decode(file_get_contents(__DIR__ . '/../resources/dictionary/icon-meta.json'), $arrayFormat); 520*04fd306cSNickeau $iconId = null; 521*04fd306cSNickeau foreach ($iconMetaJson as $key => $value) { 522*04fd306cSNickeau if ($value['name'] == $iconName) { 523*04fd306cSNickeau $iconId = $value['id']; 524*04fd306cSNickeau break; 525*04fd306cSNickeau } 526*04fd306cSNickeau } 527*04fd306cSNickeau if ($iconId != null) { 528*04fd306cSNickeau 529*04fd306cSNickeau // Download 530*04fd306cSNickeau // Call to the API 531*04fd306cSNickeau // https://dev.materialdesignicons.com/contribute/site/api 532*04fd306cSNickeau $downloadUrl = "https://materialdesignicons.com/api/download/icon/svg/$iconId"; 533*04fd306cSNickeau $filePointer = file_put_contents($mediaFilePath, fopen($downloadUrl, 'r')); 534*04fd306cSNickeau if ($filePointer == false) { 535*04fd306cSNickeau LogUtility::msg("The file ($downloadUrl) could not be downloaded to ($mediaFilePath)", LogUtility::LVL_MSG_ERROR, Icon::ICON_CANONICAL_NAME); 536*04fd306cSNickeau } else { 537*04fd306cSNickeau LogUtility::msg("The material design icon ($iconName) was downloaded to ($mediaFilePath)", LogUtility::LVL_MSG_INFO, Icon::ICON_CANONICAL_NAME); 538*04fd306cSNickeau } 539*04fd306cSNickeau 540*04fd306cSNickeau } 541*04fd306cSNickeau 542*04fd306cSNickeau } 543*04fd306cSNickeau 544*04fd306cSNickeau private static function getLibraries(): array 545*04fd306cSNickeau { 546*04fd306cSNickeau return array_merge( 547*04fd306cSNickeau self::PUBLIC_LIBRARY_ACRONYM, 548*04fd306cSNickeau self::DEPRECATED_LIBRARY_ACRONYM 549*04fd306cSNickeau ); 550*04fd306cSNickeau } 551*04fd306cSNickeau 552*04fd306cSNickeau /** 553*04fd306cSNickeau * @throws ExceptionCompile 554*04fd306cSNickeau */ 555*04fd306cSNickeau public static function getEmojiCodePoint(string $emojiName) 556*04fd306cSNickeau { 557*04fd306cSNickeau $path = DirectoryLayout::getComboDictionaryDirectory()->resolve("emojis.json"); 558*04fd306cSNickeau $jsonContent = FileSystems::getContent($path); 559*04fd306cSNickeau $jsonArray = Json::createFromString($jsonContent)->toArray(); 560*04fd306cSNickeau return $jsonArray[$emojiName]; 561*04fd306cSNickeau } 562*04fd306cSNickeau 563*04fd306cSNickeau 564*04fd306cSNickeau /** 565*04fd306cSNickeau * @param string $iconName 566*04fd306cSNickeau * @param string $sep 567*04fd306cSNickeau * @return array 568*04fd306cSNickeau * @throws ExceptionCompile 569*04fd306cSNickeau */ 570*04fd306cSNickeau private static function explodeInTwoPartsByLastPosition(string $iconName, string $sep = "-"): array 571*04fd306cSNickeau { 572*04fd306cSNickeau $index = strrpos($iconName, $sep); 573*04fd306cSNickeau if ($index === false) { 574*04fd306cSNickeau throw new ExceptionCompile ("We expect that the icon name ($iconName) has two parts separated by a `-` (example: table-outlined). The icon could not be downloaded.", Icon::ICON_CANONICAL_NAME); 575*04fd306cSNickeau } 576*04fd306cSNickeau $firstPart = substr($iconName, 0, $index); 577*04fd306cSNickeau $secondPart = substr($iconName, $index + 1); 578*04fd306cSNickeau return [$firstPart, $secondPart]; 579*04fd306cSNickeau } 580*04fd306cSNickeau 581*04fd306cSNickeau 582*04fd306cSNickeau public function __toString() 583*04fd306cSNickeau { 584*04fd306cSNickeau return $this->getIconName(); 585*04fd306cSNickeau } 586*04fd306cSNickeau 587*04fd306cSNickeau private function getLibrary() 588*04fd306cSNickeau { 589*04fd306cSNickeau return $this->library; 590*04fd306cSNickeau } 591*04fd306cSNickeau 592*04fd306cSNickeau 593*04fd306cSNickeau /** 594*04fd306cSNickeau * @noinspection PhpReturnValueOfMethodIsNeverUsedInspection 595*04fd306cSNickeau */ 596*04fd306cSNickeau private function setLibrary($libraryName): IconDownloader 597*04fd306cSNickeau { 598*04fd306cSNickeau /** 599*04fd306cSNickeau * The library may be not supported 600*04fd306cSNickeau * but the users can install them manually 601*04fd306cSNickeau * We test the support of the library if the logo does not exists 602*04fd306cSNickeau * on the file system 603*04fd306cSNickeau */ 604*04fd306cSNickeau $this->library = $libraryName; 605*04fd306cSNickeau return $this; 606*04fd306cSNickeau } 607*04fd306cSNickeau 608*04fd306cSNickeau private function setIconName(string $iconName) 609*04fd306cSNickeau { 610*04fd306cSNickeau $this->iconName = $iconName; 611*04fd306cSNickeau } 612*04fd306cSNickeau 613*04fd306cSNickeau public function getPath(): WikiPath 614*04fd306cSNickeau { 615*04fd306cSNickeau return $this->path; 616*04fd306cSNickeau } 617*04fd306cSNickeau 618*04fd306cSNickeau /** 619*04fd306cSNickeau * @throws ExceptionNotFound 620*04fd306cSNickeau */ 621*04fd306cSNickeau public function getLibraryUrl() 622*04fd306cSNickeau { 623*04fd306cSNickeau $library = $this->getLibrary(); 624*04fd306cSNickeau $libraryUrl = @self::ICON_LIBRARY_WEBSITE_URLS[$library]; 625*04fd306cSNickeau if ($libraryUrl === null) { 626*04fd306cSNickeau throw new ExceptionNotFound("The url for the library ($library) was not found"); 627*04fd306cSNickeau } 628*04fd306cSNickeau return $libraryUrl; 629*04fd306cSNickeau } 630*04fd306cSNickeau 631*04fd306cSNickeau 632*04fd306cSNickeau} 633