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