1<?php
2
3
4namespace ComboStrap;
5
6
7use ComboStrap\Meta\Api\Metadata;
8use ComboStrap\Meta\Api\MetadataText;
9use dokuwiki\Cache\Cache;
10
11class Lang extends MetadataText
12{
13
14    public const PROPERTY_NAME = "lang";
15
16
17    /**
18     * Process the lang attribute
19     * https://www.w3.org/International/questions/qa-html-language-declarations
20     * @param TagAttributes $attributes
21     *
22     * Language supported:
23     * http://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
24     *
25     * Common Locale Data Repository
26     * http://cldr.unicode.org/
27     * Data:
28     *   * http://www.unicode.org/Public/cldr/
29     *   * https://github.com/unicode-cldr/
30     *   * https://github.com/unicode-org/cldr-json
31     * The ''dir'' data is known as the ''characterOrder''
32     *
33     */
34    public static function processLangAttribute(&$attributes)
35    {
36
37
38        /**
39         * Adding the lang attribute
40         * if set
41         */
42        if ($attributes->hasComponentAttribute(self::PROPERTY_NAME)) {
43            $langValue = $attributes->getValueAndRemove(self::PROPERTY_NAME);
44            $attributes->addOutputAttributeValue("lang", $langValue);
45
46            $languageDataCache = new Cache("combo_" . $langValue, ".json");
47            $cacheDataUsable = $languageDataCache->useCache();
48            if (!$cacheDataUsable) {
49
50                // Language about the data
51                $downloadUrl = "https://raw.githubusercontent.com/unicode-org/cldr-json/master/cldr-json/cldr-misc-modern/main/$langValue/layout.json";
52
53                if (PluginUtility::isDevOrTest()) {
54                    // phpunit takes over and would catch and cache the error
55                    $filePointer = fopen($downloadUrl, 'r');
56                } else {
57                    $filePointer = @fopen($downloadUrl, 'r');
58                }
59                if ($filePointer != false) {
60
61                    $numberOfByte = @file_put_contents($languageDataCache->cache, $filePointer);
62                    if ($numberOfByte != false) {
63                        LogUtility::msg("The new language data ($langValue) was downloaded", LogUtility::LVL_MSG_INFO, self::PROPERTY_NAME);
64                        $cacheDataUsable = true;
65                    } else {
66                        LogUtility::msg("Internal error: The language data ($langValue) could no be written to ($languageDataCache->cache)", LogUtility::LVL_MSG_ERROR, self::PROPERTY_NAME);
67                    }
68
69                } else {
70
71                    // fopen(): Unable to find the wrapper "https" - did you forget to enable it when you configured PHP?
72                    $error_get_last = error_get_last();
73                    $message = $error_get_last['message'];
74                    LogUtility::msg("The data for the language ($langValue) could not be downloaded at (<a href=\"$downloadUrl\">$langValue</a>). Error: " . $message, LogUtility::LVL_MSG_ERROR, self::PROPERTY_NAME);
75
76                }
77            }
78
79            if ($cacheDataUsable) {
80                $jsonAsArray = true;
81                $languageData = json_decode(file_get_contents($languageDataCache->cache), $jsonAsArray);
82                if ($languageData == null) {
83                    LogUtility::msg("We could not read the data from the language ($langValue). No direction was set.", LogUtility::LVL_MSG_ERROR, self::PROPERTY_NAME);
84                    return;
85                }
86                $characterOrder = $languageData["main"][$langValue]["layout"]["orientation"]["characterOrder"];
87                if ($characterOrder == "right-to-left") {
88                    $attributes->addOutputAttributeValue("dir", "rtl");
89                } else {
90                    $attributes->addOutputAttributeValue("dir", "ltr");
91                }
92            } else {
93                LogUtility::msg("The language direction cannot be set because no language data was found for the language ($langValue)", LogUtility::LVL_MSG_WARNING, self::PROPERTY_NAME);
94            }
95
96        }
97
98    }
99
100    public static function createForMarkup(MarkupPath $markup): Lang
101    {
102        $lang = new Lang();
103        $lang->setResource($markup);
104        return $lang;
105    }
106
107
108    /**
109     * @throws ExceptionNotFound
110     */
111    public static function createFromRequestedMarkup(): Lang
112    {
113        return self::createForMarkup(MarkupPath::createFromRequestedPage());
114    }
115
116    /**
117     * Set the direction of the text
118     * The global lang direction (not yet inside the Lang class)
119     * @param string $string
120     * @return void
121     */
122    public static function setDirection(string $string)
123    {
124        global $lang;
125        $lang["direction"] = $string;
126    }
127
128    public static function createFromValue(string $langValue): Lang
129    {
130        $lang = new Lang();
131        $lang->value = $langValue;
132        return $lang;
133    }
134
135    static public function getTab(): ?string
136    {
137        return MetaManagerForm::TAB_LANGUAGE_VALUE;
138    }
139
140    /**
141     * @return string
142     */
143    public function getValueOrDefault(): string
144    {
145        try {
146            return $this->getValue();
147        } catch (ExceptionNotFound $e) {
148            return $this->getDefaultValue();
149        }
150    }
151
152
153    /**
154     * @throws ExceptionCompile
155     */
156    public function setFromStoreValue($value): Metadata
157    {
158
159        $this->validityCheck($value);
160        return parent::setFromStoreValue($value);
161
162    }
163
164    /**
165     * @param string|null $value
166     * @return Metadata
167     * @throws ExceptionCompile
168     */
169    public function setValue($value): Metadata
170    {
171        $this->validityCheck($value);
172        return parent::setValue($value);
173    }
174
175
176    static public function getDescription(): string
177    {
178        return "The language of the page";
179    }
180
181    static public function getLabel(): string
182    {
183        return "Language";
184    }
185
186    public static function getName(): string
187    {
188        return self::PROPERTY_NAME;
189    }
190
191    static public function getPersistenceType(): string
192    {
193        return Metadata::PERSISTENT_METADATA;
194    }
195
196    static public function isMutable(): bool
197    {
198        return true;
199    }
200
201    /**
202     * @return string
203     */
204    public function getDefaultValue(): string
205    {
206        return Site::getLang();
207    }
208
209    /**
210     * @throws ExceptionCompile
211     */
212    private function validityCheck($value)
213    {
214        if ($value === "" || $value === null) {
215            return;
216        }
217        if (!StringUtility::match($value, "^[a-zA-Z]{2}$")) {
218            throw new ExceptionCompile("The lang value ($value) for the page ($this) does not have two letters", $this->getCanonical());
219        }
220    }
221
222    static public function getCanonical(): string
223    {
224        return "lang";
225    }
226
227    public function getDirection()
228    {
229        /**
230         * TODO: should be base on the page value
231         * Search PHP and CLDR
232         * https://punic.github.io/
233         * https://www.php.net/manual/en/book.intl.php
234         *
235         * Example:
236         * https://github.com/salarmehr/cosmopolitan
237         * use Salarmehr\Cosmopolitan\Cosmo;
238         *
239         * echo Cosmo::create('fa')->direction(); // rlt
240         * echo Cosmo::create('en')->direction(); // ltr
241         */
242        return Site::getLangDirection();
243    }
244
245
246    static public function isOnForm(): bool
247    {
248        return true;
249    }
250
251}
252