Passed
Push — master ( d99cea...339592 )
by Fabio
05:58
created

THtmlArea5::loadAvailableThemes()   A

Complexity

Conditions 6
Paths 5

Size

Total Lines 12
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 9
nc 5
nop 0
dl 0
loc 12
rs 9.2222
c 0
b 0
f 0
1
<?php
2
/**
3
 * THtmlArea5 class file.
4
 *
5
 * @author Fabio Bas <ctrlaltca[at]gmail[dot]com>
6
 * @link https://github.com/pradosoft/prado
7
 * @license https://github.com/pradosoft/prado/blob/master/LICENSE
8
 */
9
10
namespace Prado\Web\UI\WebControls;
11
12
use Prado\Exceptions\TInvalidOperationException;
13
use Prado\Prado;
14
use Prado\TApplicationMode;
15
use Prado\TPropertyValue;
16
use Prado\Web\Javascripts\TJavaScript;
17
18
/**
19
 * THtmlArea5 class
20
 *
21
 * THtmlArea5 wraps the visual editing functionalities provided by the
22
 * version 5 of TinyMCE project {@link http://tinymce.com/}. It has been
23
 * developed as a plug'n'play substitute for {@link THtmlArea}, that is
24
 * based on a previous iteration (version 3) of the same project.
25
 * Please note that both components can't be used together in the same page.
26
 *
27
 * THtmlArea displays a WYSIWYG text area on the Web page for user input
28
 * in the HTML format. The text displayed in the THtmlArea component is
29
 * specified or determined by using the <b>Text</b> property.
30
 *
31
 * To enable the visual editting on the client side, set the property
32
 * <b>EnableVisualEdit</b> to true (which is default value).
33
 * To set the size of the editor when the visual editting is enabled,
34
 * set the <b>Width</b> and <b>Height</b> properties instead of
35
 * <b>Columns</b> and <b>Rows</b> because the latter has no meaning
36
 * under the situation.
37
 *
38
 * The default editor gives only the basic tool bar. To change or add
39
 * additional tool bars, use the {@link setOptions Options} property to add additional
40
 * editor options with each options on a new line.
41
 * See http://www.tinymce.com/wiki.php/Configuration
42
 * for a list of options. The options can be change/added as shown in the
43
 * following example.
44
 * <code>
45
 * <com:THtmlArea5>
46
 *       <prop:Options>
47
 *         language : "de"
48
 *         plugins: "advlist anchor autolink autoresize autosave charmap code directionality emoticons fullscreen hr image importcss insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace tabfocus table template visualblocks visualchars wordcount"
49
 *         toolbar: "undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image     | print preview media",
50
 *         statusbar: false
51
 *      </prop:Options>
52
 * </com:THtmlArea5>
53
 * </code>
54
 *
55
 * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
56
 * @since 4.2
57
 */
