xref: /plugin/combo/ComboStrap/Icon.php (revision 1fa8c418ed5809db58049141be41b7738471dd32)
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