137748cd8SNickeau<?php 237748cd8SNickeau/** 337748cd8SNickeau * Copyright (c) 2020. ComboStrap, Inc. and its affiliates. All Rights Reserved. 437748cd8SNickeau * 537748cd8SNickeau * This source code is licensed under the GPL license found in the 637748cd8SNickeau * COPYING file in the root directory of this source tree. 737748cd8SNickeau * 837748cd8SNickeau * @license GPL 3 (https://www.gnu.org/licenses/gpl-3.0.en.html) 937748cd8SNickeau * @author ComboStrap <support@combostrap.com> 1037748cd8SNickeau * 1137748cd8SNickeau */ 1237748cd8SNickeau 1337748cd8SNickeaunamespace ComboStrap; 1437748cd8SNickeau 151fa8c418SNickeaurequire_once(__DIR__ . '/PluginUtility.php'); 1637748cd8SNickeau 1737748cd8SNickeau 1837748cd8SNickeau/** 1937748cd8SNickeau * Class IconUtility 2037748cd8SNickeau * @package ComboStrap 2137748cd8SNickeau * @see https://combostrap.com/icon 2237748cd8SNickeau * 2337748cd8SNickeau * 2437748cd8SNickeau * Material design does not have a repository structure where we can extract the location 2537748cd8SNickeau * from the name 2637748cd8SNickeau * https://material.io/resources/icons https://google.github.io/material-design-icons/ 2737748cd8SNickeau * 2837748cd8SNickeau * Injection via javascript to avoid problem with the php svgsimple library 2937748cd8SNickeau * https://www.npmjs.com/package/svg-injector 3037748cd8SNickeau */ 3137748cd8SNickeauclass Icon 3237748cd8SNickeau{ 3337748cd8SNickeau const CONF_ICONS_MEDIA_NAMESPACE = "icons_namespace"; 3437748cd8SNickeau const CONF_ICONS_MEDIA_NAMESPACE_DEFAULT = ":" . PluginUtility::COMBOSTRAP_NAMESPACE_NAME . ":icons"; 3537748cd8SNickeau // Canonical name 3637748cd8SNickeau const NAME = "icon"; 3737748cd8SNickeau 3837748cd8SNickeau 3937748cd8SNickeau const ICON_LIBRARY_URLS = array( 4037748cd8SNickeau self::BOOTSTRAP => "https://raw.githubusercontent.com/twbs/icons/main/icons", 4137748cd8SNickeau self::MATERIAL_DESIGN => "https://raw.githubusercontent.com/Templarian/MaterialDesign/master/svg", 42*c3437056SNickeau self::FEATHER => "https://raw.githubusercontent.com/feathericons/feather/master/icons", 43*c3437056SNickeau self::CODE_ICON => "https://raw.githubusercontent.com/microsoft/vscode-codicons/main/src/icons/", 44*c3437056SNickeau self::LOGOS => "https://raw.githubusercontent.com/gilbarbara/logos/master/logos/", 45*c3437056SNickeau self::CARBON => "https://raw.githubusercontent.com/carbon-design-system/carbon/main/packages/icons/src/svg/32/" 4637748cd8SNickeau ); 4737748cd8SNickeau 4837748cd8SNickeau const ICON_LIBRARY_WEBSITE_URLS = array( 4937748cd8SNickeau self::BOOTSTRAP => "https://icons.getbootstrap.com/", 5037748cd8SNickeau self::MATERIAL_DESIGN => "https://materialdesignicons.com/", 51*c3437056SNickeau self::FEATHER => "https://feathericons.com/", 52*c3437056SNickeau self::CODE_ICON => "https://microsoft.github.io/vscode-codicons/", 53*c3437056SNickeau self::LOGOS => "https://svgporn.com/", 54*c3437056SNickeau self::CARBON => "https://www.carbondesignsystem.com/guidelines/icons/library/" 5537748cd8SNickeau ); 5637748cd8SNickeau 5737748cd8SNickeau const CONF_DEFAULT_ICON_LIBRARY = "defaultIconLibrary"; 58*c3437056SNickeau const CONF_DEFAULT_ICON_LIBRARY_DEFAULT = self::MATERIAL_DESIGN_ACRONYM; 59*c3437056SNickeau 60*c3437056SNickeau /** 61*c3437056SNickeau * Deprecated library acronym / name 62*c3437056SNickeau */ 63*c3437056SNickeau const DEPRECATED_LIBRARY_ACRONYM = array( 64*c3437056SNickeau "bs" => self::BOOTSTRAP, // old one (deprecated) - the good acronym is bi (seen also in the class) 65*c3437056SNickeau "md" => self::MATERIAL_DESIGN 6637748cd8SNickeau ); 67*c3437056SNickeau 68*c3437056SNickeau /** 69*c3437056SNickeau * Public known acronym / name (Used in the configuration) 70*c3437056SNickeau */ 71*c3437056SNickeau const PUBLIC_LIBRARY_ACRONYM = array( 72*c3437056SNickeau "bi" => self::BOOTSTRAP, 73*c3437056SNickeau self::MATERIAL_DESIGN_ACRONYM => self::MATERIAL_DESIGN, 74*c3437056SNickeau "fe" => self::FEATHER, 75*c3437056SNickeau "codicon" => self::CODE_ICON, 76*c3437056SNickeau "logos" => self::LOGOS, 77*c3437056SNickeau "carbon" => self::CARBON 78*c3437056SNickeau ); 79*c3437056SNickeau 8037748cd8SNickeau const FEATHER = "feather"; 8137748cd8SNickeau const BOOTSTRAP = "bootstrap"; 8237748cd8SNickeau const MATERIAL_DESIGN = "material-design"; 83*c3437056SNickeau const CODE_ICON = "codicon"; 84*c3437056SNickeau const LOGOS = "logos"; 85*c3437056SNickeau const CARBON = "carbon"; 86*c3437056SNickeau const MATERIAL_DESIGN_ACRONYM = "mdi"; 8737748cd8SNickeau 8837748cd8SNickeau 8937748cd8SNickeau /** 9037748cd8SNickeau * The function used to render an icon 9137748cd8SNickeau * @param TagAttributes $tagAttributes - the icon attributes 9237748cd8SNickeau * @return bool|mixed - false if any error or the HTML 9337748cd8SNickeau */ 94*c3437056SNickeau static public function renderIconByAttributes(TagAttributes $tagAttributes) 9537748cd8SNickeau { 9637748cd8SNickeau 9737748cd8SNickeau 9837748cd8SNickeau $name = "name"; 9937748cd8SNickeau if (!$tagAttributes->hasComponentAttribute($name)) { 10037748cd8SNickeau LogUtility::msg("The attributes should have a name. It's mandatory for an icon.", LogUtility::LVL_MSG_ERROR, self::NAME); 10137748cd8SNickeau return false; 10237748cd8SNickeau } 10337748cd8SNickeau 10437748cd8SNickeau /** 10537748cd8SNickeau * The Name 10637748cd8SNickeau */ 10737748cd8SNickeau $iconNameAttribute = $tagAttributes->getValue($name); 10837748cd8SNickeau 10937748cd8SNickeau /** 11037748cd8SNickeau * If the name have an extension, it's a file from the media directory 11137748cd8SNickeau * Otherwise, it's an icon from a library 11237748cd8SNickeau */ 11337748cd8SNickeau $mediaDokuPath = DokuPath::createMediaPathFromId($iconNameAttribute); 11437748cd8SNickeau if (!empty($mediaDokuPath->getExtension())) { 11537748cd8SNickeau 11637748cd8SNickeau // loop through candidates until a match was found: 11737748cd8SNickeau // May be an icon from the templates 118*c3437056SNickeau if (!FileSystems::exists($mediaDokuPath)) { 11937748cd8SNickeau 12037748cd8SNickeau // Trying to see if it's not in the template images directory 12137748cd8SNickeau $message = "The media file could not be found in the media library. If you want an icon from an icon library, indicate a name without extension."; 12237748cd8SNickeau $message .= "<BR> Media File Library tested: $mediaDokuPath"; 12337748cd8SNickeau LogUtility::msg($message, LogUtility::LVL_MSG_ERROR, self::NAME); 12437748cd8SNickeau return false; 12537748cd8SNickeau 12637748cd8SNickeau } 12737748cd8SNickeau 12837748cd8SNickeau } else { 12937748cd8SNickeau 13037748cd8SNickeau // It may be a icon already downloaded 131*c3437056SNickeau $iconNameSpace = PluginUtility::getConfValue(self::CONF_ICONS_MEDIA_NAMESPACE, self::CONF_ICONS_MEDIA_NAMESPACE_DEFAULT); 13237748cd8SNickeau if (substr($iconNameSpace, 0, 1) != DokuPath::PATH_SEPARATOR) { 13337748cd8SNickeau $iconNameSpace = DokuPath::PATH_SEPARATOR . $iconNameSpace; 13437748cd8SNickeau } 13537748cd8SNickeau if (substr($iconNameSpace, -1) != DokuPath::PATH_SEPARATOR) { 13637748cd8SNickeau $iconNameSpace = $iconNameSpace . ":"; 13737748cd8SNickeau } 13837748cd8SNickeau $mediaPathId = $iconNameSpace . $iconNameAttribute . ".svg"; 13937748cd8SNickeau $mediaDokuPath = DokuPath::createMediaPathFromAbsolutePath($mediaPathId); 14037748cd8SNickeau 14137748cd8SNickeau // Bug: null file created when the stream could not get any byte 14237748cd8SNickeau // We delete them 143*c3437056SNickeau if (FileSystems::exists($mediaDokuPath)) { 144*c3437056SNickeau if (FileSystems::getSize($mediaDokuPath) == 0) { 145*c3437056SNickeau FileSystems::delete($mediaDokuPath); 14637748cd8SNickeau } 14737748cd8SNickeau } 14837748cd8SNickeau 149*c3437056SNickeau if (!FileSystems::exists($mediaDokuPath)) { 15037748cd8SNickeau 15137748cd8SNickeau /** 15237748cd8SNickeau * Download the icon 15337748cd8SNickeau */ 15437748cd8SNickeau 15537748cd8SNickeau // Create the target directory if it does not exist 15637748cd8SNickeau $iconDir = $mediaDokuPath->getParent(); 157*c3437056SNickeau if (!FileSystems::exists($iconDir)) { 158*c3437056SNickeau try { 159*c3437056SNickeau FileSystems::createDirectory($iconDir); 160*c3437056SNickeau } catch (ExceptionCombo $e) { 16137748cd8SNickeau LogUtility::msg("The icon directory ($iconDir) could not be created.", LogUtility::LVL_MSG_ERROR, self::NAME); 16237748cd8SNickeau return false; 16337748cd8SNickeau } 16437748cd8SNickeau } 16537748cd8SNickeau 16637748cd8SNickeau // Name parsing to extract the library name and icon name 16737748cd8SNickeau $sepPosition = strpos($iconNameAttribute, ":"); 168*c3437056SNickeau $library = PluginUtility::getConfValue(self::CONF_DEFAULT_ICON_LIBRARY, self::CONF_DEFAULT_ICON_LIBRARY_DEFAULT); 16937748cd8SNickeau $iconName = $iconNameAttribute; 17037748cd8SNickeau if ($sepPosition != false) { 17137748cd8SNickeau $library = substr($iconNameAttribute, 0, $sepPosition); 17237748cd8SNickeau $iconName = substr($iconNameAttribute, $sepPosition + 1); 17337748cd8SNickeau } 17437748cd8SNickeau 17537748cd8SNickeau // Get the qualified library name 176*c3437056SNickeau $acronymLibraries = self::getLibraries(); 17737748cd8SNickeau if (isset($acronymLibraries[$library])) { 17837748cd8SNickeau $library = $acronymLibraries[$library]; 17937748cd8SNickeau } 18037748cd8SNickeau 18137748cd8SNickeau // Get the url 18237748cd8SNickeau $iconLibraries = self::ICON_LIBRARY_URLS; 18337748cd8SNickeau if (!isset($iconLibraries[$library])) { 18437748cd8SNickeau LogUtility::msg("The icon library ($library) is unknown. The icon could not be downloaded.", LogUtility::LVL_MSG_ERROR, self::NAME); 18537748cd8SNickeau return false; 18637748cd8SNickeau } else { 18737748cd8SNickeau $iconBaseUrl = $iconLibraries[$library]; 18837748cd8SNickeau } 18937748cd8SNickeau 19037748cd8SNickeau // The url 19137748cd8SNickeau $downloadUrl = "$iconBaseUrl/$iconName.svg"; 19237748cd8SNickeau $filePointer = @fopen($downloadUrl, 'r'); 19337748cd8SNickeau if ($filePointer != false) { 19437748cd8SNickeau 195*c3437056SNickeau $numberOfByte = @file_put_contents($mediaDokuPath->toLocalPath()->toAbsolutePath()->toString(), $filePointer); 19637748cd8SNickeau if ($numberOfByte != false) { 19737748cd8SNickeau LogUtility::msg("The icon ($iconName) from the library ($library) was downloaded to ($mediaPathId)", LogUtility::LVL_MSG_INFO, self::NAME); 19837748cd8SNickeau } else { 19937748cd8SNickeau LogUtility::msg("Internal error: The icon ($iconName) from the library ($library) could no be written to ($mediaPathId)", LogUtility::LVL_MSG_ERROR, self::NAME); 20037748cd8SNickeau } 20137748cd8SNickeau 20237748cd8SNickeau } else { 20337748cd8SNickeau 20437748cd8SNickeau // (ie no icon file found at ($downloadUrl) 20537748cd8SNickeau $urlLibrary = self::ICON_LIBRARY_WEBSITE_URLS[$library]; 20637748cd8SNickeau LogUtility::msg("The library (<a href=\"$urlLibrary\">$library</a>) does not have a icon (<a href=\"$downloadUrl\">$iconName</a>).", LogUtility::LVL_MSG_ERROR, self::NAME); 20737748cd8SNickeau 20837748cd8SNickeau } 20937748cd8SNickeau 21037748cd8SNickeau } 21137748cd8SNickeau 21237748cd8SNickeau } 21337748cd8SNickeau 214*c3437056SNickeau if (FileSystems::exists($mediaDokuPath)) { 21537748cd8SNickeau 21637748cd8SNickeau 21737748cd8SNickeau /** 21837748cd8SNickeau * After optimization, the width and height of the svg are gone 21937748cd8SNickeau * but the icon type set them again 22037748cd8SNickeau * 22137748cd8SNickeau * The icon type is used to set: 22237748cd8SNickeau * * the default dimension 22337748cd8SNickeau * * color styling 22437748cd8SNickeau * * disable the responsive properties 22537748cd8SNickeau * 22637748cd8SNickeau */ 22737748cd8SNickeau $tagAttributes->addComponentAttributeValue("type", SvgDocument::ICON_TYPE); 22837748cd8SNickeau 22937748cd8SNickeau 230*c3437056SNickeau $svgImageLink = SvgImageLink::createMediaLinkFromId( 23137748cd8SNickeau $mediaDokuPath->getAbsolutePath(), 23237748cd8SNickeau null, 23337748cd8SNickeau $tagAttributes 23437748cd8SNickeau ); 23537748cd8SNickeau return $svgImageLink->renderMediaTag(); 23637748cd8SNickeau 23737748cd8SNickeau } else { 23837748cd8SNickeau 23937748cd8SNickeau return ""; 24037748cd8SNickeau 24137748cd8SNickeau } 24237748cd8SNickeau 24337748cd8SNickeau 24437748cd8SNickeau } 24537748cd8SNickeau 24637748cd8SNickeau /** 24737748cd8SNickeau * @param $iconName 24837748cd8SNickeau * @param $mediaFilePath 24937748cd8SNickeau * @deprecated Old code to download icon from the material design api 25037748cd8SNickeau */ 25137748cd8SNickeau public 25237748cd8SNickeau static function downloadIconFromMaterialDesignApi($iconName, $mediaFilePath) 25337748cd8SNickeau { 25437748cd8SNickeau // Try the official API 25537748cd8SNickeau // Read the icon meta of 25637748cd8SNickeau // Meta Json file got all icons 25737748cd8SNickeau // 25837748cd8SNickeau // * Available at: https://raw.githubusercontent.com/Templarian/MaterialDesign/master/meta.json 25937748cd8SNickeau // * See doc: https://github.com/Templarian/MaterialDesign-Site/blob/master/src/content/api.md) 26037748cd8SNickeau $arrayFormat = true; 26137748cd8SNickeau $iconMetaJson = json_decode(file_get_contents(__DIR__ . '/icon-meta.json'), $arrayFormat); 26237748cd8SNickeau $iconId = null; 26337748cd8SNickeau foreach ($iconMetaJson as $key => $value) { 26437748cd8SNickeau if ($value['name'] == $iconName) { 26537748cd8SNickeau $iconId = $value['id']; 26637748cd8SNickeau break; 26737748cd8SNickeau } 26837748cd8SNickeau } 26937748cd8SNickeau if ($iconId != null) { 27037748cd8SNickeau 27137748cd8SNickeau // Download 27237748cd8SNickeau // Call to the API 27337748cd8SNickeau // https://dev.materialdesignicons.com/contribute/site/api 27437748cd8SNickeau $downloadUrl = "https://materialdesignicons.com/api/download/icon/svg/$iconId"; 27537748cd8SNickeau $filePointer = file_put_contents($mediaFilePath, fopen($downloadUrl, 'r')); 27637748cd8SNickeau if ($filePointer == false) { 27737748cd8SNickeau LogUtility::msg("The file ($downloadUrl) could not be downloaded to ($mediaFilePath)", LogUtility::LVL_MSG_ERROR, self::NAME); 27837748cd8SNickeau } else { 27937748cd8SNickeau LogUtility::msg("The material design icon ($iconName) was downloaded to ($mediaFilePath)", LogUtility::LVL_MSG_INFO, self::NAME); 28037748cd8SNickeau } 28137748cd8SNickeau 28237748cd8SNickeau } 28337748cd8SNickeau 28437748cd8SNickeau } 28537748cd8SNickeau 286*c3437056SNickeau private static function getLibraries() 287*c3437056SNickeau { 288*c3437056SNickeau return array_merge(self::PUBLIC_LIBRARY_ACRONYM, self::DEPRECATED_LIBRARY_ACRONYM); 289*c3437056SNickeau } 290*c3437056SNickeau 29137748cd8SNickeau 29237748cd8SNickeau} 293