58
class THtmlArea5 extends TTextBox
59
{
60
	/**
61
	 * @var array list of available language files
62
	 */
63
	private static $_langs;
64
65
	/**
66
	 * @var array list of available plugins
67
	 */
68
	private static $_plugins;
69
70
	/**
71
	 * @var array list of available themes
72
	 */
73
	private static $_themes;
74
75
	/**
76
	 * Constructor.
77
	 * Sets default width and height.
78
	 */
79
	public function __construct()
80
	{
81
		$this->setWidth('600px');
82
		$this->setHeight('250px');
83
		parent::__construct();
84
	}
85
86
	protected function loadAvailableLanguages()
87
	{
88
		if(self::$_langs === null) {
0 ignored issues
show
introduced by
The condition self::_langs === null is always false.
Loading history...
89
			self::$_langs = [];
90
			$path = Prado::getPathOfNameSpace('Vendor\\pradosoft\\tinymce-langs');
91
			$files = scandir($path);
92
			if($files !== false) {
93
				foreach ($files as $f) {
94
					if ($f === '.' || $f === '..' || strlen($f) < 4 || substr($f, -3) != '.js') {
95
						continue;
96
					}
97
					$filename = substr($f, 0, -3);
98
					self::$_langs[] = $filename;
99
				}
100
			}
101
		}
102
	}
103
104
	protected function loadAvailablePlugins()
105
	{
106
		if(self::$_plugins === null) {
0 ignored issues
show
introduced by
The condition self::_plugins === null is always false.
Loading history...
107
			self::$_plugins = [];
108
			$path = Prado::getPathOfNameSpace('Vendor\\bower-asset\\tinymce\\plugins');
109
			$files = scandir($path);
110
			if($files !== false) {
111
				foreach ($files as $f) {
112
					if ($f === '.' || $f === '..') {
113
						continue;
114
					}
115
					self::$_plugins[] = $f;
116
				}
117
			}
118
		}
119
	}
120
121
	protected function loadAvailableThemes()
122
	{
123
		if(self::$_themes === null) {
0 ignored issues
show
introduced by
The condition self::_themes === null is always false.
Loading history...
124
			self::$_themes = [];
125
			$path = Prado::getPathOfNameSpace('Vendor\\bower-asset\\tinymce\\themes');
126
			$files = scandir($path);
127
			if($files !== false) {
128
				foreach ($files as $f) {
129
					if ($f === '.' || $f === '..') {
130
						continue;
131
					}
132
					self::$_themes[] = $f;
133
				}
134
			}
135
		}
136
	}
137
138
	/**
139
	 * Overrides the parent implementation.
140
	 * TextMode for THtmlArea control is always 'MultiLine'
141
	 * @return string the behavior mode of the THtmlArea component.
142
	 */
143
	public function getTextMode()
144
	{
145
		return 'MultiLine';
146
	}
147
148
	/**
149
	 * Overrides the parent implementation.
150
	 * TextMode for THtmlArea is always 'MultiLine' and cannot be changed to others.
151
	 * @param string $value the text mode
152
	 */
153
	public function setTextMode($value)
154
	{
155
		throw new TInvalidOperationException("htmlarea_textmode_readonly");
156
	}
157
158
	/**
159
	 * @return bool whether change of the content should cause postback. Return false if EnableVisualEdit is true.
160
	 */
161
	public function getAutoPostBack()
162
	{
163
		return $this->getEnableVisualEdit() ? false : parent::getAutoPostBack();
164
	}
165
166
	/**
167
	 * @return bool whether to show WYSIWYG text editor. Defaults to true.
168
	 */
169
	public function getEnableVisualEdit()
170
	{
171
		return $this->getViewState('EnableVisualEdit', true);
172
	}
173
174
	/**
175
	 * Sets whether to show WYSIWYG text editor.
176
	 * @param bool $value whether to show WYSIWYG text editor
177
	 */
178
	public function setEnableVisualEdit($value)
179
	{
180
		$this->setViewState('EnableVisualEdit', TPropertyValue::ensureBoolean($value), true);
181
	}
182
183
	/**
184
	 * Gets the current culture.
185
	 * @return string current culture, e.g. de or it_IT.
186
	 */
187
	public function getCulture()
188
	{
189
		return $this->getViewState('Culture', '');
190
	}
191
192
	/**
193
	 * Sets the culture/language for the html area
194
	 * @param string $value a culture string, e.g. de or it_IT.
195
	 */
196
	public function setCulture($value)
197
	{
198
		$this->setViewState('Culture', $value, '');
199
	}
200
201
	/**
202
	 * Gets the list of options for the WYSIWYG (TinyMCE) editor
203
	 * @see http://www.tinymce.com/wiki.php/Configuration
204
	 * @return string options
205
	 */
206
	public function getOptions()
207
	{
208
		return $this->getViewState('Options', '');
209
	}
210
211
	/**
212
	 * Sets the list of options for the WYSIWYG (TinyMCE) editor
213
	 * @see http://www.tinymce.com/wiki.php/Configuration
214
	 * @param string $value options
215
	 */
216
	public function setOptions($value)
217
	{
218
		$this->setViewState('Options', $value, '');
219
	}
220
221
	/**
222
	 * @param string $value path to custom plugins to be copied.
223
	 */
224
	public function setCustomPluginPath($value)
225
	{
226
		$this->setViewState('CustomPluginPath', $value);
227
	}
228
229
	/**
230
	 * @return string path to custom plugins to be copied.
231
	 */
232
	public function getCustomPluginPath()
233
	{
234
		return $this->getViewState('CustomPluginPath');
235
	}
236
237
	/**
238
	 * Adds attribute name-value pairs to renderer.
239
	 * This method overrides the parent implementation by registering
240
	 * additional javacript code.
241
	 * @param \Prado\Web\UI\THtmlWriter $writer the writer used for the rendering purpose
242
	 */
243
	protected function addAttributesToRender($writer)
244
	{
245
		if ($this->getEnableVisualEdit() && $this->getEnabled(true)) {
246
			$writer->addAttribute('id', $this->getClientID());
247
			$this->registerEditorClientScript($writer);
248
		}
249
250
		parent::addAttributesToRender($writer);
251
	}
252
253
	/**
254
	 * Returns a list of available languages
255
	 * @return array list of available languages
256
	 */
257
	public function getAvailableLanguages()
258
	{
259
		$this->loadAvailableLanguages();
260
		return self::$_langs;
261
	}
262
263
	/**
264
	 * Returns a list of available plugins
265
	 * @return array list of available plugins
266
	 */
267
	public function getAvailablePlugins()
268
	{
269
		$this->loadAvailablePlugins();
270
		return self::$_plugins;
271
	}
272
273
	/**
274
	 * Returns a list of available themes
275
	 * @return array list of available themes
276
	 */
277
	public function getAvailableThemes()
278
	{
279
		$this->loadAvailableThemes();
280
		return self::$_themes;
281
	}
282
283
	protected function loadJavascriptLibrary()
284
	{
285
		$scripts = $this->getPage()->getClientScript();
286
		$scripts->registerPradoScript('htmlarea5');
287
		$this->copyLangs();
288
		$this->copyCustomPlugins();
289
	}
290
291
	/**
292
	 * Registers the editor javascript file and code to initialize the editor.
293
	 * @param mixed $writer
294
	 */
295
	protected function registerEditorClientScript($writer)
0 ignored issues
show
Unused Code introduced by
The parameter $writer is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

295
	protected function registerEditorClientScript(/** @scrutinizer ignore-unused */ $writer)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
296
	{
297
		$this->loadJavascriptLibrary();
298
		$scripts = $this->getPage()->getClientScript();
299
		$options = [
300
			'ID' => $this->getClientID(),
301
			'EditorOptions' => $this->getEditorOptions()
302
		];
303
304
		$options = TJavaScript::encode($options, true, true);
305
		$script = "new {$this->getClientClassName()}($options)";
306
		$scripts->registerEndScript('prado:THtmlArea5' . $this->getClientID(), $script);
307
	}
308
309
	protected function copyCustomPlugins()
310
	{
311
		if ($plugins = $this->getCustomPluginPath()) {
312
			$basepath = $this->getPage()->getClientScript()->getPradoScriptAssetPath('tinymce');
313
			$assets = $this->getApplication()->getAssetManager();
314
			$path = is_dir($plugins) ? $plugins : Prado::getPathOfNameSpace($plugins);
315
			$name = basename($path);
0 ignored issues
show
Bug introduced by
It seems like $path can also be of type null; however, parameter $path of basename() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

315
			$name = basename(/** @scrutinizer ignore-type */ $path);
Loading history...
316
			$dest = $basepath . '/plugins/' . $name;
317
			if (!is_dir($dest) || $this->getApplication()->getMode() !== TApplicationMode::Performance) {
318
				$assets->copyDirectory($path, $dest);
319
			}
320
		}
321
	}
322
323
	protected function copyLangs()
324
	{
325
		$basepath = $this->getPage()->getClientScript()->getPradoScriptAssetPath('tinymce');
326
		$assets = $this->getApplication()->getAssetManager();
327
		$path = Prado::getPathOfNameSpace('Vendor\\pradosoft\\tinymce-langs');
328
		$name = basename($path);
0 ignored issues
show
Bug introduced by
It seems like $path can also be of type null; however, parameter $path of basename() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

328
		$name = basename(/** @scrutinizer ignore-type */ $path);
Loading history...
Unused Code introduced by
The assignment to $name is dead and can be removed.
Loading history...
329
		$dest = $basepath . '/langs';
330
		if (!is_dir($dest) || $this->getApplication()->getMode() !== TApplicationMode::Performance) {
331
			$assets->copyDirectory($path, $dest);
332
		}
333
	}
334
335
	/**
336
	 * Default editor options gives basic tool bar only.
337
	 * @return array editor initialization options.
338
	 */
339
	protected function getEditorOptions()
340
	{
341
		$options['selector'] = '#' . $this->getClientID();
0 ignored issues
show
Comprehensibility Best Practice introduced by
$options was never initialized. Although not strictly required by PHP, it is generally a good practice to add $options = array(); before regardless.
Loading history...
342
		$options['language'] = $this->getLanguageSuffix($this->getCulture());
343
		$options['theme'] = 'silver';
344
		$options['width'] = $this->getWidth();
345
		$options['height'] = $this->getHeight();
346
		$options['resize'] = 'both';
347
		$options['menubar'] = false;
348
		if ($this->getReadOnly()) {
349
			$options['readonly'] = true;
350
			$options['toolbar'] = false;
351
			$options['menubar'] = false;
352
			$options['statusbar'] = false;
353
		}
354
355
		$options['extended_valid_elements'] = 'a[name|href|target|title|onclick],img[class|src|border=0|alt|title|hspace|vspace|width|height|align|onmouseover|onmouseout|name],hr[class|width|size|noshade],font[face|size|color|style],span[class|align|style]';
356
357
		$options = array_merge($options, $this->parseEditorOptions($this->getOptions()));
358
		return $options;
359
	}
360
361
	/**
362
	 * Parse additional options set in the Options property.
363
	 * @param mixed $string
364
	 * @return array additional custom options
365
	 */
366
	protected function parseEditorOptions($string)
367
	{
368
		$options = [];
369
		$substrings = preg_split('/,\s*\n|\n/', trim($string));
370
		foreach ($substrings as $bits) {
371
			$option = explode(":", $bits, 2);
372
373
			if (count($option) == 2) {
374
				$value = trim(trim($option[1]), "'\"");
375
				if (($s = strtolower($value)) === 'false') {
376
					$value = false;
377
				} elseif ($s === 'true') {
378
					$value = true;
379
				}
380
				$options[trim($option[0])] = $value;
381
			}
382
		}
383
		return $options;
384
	}
385
386
	/**
387
	 * @param mixed $culture
388
	 * @return string localized editor interface language extension.
389
	 */
390
	protected function getLanguageSuffix($culture)
391
	{
392
		$app = $this->getApplication()->getGlobalization();
393
		if (empty($culture) && ($app !== null)) {
394
			$culture = $app->getCulture();
395
		}
396
		$variants = [];
397
		if ($app !== null) {
398
			$variants = $app->getCultureVariants($culture);
399
		}
400
401
		$langs = $this->getAvailableLanguages();
402
		foreach ($variants as $variant) {
403
			if (in_array($variant, $langs)) {
404
				return $variant;
405
			}
406
		}
407
408
		return 'en';
409
	}
410
411
	/**
412
	 * Gets the name of the javascript class responsible for performing postback for this control.
413
	 * This method overrides the parent implementation.
414
	 * @return string the javascript class name
415
	 */
416
	protected function getClientClassName()
417
	{
418
		return 'Prado.WebUI.THtmlArea5';
419
	}
420
}
421