115 public function __construct($htmlname, $content, $width =
'', $height = 200, $toolbarname =
'Basic', $notused =
'', $toolbarstartexpanded =
false, $uselocalbrowser = -1, $okforextendededitor =
true, $rows = 0, $cols =
'', $readonly = 0, $poscursor = array())
119 dol_syslog(get_class($this).
"::DolEditor htmlname=".$htmlname.
" width=".$width.
" height=".$height.
" toolbarname=".$toolbarname.
" uselocalbrowser=".$uselocalbrowser);
121 if ($uselocalbrowser) {
123 $uselocalbrowser =
getDolGlobalInt(
"WYSIWYG_ALLOW_UPLOAD_MEDIA_FILES");
127 $rows = round($height / 20);
130 $cols = ($width ? round($width / 6) : 80);
132 $shorttoolbarname = preg_replace(
'/_encoded$/',
'', $toolbarname);
135 $defaulteditor =
'ckeditor';
137 $this->uselocalbrowser = $uselocalbrowser;
138 $this->readonly = $readonly;
141 if ((!
isModEnabled(
'fckeditor') && $okforextendededitor !==
'ace') || empty($okforextendededitor)) {
142 $this->tool =
'textarea';
144 if ($okforextendededitor ===
'ace') {
148 if (empty(
$conf->use_javascript_ajax)) {
149 $this->tool =
'textarea';
152 if (isset($poscursor[
'find'])) {
154 $lines = explode(
"\n", $content);
155 $nblines = count($lines);
156 for ($i = 0 ; $i < $nblines ; $i++) {
157 if (preg_match(
'/'.$poscursor[
'find'].
'/', $lines[$i])) {
163 $poscursor[
'y'] = $posy;
168 if (in_array($this->tool, array(
'textarea',
'ckeditor',
'tinymce',
'ace'))) {
169 if (in_array($this->tool, array(
'ckeditor',
'tinymce')) && !
dol_textishtml($content)) {
172 $this->content = $content;
174 $this->htmlname = $htmlname;
175 $this->toolbarname = $shorttoolbarname;
176 $this->toolbarstartexpanded = $toolbarstartexpanded;
177 $this->rows = max(ROWS_3, $rows);
178 $this->cols = (preg_match(
'/%/', $cols) ? $cols : max(40, $cols));
179 $this->height = $height;
180 $this->width = $width;
181 $this->posx = empty($poscursor[
'x']) ? 0 : $poscursor[
'x'];
182 $this->posy = empty($poscursor[
'y']) ? 0 : $poscursor[
'y'];
203 public function Create($noprint = 0, $morejs =
'', $restrictContent =
true, $titlecontent =
'', $option =
'', $moreparam =
'', $morecss =
'')
206 global
$conf, $langs;
211 $extraAllowedContent =
'a[target]{text-decoration};';
212 $extraAllowedContent .=
'section[contenteditable,id];';
213 $extraAllowedContent .=
'table{border-spacing};';
214 $extraAllowedContent .=
'td{padding};';
215 $extraAllowedContent .=
'p{margin-left,margin-right,margin-top,margin-bottom,padding,line-height};';
216 $extraAllowedContent .=
'div{background-color,color,display,float,height,margin,margin-top,margin-bottom,padding,padding-left,padding-right,padding-top,padding-bottom,width,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-left-radius,border-bottom-right-radius,box-shadow}';
218 if (is_string($restrictContent)) {
219 $extraAllowedContent = $restrictContent;
221 if (isset(
$conf->global->FCKEDITOR_ALLOW_ANY_CONTENT)) {
228 $this->content = (string) $this->content;
230 if (in_array($this->tool, array(
'textarea',
'ckeditor',
'tinymce'))) {
236 $out .=
'<textarea id="'.$this->htmlname.
'" name="'.$this->htmlname.
'"';
237 $out .=
' rows="'.$this->rows.
'"';
238 if (preg_match(
'/%/', $this->cols)) {
239 $out .=
' style="margin-top: 5px; width: ';
240 if ($this->tool ==
'tinymce') {
243 $out .=
'calc('.$this->cols.
' - 20px)';
247 $out .=
' cols="'.$this->cols.
'"';
249 $out .=
' '.($moreparam ? $moreparam :
'');
250 $out .=
' class="flat '.dol_string_nohtmltag($this->toolbarname).
' '.$morecss.
'">';
251 $out .= htmlspecialchars($this->content);
252 $out .=
'</textarea>';
254 if ($this->tool ==
'ckeditor' && !empty(
$conf->use_javascript_ajax) &&
isModEnabled(
'fckeditor')) {
255 if (!defined(
'REQUIRE_CKEDITOR')) {
256 define(
'REQUIRE_CKEDITOR',
'1');
261 $pluginstodisable =
'elementspath,save,flash,div,anchor';
263 $pluginstodisable .=
',specialchar';
265 if (!empty(
$conf->dol_optimize_smallscreen)) {
266 $pluginstodisable .=
',scayt,wsc,find,undo';
269 $pluginstodisable .=
',wsc';
272 $pluginstodisable .=
',exportpdf';
275 $this->uselocalbrowser = 0;
277 $scaytautostartup =
'';
279 $scaytautostartup =
'scayt_autoStartup: true,';
280 $scaytautostartup .=
'scayt_sLang: \''.dol_escape_js($langs->getDefaultLang()).
'\',
';
282 $pluginstodisable .= ',scayt
';
285 $htmlencode_force = preg_match('/_encoded$/
', $this->toolbarname);
287 $out .= '<!-- Output ckeditor disallowAnyContent=
'.dol_escape_htmltag((string) $restrictContent).' toolbarname=
'.dol_escape_htmltag($this->toolbarname).' -->
'."\n";
288 //$out .= '<style>#cke_1_top { height: 34px !important; }</style>
';
289 $out .= '<script nonce=
"'.getNonce().'" type=
"text/javascript">
290 $(document).ready(
function () {
294 tmpeditor = CKEDITOR.replace(\
''.
dol_escape_js($this->htmlname).
'\',
297 customConfig: ckeditorConfig,
298 removePlugins: \
''.dol_escape_js($pluginstodisable).
'\',
300 readOnly:
'.($this->readonly ? 'true' : 'false').',
301 htmlEncodeOutput:
'.($htmlencode_force ? 'true' : 'false').',
302 allowedContent:
'.($restrictContent ? 'false' : 'true').',
303 extraAllowedContent: \
''.dol_escape_js($extraAllowedContent).
'\',
304 disallowedContent: \
'\',
305 fullPage:
'.($fullpage ? 'true' : 'false').',
306 toolbar: \
''.dol_escape_js($this->toolbarname).
'\',
307 toolbarStartupExpanded:
'.($this->toolbarstartexpanded ? 'true' : 'false').',
308 width:
'.($this->width ? '\
''.dol_escape_js($this->width).
'\'' :
'\'\
'').
',
310 skin: \
''.dol_escape_js($skin).
'\',
311 '.$scaytautostartup.'
312 language: \
''.dol_escape_js($langs->defaultlang).
'\',
313 textDirection: \
''.dol_escape_js($langs->trans(
"DIRECTION")).
'\',
315 instanceReady :
function(ev) {
316 console.log(\
'ckeditor '.
dol_escape_js($this->htmlname).
' instanceReady\');
318 /* If we found the attribute required on source div, we remove it (not compatible with ckeditor) */
319 /* Disabled, because attribute "required" should never be used on fields for doleditor */
320 /* jQuery(\''.
dol_escape_js($this->htmlname).
'\').attr(
"required",
false); */
323 this.dataProcessor.writer.setRules( \
'p\', {
325 breakBeforeOpen : true,
326 breakAfterOpen : false,
327 breakBeforeClose : false,
328 breakAfterClose : true
332 disableNativeSpellChecker: '.(
getDolGlobalString(
'CKEDITOR_NATIVE_SPELLCHECKER') ?
'false' :
'true');
334 if ($this->uselocalbrowser) {
338 $out .=
' filebrowserBrowseUrl : ckeditorFilebrowserBrowseUrl,';
339 $out .=
' filebrowserImageBrowseUrl : ckeditorFilebrowserImageBrowseUrl,';
341 $out .=
' filebrowserWindowWidth : \'900\',
342 filebrowserWindowHeight : \'500\',
343 filebrowserImageWindowWidth : \'900\',
344 filebrowserImageWindowHeight : \'500\'';
346 $out .=
' })'.$morejs;
350 $out .=
'</script>'.
"\n";
353 if ($this->tool ==
'tinymce' && !empty(
$conf->use_javascript_ajax) &&
isModEnabled(
'fckeditor')) {
354 if (!defined(
'REQUIRE_TINYMCE')) {
355 define(
'REQUIRE_TINYMCE',
'1');
358 $htmlencode_force = preg_match(
'/_encoded$/', $this->toolbarname);
359 $pickerenabled = $this->uselocalbrowser ?
'true' :
'false';
361 $out .=
'<!-- Output tinymce toolbarname='.dol_escape_htmltag($this->toolbarname).
' -->'.
"\n";
362 $out .=
'<script nonce="'.getNonce().
'" type="text/javascript">'.
"\n";
363 $out .=
'$(document).ready(function () {'.
"\n";
364 $out .=
' if (typeof tinymce === "undefined") {'.
"\n";
365 $out .=
' console.error("TinyMCE library not loaded. Check that .../includes/tinymce/tinymce/tinymce.min.js exists and FCKEDITOR_EDITORNAME=tinymce.");'.
"\n";
366 $out .=
' return;'.
"\n";
368 $out .=
' var toolbarName = \''.dol_escape_js($this->toolbarname).
'\';
'."\n";
369 $out .= ' var toolbars = (window.dolTinymceToolbars || {});
'."\n";
370 $out .= ' var toolbarStr = toolbars[toolbarName] || toolbars[
"dolibarr_details"] ||
"undo redo | bold italic | link | code";
'."\n";
371 $out .= ' var pluginsStr = (typeof window.dolTinymcePluginsFor ===
"function") ? window.dolTinymcePluginsFor(toolbarName) :
"advlist autolink lists link image charmap preview anchor searchreplace visualblocks code fullscreen table help wordcount";
'."\n";
372 if (!getDolGlobalString('FCKEDITOR_ENABLE_SPECIALCHAR
')) {
373 $out .= 'pluginsStr = pluginsStr.replace(\
' charmap\', \'\');'.
"\n";
375 $out .=
' var tinyLang = \''.dol_escape_js($langs->defaultlang).
'\';
'."\n";
376 $out .= ' var tinyConf = {
'."\n";
377 $out .= ' selector: \
'textarea#'.dol_escape_js($this->htmlname).
'\',
'."\n";
378 $out .= ' toolbar: toolbarStr,
'."\n";
379 $out .= ' plugins: pluginsStr,
'."\n";
380 $out .= ' menubar:
false,
'."\n";
381 if (!getDolGlobalString('WYSIWYG_TINYMCE_ENABLE_STATUS_BAR
')) {
382 $out .= ' statusbar:
false,
'."\n";
384 $out .= ' branding:
false,
'."\n";
385 $out .= ' promotion:
false,
'."\n";
386 $out .= ' license_key:
"gpl",
'."\n";
387 $out .= ' readonly:
'.($this->readonly ? 'true' : 'false').',
'."\n";
388 $out .= ' height:
'.((int) $this->height + 80).',
'."\n";
390 $out .= ' width: \
''.dol_escape_js($this->width).
'\',
'."\n";
392 $out .= ' directionality: \
''.dol_escape_js(strtolower($langs->trans(
"DIRECTION")) ===
'rtl' ?
'rtl' :
'ltr').
'\',
'."\n";
393 $out .= ' forced_root_block:
false,
'."\n"; // equivalent of CKEDITOR.ENTER_BR
394 $out .= ' convert_urls:
false,
'."\n";
395 $out .= ' relative_urls:
false,
'."\n";
396 $out .= ' entity_encoding:
"raw",
'."\n";
397 $out .= ' protect: [/<\?[\s\S]*?\?>/g],
'."\n"; // protect PHP tags like CKEditor did
398 $out .= ' paste_data_images:
true,
'."\n";
399 $fontlist = 'arial,tahoma,verdana,helvetica
';
400 if (getDolGlobalString('THEME_FONT_FAMILY
')) {
401 $fontlist = getDolGlobalString('THEME_FONT_FAMILY
').',
'.$fontlist;
404 $out .= ' content_style: \
'p { margin: 6px 0 6px 0; line-height: 1.4em; } .mce-content-body { font-size: 0.94em; margin: 10px; font-family: '.$fontlist.
' } \',';
405 $out .=
' toolbar_mode: "sliding",'.
"\n";
406 $out .=
' browser_spellcheck: '.(getDolGlobalString(
'CKEDITOR_NATIVE_SPELLCHECKER') ?
'true' :
'false').
','.
"\n";
407 $out .=
' language: (tinyLang && tinyLang.indexOf("en") === 0) ? "en" : tinyLang,'.
"\n";
408 $out .=
' htmlEncodeOutput: '.($htmlencode_force ?
'true' :
'false').
','.
"\n";
409 if ($pickerenabled ===
'true') {
410 $out .=
' file_picker_types: "file image media",'.
"\n";
411 $out .=
' file_picker_callback: function (cb, value, meta) {'.
"\n";
412 $out .=
' var url = (meta.filetype === "image") ? window.tinymceFilebrowserImageBrowseUrl : window.tinymceFilebrowserBrowseUrl;'.
"\n";
413 $out .=
' if (!url) { return; }'.
"\n";
414 $out .=
' var w = window.open(url, "tinymcePicker", "width=900,height=500");'.
"\n";
415 $out .=
' window.dolTinymcePickerCb = function (picked) { if (picked) { cb(picked); } if (w) { w.close(); } };'.
"\n";
418 $out .=
' setup: function (editor) {'.
"\n";
419 $out .=
' editor.on("init", function () {'.
"\n";
420 $out .=
' console.log(\'tinymce '.dol_escape_js($this->htmlname).
' instanceReady\');'.
"\n";
424 $out .=
' tinymce.init(tinyConf)'.$morejs.
';'.
"\n";
426 $out .=
'</script>'.
"\n";
432 if (preg_match(
'/^ace/', $this->tool)) {
439 $out .=
'<div class="aceeditorstatusbar" id="statusBar'.$this->htmlname.
'">'.$titlecontent;
440 $out .=
' - <span id="morelines" class="right classlink cursorpointer morelines'.$this->htmlname.
'">'.
dol_escape_htmltag($langs->trans(
"ShowMoreLines")).
'</span> ';
442 $out .=
'<script nonce="'.getNonce().
'" type="text/javascript">'.
"\n";
443 $out .=
'jQuery(document).ready(function() {'.
"\n";
444 $out .=
' var aceEditor = window.ace.edit("'.dol_escape_all($this->htmlname).
'aceeditorid");
445 aceEditor.moveCursorTo('.($this->posy + 1).
','.$this->posx.
');
446 aceEditor.gotoLine('.($this->posy + 1).
','.$this->posx.
');
447 var StatusBar = window.ace.require("ace/ext/statusbar").StatusBar; // Init status bar. Need lib ext-statusbar
448 var statusBar = new StatusBar(aceEditor, document.getElementById("statusBar'.
dol_escape_all($this->htmlname).
'")); // Init status bar. Need lib ext-statusbar
450 var oldNbOfLines = 0;
451 jQuery(".morelines'.
dol_escape_all($this->htmlname).
'").click(function() {
452 var aceEditorClicked = window.ace.edit("'.$this->htmlname.
'aceeditorid");
453 currentline = aceEditorClicked.getOption("maxLines");
454 if (oldNbOfLines == 0)
456 oldNbOfLines = currentline;
458 console.log("We click on more lines, oldNbOfLines is "+oldNbOfLines+", we have currently "+currentline);
459 if (currentline < 500)
461 aceEditorClicked.setOptions({ maxLines: 500 });
465 aceEditorClicked.setOptions({ maxLines: oldNbOfLines });
469 $out .=
'</script>'.
"\n";
472 $out .=
'<pre id="'.$this->htmlname.
'aceeditorid" style="'.($this->width ?
'width: '.$this->width.
'px; ' :
'');
473 $out .= ($this->height ?
' height: '.$this->height.
'px; ' :
'');
476 $out .= htmlspecialchars($this->content);
478 $out .=
'<input type="hidden" id="'.$this->htmlname.
'_x" name="'.$this->htmlname.
'_x">';
479 $out .=
'<input type="hidden" id="'.$this->htmlname.
'_y" name="'.$this->htmlname.
'_y">';
480 $out .=
'<textarea id="'.$this->htmlname.
'" name="'.$this->htmlname.
'" style="width:0px; height: 0px; display: none;">';
481 $out .= htmlspecialchars($this->content);
482 $out .=
'</textarea>';
484 $out .=
'<script nonce="'.getNonce().
'" type="text/javascript">'.
"\n";
485 $out .=
'var aceEditor = window.ace.edit("'.$this->htmlname.
'aceeditorid");
487 aceEditor.session.setMode("ace/mode/'.$format.
'");
488 aceEditor.setReadOnly('.($this->readonly ?
'true' :
'false').
');
489 aceEditor.setOptions({
490 enableBasicAutocompletion: true, // the editor completes the statement when you hit Ctrl + Space. Need lib ext-language_tools.js
491 enableLiveAutocompletion: false, // the editor completes the statement while you are typing. Need lib ext-language_tools.js
492 //enableSnippets: true, // ???
493 showPrintMargin: false, // hides the vertical limiting strip
495 maxLines: '.(empty($this->height) ?
'34' : (round(($this->height - 60) / 19))).
', // we remove 60 for the ace toolbar + bottom status line. 19 seems the height in px required for 1 line.
496 fontSize: "110%" // ensures that the editor fits in the environment
499 // defines the style of the editor
500 aceEditor.setTheme("ace/theme/chrome");
502 // hides line numbers (widens the area occupied by error and warning messages)
503 //aceEditor.renderer.setOption("showLineNumbers", false);
504 // ensures proper autocomplete, validation and highlighting of JavaScript code
505 //aceEditor.getSession().setMode("ace/mode/javascript_expression");'.
"\n";
508 if ($this->htmlname ==
'PAGE_CONTENT') {
510 // Add custom function in the autocompletion
511 var customCompleter = {
512 getCompletions: function(editor, session, pos, prefix, callback) {
514 { caption: \'dol_escape_all\', value: \'dol_escape_all(string)\', meta: \'custom\' },
515 { caption: \'dol_escape_js\', value: \'dol_escape_js(string)\', meta: \'custom\' },
516 { caption: \'includeContainer\', value: \'includeContainer(alias_of_container_to_include)\', meta: \'custom\' },
517 { caption: \'redirectToContainer\', value: \'redirectToContainer(alias_of_container_to_redirect_to)\', meta: \'custom\' },
518 { caption: \'getImageFromHtmlContent\', value: \'getImageFromHtmlContent(websitepage->htmlcontent)\', meta: \'custom\' },
520 callback(null, wordList.map(function(word) {
522 caption: word.caption,
529 aceEditor.completers = [customCompleter];
533 $out .=
'jQuery(document).ready(function() {';
534 $out .=
' jQuery(".buttonforacesave").click(function() {
535 console.log(\'We click on button (with class .buttonforacesave) that must fill ace fields for component '.dol_escape_js($this->htmlname).
'\');
536 var aceEditor = window.ace.edit(\
''.
dol_escape_js($this->htmlname).
'aceeditorid\');
538 var cursorPos = aceEditor.getCursorPosition();
539 //console.log(cursorPos);
541 jQuery(\'#'.
dol_escape_js($this->htmlname).
'_x\').val(cursorPos.column);
542 jQuery(\'#'.
dol_escape_js($this->htmlname).
'_y\').val(cursorPos.row);
544 //console.log(aceEditor.getSession().getValue());
545 // Inject content of editor into the original HTML field.
546 jQuery(\'#'.
dol_escape_js($this->htmlname).
'\').val(aceEditor.getSession().getValue());
551 console.log(
"Failed to retrieve js object ACE from its name");
556 $out .= '</script>
'."\n";
560 $out .= 'Error, unknown value
for tool
'.$this->tool.' in
DolEditor Create function.
';