|
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) { |
|
|
|
|
|
|
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) { |
|
|
|
|
|
|
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) { |
|
|
|
|
|
|
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) |
|
|
|
|
|
|
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); |
|
|
|
|
|
|
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); |
|
|
|
|
|
|
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(); |
|
|
|
|
|
|
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
|
|
|
|