Unify the terms 'Staff' and 'Team'
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / system / bbcode / BBCodeHandler.class.php
... / ...
CommitLineData
1<?php
2
3namespace wcf\system\bbcode;
4
5use wcf\data\bbcode\BBCode;
6use wcf\data\bbcode\BBCodeCache;
7use wcf\system\application\ApplicationHandler;
8use wcf\system\package\license\LicenseApi;
9use wcf\system\SingletonFactory;
10use wcf\system\WCF;
11use wcf\util\ArrayUtil;
12use wcf\util\JSON;
13use wcf\util\StringUtil;
14
15/**
16 * Handles BBCodes displayed as buttons within the WYSIWYG editor.
17 *
18 * @author Alexander Ebert
19 * @copyright 2001-2019 WoltLab GmbH
20 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
21 */
22class BBCodeHandler extends SingletonFactory
23{
24 /**
25 * list of BBCodes displayed as buttons
26 * @var BBCode[]
27 */
28 protected $buttonBBCodes = [];
29
30 /**
31 * list of BBCodes disallowed for usage
32 * @var BBCode[]
33 */
34 protected $disallowedBBCodes = [];
35
36 /**
37 * list of BBCodes which contain raw code (disabled BBCode parsing)
38 * @var BBCode[]
39 */
40 protected $sourceBBCodes;
41
42 /**
43 * meta information about highlighters
44 * @var mixed[]
45 */
46 protected $highlighterMeta;
47
48 /**
49 * @inheritDoc
50 */
51 protected function init()
52 {
53 foreach (BBCodeCache::getInstance()->getBBCodes() as $bbcode) {
54 if ($bbcode->showButton) {
55 $this->buttonBBCodes[] = $bbcode;
56 }
57 }
58 }
59
60 /**
61 * Returns true if the BBCode with the given tag is available in the WYSIWYG editor.
62 */
63 public function isAvailableBBCode(string $bbCodeTag, bool $overrideFormattingRemoval = false): bool
64 {
65 if ($overrideFormattingRemoval === false) {
66 if ($bbCodeTag === "color" && \FORMATTING_REMOVE_COLOR) {
67 return false;
68 }
69
70 if ($bbCodeTag === "font" && \FORMATTING_REMOVE_FONT) {
71 return false;
72 }
73
74 if ($bbCodeTag === "size" && \FORMATTING_REMOVE_SIZE) {
75 return false;
76 }
77 }
78
79 return !\in_array($bbCodeTag, $this->disallowedBBCodes);
80 }
81
82 /**
83 * Returns all bbcodes.
84 *
85 * @return BBCode[]
86 */
87 public function getBBCodes()
88 {
89 return BBCodeCache::getInstance()->getBBCodes();
90 }
91
92 /**
93 * Returns a list of BBCodes displayed as buttons.
94 *
95 * @param bool $excludeCoreBBCodes do not return bbcodes that are available by default
96 * @return BBCode[]
97 */
98 public function getButtonBBCodes($excludeCoreBBCodes = false)
99 {
100 $buttons = [];
101 $coreBBCodes = [
102 'align',
103 'b',
104 'code',
105 'color',
106 'html',
107 'i',
108 'img',
109 'list',
110 's',
111 'size',
112 'sub',
113 'sup',
114 'quote',
115 'spoiler',
116 'table',
117 'tt',
118 'u',
119 'url',
120 ];
121 foreach ($this->buttonBBCodes as $bbcode) {
122 if ($excludeCoreBBCodes && \in_array($bbcode->bbcodeTag, $coreBBCodes)) {
123 continue;
124 }
125
126 if ($this->isAvailableBBCode($bbcode->bbcodeTag)) {
127 $buttons[] = $bbcode;
128 }
129 }
130
131 return $buttons;
132 }
133
134 /**
135 * Sets the disallowed BBCodes.
136 *
137 * @param string[] $bbCodes
138 */
139 public function setDisallowedBBCodes(array $bbCodes)
140 {
141 $this->disallowedBBCodes = $bbCodes;
142 }
143
144 /**
145 * Returns a list of BBCodes which contain raw code (disabled BBCode parsing)
146 *
147 * @return BBCode[]
148 * @deprecated 3.1 - This method is no longer supported.
149 */
150 public function getSourceBBCodes()
151 {
152 return [];
153 }
154
155 /**
156 * Returns metadata about the highlighters.
157 *
158 * @return string[][]
159 */
160 public function getHighlighterMeta()
161 {
162 if ($this->highlighterMeta === null) {
163 $this->highlighterMeta = JSON::decode(\preg_replace(
164 '/.*\/\*!START\*\/\s*const\s*metadata\s*=\s*(.*)\s*;\s*\/\*!END\*\/.*/s',
165 '\\1',
166 \file_get_contents(WCF_DIR . '/js/WoltLabSuite/Core/prism-meta.js')
167 ));
168 }
169
170 return $this->highlighterMeta;
171 }
172
173 /**
174 * Returns a list of known highlighters.
175 *
176 * @return string[]
177 */
178 public function getHighlighters()
179 {
180 return \array_keys($this->getHighlighterMeta());
181 }
182
183 /**
184 * Returns the list of languages that are available for selection in the
185 * UI of CKEditor’s code block.
186 *
187 * @return list<string>
188 * @since 6.0
189 */
190 public function getCodeBlockLanguages(): array
191 {
192 return \explode("\n", StringUtil::unifyNewlines(\MESSAGE_PUBLIC_HIGHLIGHTERS));
193 }
194
195 /**
196 * Returns a list of hostnames that are permitted as image sources.
197 *
198 * @return string[]
199 * @since 5.2
200 */
201 public function getImageExternalSourceWhitelist()
202 {
203 $hosts = [];
204 // Hide these hosts unless external sources are actually denied.
205 if (!IMAGE_ALLOW_EXTERNAL_SOURCE) {
206 $hosts = ArrayUtil::trim(\explode(
207 "\n",
208 \sprintf(
209 "%s\n%s",
210 \IMAGE_EXTERNAL_SOURCE_WHITELIST,
211 \INTERNAL_HOSTNAMES
212 )
213 ));
214 }
215
216 $hosts[] = ApplicationHandler::getInstance()->getDomainName();
217
218 return \array_unique($hosts);
219 }
220
221 /**
222 * Exports a require.js requirement for the localization of the editor based
223 * on the current locale. Returns an empty string when there is no available
224 * localization or the locale equals the bundled value 'en'.
225 *
226 * @since 6.0
227 */
228 public function getEditorLocalization(): string
229 {
230 $availableTranslations = [
231 'af',
232 'ar',
233 'ast',
234 'az',
235 'bg',
236 'bn',
237 'bs',
238 'ca',
239 'cs',
240 'da',
241 'de-ch',
242 'de',
243 'el',
244 'en-au',
245 'en-gb',
246 'eo',
247 'es-co',
248 'es',
249 'et',
250 'eu',
251 'fa',
252 'fi',
253 'fr',
254 'gl',
255 'gu',
256 'he',
257 'hi',
258 'hr',
259 'hu',
260 'id',
261 'it',
262 'ja',
263 'jv',
264 'kk',
265 'km',
266 'kn',
267 'ko',
268 'ku',
269 'lt',
270 'lv',
271 'ms',
272 'nb',
273 'ne',
274 'nl',
275 'no',
276 'oc',
277 'pl',
278 'pt-br',
279 'pt',
280 'ro',
281 'ru',
282 'si',
283 'sk',
284 'sl',
285 'sq',
286 'sr-latn',
287 'sr',
288 'sv',
289 'th',
290 'tk',
291 'tr',
292 'tt',
293 'ug',
294 'uk',
295 'ur',
296 'uz',
297 'vi',
298 'zh-cn',
299 'zh',
300 ];
301
302 $locale = \strtolower(WCF::getLanguage()->getBcp47());
303 if (\in_array($locale, $availableTranslations, true)) {
304 return \sprintf(
305 '"ckeditor5-translation/%s",',
306 $locale
307 );
308 }
309
310 // Some languages offer both specialized variants for certain locales
311 // but also provide a "generic" variant. For example, "en-gb" and "en".
312 [$languageCode] = \explode('-', $locale, 2);
313 if (\in_array($languageCode, $availableTranslations, true)) {
314 return \sprintf(
315 '"ckeditor5-translation/%s",',
316 $languageCode
317 );
318 }
319
320 // The default locale "en" is part of the generated bundle, we must not
321 // yield any module if this locale is (implicitly) requested.
322 return "";
323 }
324
325 /**
326 * @since 6.0
327 */
328 public function getCkeditorLicenseKey(): string
329 {
330 $licenseApi = new LicenseApi();
331 $licenseData = $licenseApi->readFromFile();
332
333 if ($licenseData === null) {
334 return '';
335 }
336
337 return $licenseData->license['ckeditorLicenseKey'] ?? '';
338 }
339}