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