1<?php 2/** 3 * Copyright (c) 2020. 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 15require_once(__DIR__ . '/PluginUtility.php'); 16 17 18/** 19 * Class IconUtility 20 * @package ComboStrap 21 * @see https://combostrap.com/icon 22 * 23 * 24 * Material design does not have a repository structure where we can extract the location 25 * from the name 26 * https://material.io/resources/icons https://google.github.io/material-design-icons/ 27 * 28 * Injection via javascript to avoid problem with the php svgsimple library 29 * https://www.npmjs.com/package/svg-injector 30 */ 31class Icon 32{ 33 const CONF_ICONS_MEDIA_NAMESPACE = "icons_namespace"; 34 const CONF_ICONS_MEDIA_NAMESPACE_DEFAULT = ":" . PluginUtility::COMBOSTRAP_NAMESPACE_NAME . ":icons"; 35 // Canonical name 36 const NAME = "icon"; 37 38 39 const ICON_LIBRARY_URLS = array( 40 self::BOOTSTRAP => "https://raw.githubusercontent.com/twbs/icons/main/icons", 41 self::MATERIAL_DESIGN => "https://raw.githubusercontent.com/Templarian/MaterialDesign/master/svg", 42 self::FEATHER => "https://raw.githubusercontent.com/feathericons/feather/master/icons" 43 ); 44 45 const ICON_LIBRARY_WEBSITE_URLS = array( 46 self::BOOTSTRAP => "https://icons.getbootstrap.com/", 47 self::MATERIAL_DESIGN => "https://materialdesignicons.com/", 48 self::FEATHER => "https://feathericons.com/" 49 ); 50 51 const CONF_DEFAULT_ICON_LIBRARY = "defaultIconLibrary"; 52 const LIBRARY_ACRONYM = array( 53 "bs" => self::BOOTSTRAP, 54 "md" => self::MATERIAL_DESIGN, 55 "fe" => self::FEATHER 56 ); 57 const FEATHER = "feather"; 58 const BOOTSTRAP = "bootstrap"; 59 const MATERIAL_DESIGN = "material-design"; 60 61 62 /** 63 * The function used to render an icon 64 * @param TagAttributes $tagAttributes - the icon attributes 65 * @return bool|mixed - false if any error or the HTML 66 */ 67 static public function renderIconByAttributes($tagAttributes) 68 { 69 70 71 $name = "name"; 72 if (!$tagAttributes->hasComponentAttribute($name)) { 73 LogUtility::msg("The attributes should have a name. It's mandatory for an icon.", LogUtility::LVL_MSG_ERROR, self::NAME); 74 return false; 75 } 76 77 /** 78 * The Name 79 */ 80 $iconNameAttribute = $tagAttributes->getValue($name); 81 82 /** 83 * If the name have an extension, it's a file from the media directory 84 * Otherwise, it's an icon from a library 85 */ 86 $mediaDokuPath = DokuPath::createMediaPathFromId($iconNameAttribute); 87 if (!empty($mediaDokuPath->getExtension())) { 88 89 // loop through candidates until a match was found: 90 // May be an icon from the templates 91 if (!$mediaDokuPath->exists()) { 92 93 // Trying to see if it's not in the template images directory 94 $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."; 95 $message .= "<BR> Media File Library tested: $mediaDokuPath"; 96 LogUtility::msg($message, LogUtility::LVL_MSG_ERROR, self::NAME); 97 return false; 98 99 } 100 101 } else { 102 103 // It may be a icon already downloaded 104 $iconNameSpace = ConfUtility::getConf(self::CONF_ICONS_MEDIA_NAMESPACE); 105 if (substr($iconNameSpace, 0, 1) != DokuPath::PATH_SEPARATOR) { 106 $iconNameSpace = DokuPath::PATH_SEPARATOR . $iconNameSpace; 107 } 108 if (substr($iconNameSpace, -1) != DokuPath::PATH_SEPARATOR) { 109 $iconNameSpace = $iconNameSpace . ":"; 110 } 111 $mediaPathId = $iconNameSpace . $iconNameAttribute . ".svg"; 112 $mediaDokuPath = DokuPath::createMediaPathFromAbsolutePath($mediaPathId); 113 114 // Bug: null file created when the stream could not get any byte 115 // We delete them 116 if ($mediaDokuPath->exists()) { 117 if ($mediaDokuPath->getSize() == 0) { 118 $mediaDokuPath->remove(); 119 } 120 } 121 122 if (!$mediaDokuPath->exists()) { 123 124 /** 125 * Download the icon 126 */ 127 128 // Create the target directory if it does not exist 129 $iconDir = $mediaDokuPath->getParent(); 130 if (!$iconDir->exists()) { 131 $filePointer = $iconDir->createAsDirectory(); 132 if ($filePointer == false) { 133 LogUtility::msg("The icon directory ($iconDir) could not be created.", LogUtility::LVL_MSG_ERROR, self::NAME); 134 return false; 135 } 136 } 137 138 // Name parsing to extract the library name and icon name 139 $sepPosition = strpos($iconNameAttribute, ":"); 140 $library = PluginUtility::getConfValue(self::CONF_DEFAULT_ICON_LIBRARY); 141 $iconName = $iconNameAttribute; 142 if ($sepPosition != false) { 143 $library = substr($iconNameAttribute, 0, $sepPosition); 144 $iconName = substr($iconNameAttribute, $sepPosition + 1); 145 } 146 147 // Get the qualified library name 148 $acronymLibraries = self::LIBRARY_ACRONYM; 149 if (isset($acronymLibraries[$library])) { 150 $library = $acronymLibraries[$library]; 151 } 152 153 // Get the url 154 $iconLibraries = self::ICON_LIBRARY_URLS; 155 if (!isset($iconLibraries[$library])) { 156 LogUtility::msg("The icon library ($library) is unknown. The icon could not be downloaded.", LogUtility::LVL_MSG_ERROR, self::NAME); 157 return false; 158 } else { 159 $iconBaseUrl = $iconLibraries[$library]; 160 } 161 162 // The url 163 $downloadUrl = "$iconBaseUrl/$iconName.svg"; 164 $filePointer = @fopen($downloadUrl, 'r'); 165 if ($filePointer != false) { 166 167 $numberOfByte = @file_put_contents($mediaDokuPath->getFileSystemPath(), $filePointer); 168 if ($numberOfByte != false) { 169 LogUtility::msg("The icon ($iconName) from the library ($library) was downloaded to ($mediaPathId)", LogUtility::LVL_MSG_INFO, self::NAME); 170 } else { 171 LogUtility::msg("Internal error: The icon ($iconName) from the library ($library) could no be written to ($mediaPathId)", LogUtility::LVL_MSG_ERROR, self::NAME); 172 } 173 174 } else { 175 176 // (ie no icon file found at ($downloadUrl) 177 $urlLibrary = self::ICON_LIBRARY_WEBSITE_URLS[$library]; 178 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); 179 180 } 181 182 } 183 184 } 185 186 if ($mediaDokuPath->exists()) { 187 188 189 /** 190 * After optimization, the width and height of the svg are gone 191 * but the icon type set them again 192 * 193 * The icon type is used to set: 194 * * the default dimension 195 * * color styling 196 * * disable the responsive properties 197 * 198 */ 199 $tagAttributes->addComponentAttributeValue("type", SvgDocument::ICON_TYPE); 200 201 202 $svgImageLink = SvgImageLink::createMediaLinkFromNonQualifiedPath( 203 $mediaDokuPath->getAbsolutePath(), 204 null, 205 $tagAttributes 206 ); 207 return $svgImageLink->renderMediaTag(); 208 209 } else { 210 211 return ""; 212 213 } 214 215 216 } 217 218 /** 219 * @param $iconName 220 * @param $mediaFilePath 221 * @deprecated Old code to download icon from the material design api 222 */ 223 public 224 static function downloadIconFromMaterialDesignApi($iconName, $mediaFilePath) 225 { 226 // Try the official API 227 // Read the icon meta of 228 // Meta Json file got all icons 229 // 230 // * Available at: https://raw.githubusercontent.com/Templarian/MaterialDesign/master/meta.json 231 // * See doc: https://github.com/Templarian/MaterialDesign-Site/blob/master/src/content/api.md) 232 $arrayFormat = true; 233 $iconMetaJson = json_decode(file_get_contents(__DIR__ . '/icon-meta.json'), $arrayFormat); 234 $iconId = null; 235 foreach ($iconMetaJson as $key => $value) { 236 if ($value['name'] == $iconName) { 237 $iconId = $value['id']; 238 break; 239 } 240 } 241 if ($iconId != null) { 242 243 // Download 244 // Call to the API 245 // https://dev.materialdesignicons.com/contribute/site/api 246 $downloadUrl = "https://materialdesignicons.com/api/download/icon/svg/$iconId"; 247 $filePointer = file_put_contents($mediaFilePath, fopen($downloadUrl, 'r')); 248 if ($filePointer == false) { 249 LogUtility::msg("The file ($downloadUrl) could not be downloaded to ($mediaFilePath)", LogUtility::LVL_MSG_ERROR, self::NAME); 250 } else { 251 LogUtility::msg("The material design icon ($iconName) was downloaded to ($mediaFilePath)", LogUtility::LVL_MSG_INFO, self::NAME); 252 } 253 254 } 255 256 } 257 258 259} 260