dolibarr  17.0.3
doleditor.class.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2006-2008 Laurent Destailleur <eldy@users.sourceforge.net>
3  * Copyright (C) 2021 GaĆ«tan MAISON <gm@ilad.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <https://www.gnu.org/licenses/>.
17  * or see https://www.gnu.org/
18  */
19 
30 class DolEditor
31 {
32  public $tool; // Store the selected tool
33 
34  // If using fckeditor
35  public $editor;
36 
37  // If not using fckeditor
38  public $content;
39  public $htmlname;
40  public $toolbarname;
41  public $toolbarstartexpanded;
42  public $rows;
43  public $cols;
44  public $height;
45  public $width;
46  public $readonly;
47  public $posx;
48  public $posy;
49 
50 
71  public function __construct($htmlname, $content, $width = '', $height = 200, $toolbarname = 'Basic', $toolbarlocation = 'In', $toolbarstartexpanded = false, $uselocalbrowser = 1, $okforextendededitor = true, $rows = 0, $cols = 0, $readonly = 0, $poscursor = array())
72  {
73  global $conf, $langs;
74 
75  dol_syslog(get_class($this)."::DolEditor htmlname=".$htmlname." width=".$width." height=".$height." toolbarname=".$toolbarname);
76 
77  if (!$rows) {
78  $rows = round($height / 20);
79  }
80  if (!$cols) {
81  $cols = ($width ?round($width / 6) : 80);
82  }
83  $shorttoolbarname = preg_replace('/_encoded$/', '', $toolbarname);
84 
85  // Name of extended editor to use (FCKEDITOR_EDITORNAME can be 'ckeditor' or 'fckeditor')
86  $defaulteditor = 'ckeditor';
87  $this->tool = empty($conf->global->FCKEDITOR_EDITORNAME) ? $defaulteditor : $conf->global->FCKEDITOR_EDITORNAME;
88  $this->uselocalbrowser = $uselocalbrowser;
89  $this->readonly = $readonly;
90 
91  // Check if extended editor is ok. If not we force textarea
92  if ((empty($conf->fckeditor->enabled) && $okforextendededitor != 'ace') || empty($okforextendededitor)) {
93  $this->tool = 'textarea';
94  }
95  if ($okforextendededitor === 'ace') {
96  $this->tool = 'ace';
97  }
98  //if ($conf->dol_use_jmobile) $this->tool = 'textarea'; // ckeditor and ace seems ok with mobile
99 
100  // Define some properties
101  if (in_array($this->tool, array('textarea', 'ckeditor', 'ace'))) {
102  if ($this->tool == 'ckeditor' && !dol_textishtml($content)) { // We force content to be into HTML if we are using an advanced editor if content is not HTML.
103  $this->content = dol_nl2br($content);
104  } else {
105  $this->content = $content;
106  }
107  $this->htmlname = $htmlname;
108  $this->toolbarname = $shorttoolbarname;
109  $this->toolbarstartexpanded = $toolbarstartexpanded;
110  $this->rows = max(ROWS_3, $rows);
111  $this->cols = (preg_match('/%/', $cols) ? $cols : max(40, $cols)); // If $cols is a percent, we keep it, otherwise, we take max
112  $this->height = $height;
113  $this->width = $width;
114  $this->posx = empty($poscursor['x']) ? 0 : $poscursor['x'];
115  $this->posy = empty($poscursor['y']) ? 0 : $poscursor['y'];
116  }
117  }
118 
119  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
133  public function Create($noprint = 0, $morejs = '', $disallowAnyContent = true, $titlecontent = '', $option = '', $moreparam = '', $morecss = '')
134  {
135  // phpcs:enable
136  global $conf, $langs;
137 
138  $fullpage = false;
139  if (isset($conf->global->FCKEDITOR_ALLOW_ANY_CONTENT)) {
140  $disallowAnyContent = empty($conf->global->FCKEDITOR_ALLOW_ANY_CONTENT); // Only predefined list of html tags are allowed or all
141  }
142 
143  $found = 0;
144  $out = '';
145 
146  if (in_array($this->tool, array('textarea', 'ckeditor'))) {
147  $found = 1;
148  //$out.= '<textarea id="'.$this->htmlname.'" name="'.$this->htmlname.'" '.($this->readonly?' disabled':'').' rows="'.$this->rows.'"'.(preg_match('/%/',$this->cols)?' style="margin-top: 5px; width: '.$this->cols.'"':' cols="'.$this->cols.'"').' class="flat">';
149  // TODO We do not put the 'disabled' tag because on a read form, it change style with grey.
150  //print $this->content;
151  $out .= '<textarea id="'.$this->htmlname.'" name="'.$this->htmlname.'" rows="'.$this->rows.'"'.(preg_match('/%/', $this->cols) ? ' style="margin-top: 5px; width: '.$this->cols.'"' : ' cols="'.$this->cols.'"').' '.($moreparam ? $moreparam : '').' class="flat '.$morecss.'">';
152  $out .= htmlspecialchars($this->content);
153  $out .= '</textarea>';
154 
155  if ($this->tool == 'ckeditor' && !empty($conf->use_javascript_ajax) && isModEnabled('fckeditor')) {
156  if (!defined('REQUIRE_CKEDITOR')) {
157  define('REQUIRE_CKEDITOR', '1');
158  }
159 
160  if (!empty($conf->global->FCKEDITOR_SKIN)) {
161  $skin = $conf->global->FCKEDITOR_SKIN;
162  } else {
163  $skin = 'moono-lisa'; // default with ckeditor 4.6 : moono-lisa
164  }
165 
166  $pluginstodisable = 'elementspath,save,flash,div,specialchar,anchor';
167  if (!empty($conf->dol_optimize_smallscreen)) {
168  $pluginstodisable .= ',scayt,wsc,find,undo';
169  }
170  if (empty($conf->global->FCKEDITOR_ENABLE_WSC)) { // spellchecker has end of life december 2021
171  $pluginstodisable .= ',wsc';
172  }
173  if (empty($conf->global->FCKEDITOR_ENABLE_PDF)) {
174  $pluginstodisable .= ',exportpdf';
175  }
176  $scaytautostartup = '';
177  if (!empty($conf->global->FCKEDITOR_ENABLE_SCAYT_AUTOSTARTUP)) {
178  $scaytautostartup = 'scayt_autoStartup: true,';
179  $scaytautostartup .= 'scayt_sLang: \''.dol_escape_js($langs->getDefaultLang()).'\',';
180  } else {
181  $pluginstodisable .= ',scayt';
182  }
183 
184  $htmlencode_force = preg_match('/_encoded$/', $this->toolbarname) ? 'true' : 'false';
185 
186  $out .= '<!-- Output ckeditor $disallowAnyContent='.$disallowAnyContent.' toolbarname='.$this->toolbarname.' -->'."\n";
187  $out .= '<script type="text/javascript">
188  $(document).ready(function () {
189  /* console.log("Run ckeditor"); */
190  /* if (CKEDITOR.loadFullCore) CKEDITOR.loadFullCore(); */
191  /* should be editor=CKEDITOR.replace but what if there is several editors ? */
192  tmpeditor = CKEDITOR.replace(\''.$this->htmlname.'\',
193  {
194  /* property:xxx is same than CKEDITOR.config.property = xxx */
195  customConfig: ckeditorConfig,
196  removePlugins: \''.$pluginstodisable.'\',
197  readOnly: '.($this->readonly ? 'true' : 'false').',
198  htmlEncodeOutput:'.$htmlencode_force.',
199  allowedContent:'.($disallowAnyContent ? 'false' : 'true').', /* Advanced Content Filter (ACF) is own when allowedContent is false */
200  extraAllowedContent: \'a[target];div{float,display}\', /* Add the style float and display into div to default other allowed tags */
201  disallowedContent: '.($disallowAnyContent ? '\'\'' : '\'\'').', /* Tags that are not allowed */
202  fullPage: '.($fullpage ? 'true' : 'false').', /* if true, the html, header and body tags are kept */
203  toolbar: \''.$this->toolbarname.'\',
204  toolbarStartupExpanded: '.($this->toolbarstartexpanded ? 'true' : 'false').',
205  width: '.($this->width ? '\''.$this->width.'\'' : '\'\'').',
206  height: '.$this->height.',
207  skin: \''.$skin.'\',
208  '.$scaytautostartup.'
209  language: \''.$langs->defaultlang.'\',
210  textDirection: \''.$langs->trans("DIRECTION").'\',
211  on : {
212  instanceReady : function( ev )
213  {
214  // Output paragraphs as <p>Text</p>.
215  this.dataProcessor.writer.setRules( \'p\', {
216  indent : false,
217  breakBeforeOpen : true,
218  breakAfterOpen : false,
219  breakBeforeClose : false,
220  breakAfterClose : true
221  });
222  }
223  },
224  disableNativeSpellChecker: '.(empty($conf->global->CKEDITOR_NATIVE_SPELLCHECKER) ? 'true' : 'false');
225 
226  if ($this->uselocalbrowser) {
227  $out .= ','."\n";
228  // To use filemanager with old fckeditor (GPL)
229  $out .= ' filebrowserBrowseUrl : ckeditorFilebrowserBrowseUrl,';
230  $out .= ' filebrowserImageBrowseUrl : ckeditorFilebrowserImageBrowseUrl,';
231  //$out.= ' filebrowserUploadUrl : \''.DOL_URL_ROOT.'/includes/fckeditor/editor/filemanagerdol/connectors/php/upload.php?Type=File\',';
232  //$out.= ' filebrowserImageUploadUrl : \''.DOL_URL_ROOT.'/includes/fckeditor/editor/filemanagerdol/connectors/php/upload.php?Type=Image\',';
233  $out .= "\n";
234  // To use filemanager with ckfinder (Non free) and ckfinder directory is inside htdocs/includes
235  /* $out.= ' filebrowserBrowseUrl : \''.DOL_URL_ROOT.'/includes/ckfinder/ckfinder.html\',
236  filebrowserImageBrowseUrl : \''.DOL_URL_ROOT.'/includes/ckfinder/ckfinder.html?Type=Images\',
237  filebrowserFlashBrowseUrl : \''.DOL_URL_ROOT.'/includes/ckfinder/ckfinder.html?Type=Flash\',
238  filebrowserUploadUrl : \''.DOL_URL_ROOT.'/includes/ckfinder/core/connector/php/connector.php?command=QuickUpload&type=Files\',
239  filebrowserImageUploadUrl : \''.DOL_URL_ROOT.'/includes/ckfinder/core/connector/php/connector.php?command=QuickUpload&type=Images\',
240  filebrowserFlashUploadUrl : \''.DOL_URL_ROOT.'/includes/ckfinder/core/connector/php/connector.php?command=QuickUpload&type=Flash\','."\n";
241  */
242  $out .= ' filebrowserWindowWidth : \'900\',
243  filebrowserWindowHeight : \'500\',
244  filebrowserImageWindowWidth : \'900\',
245  filebrowserImageWindowHeight : \'500\'';
246  }
247  $out .= ' })'.$morejs; // end CKEditor.replace
248  // Show the CKEditor javascript object once loaded is ready 'For debug)
249  //$out .= '; CKEDITOR.on(\'instanceReady\', function(ck) { ck.editor.removeMenuItem(\'maximize\'); ck.editor.removeMenuItem(\'Undo\'); ck.editor.removeMenuItem(\'undo\'); console.log(ck.editor); console.log(ck.editor.toolbar[0]); }); ';
250  $out .= '});'."\n"; // end document.ready
251  $out .= '</script>'."\n";
252  }
253  }
254 
255  // Output editor ACE
256  // Warning: ace.js and ext-statusbar.js must be loaded by the parent page.
257  if (preg_match('/^ace/', $this->tool)) {
258  $found = 1;
259  $format = $option;
260 
261  $out .= "\n".'<!-- Output Ace editor -->'."\n";
262 
263  if ($titlecontent) {
264  $out .= '<div class="aceeditorstatusbar" id="statusBar'.$this->htmlname.'">'.$titlecontent;
265  $out .= ' &nbsp; - &nbsp; <a id="morelines" href="#" class="right morelines'.$this->htmlname.' reposition">'.dol_escape_htmltag($langs->trans("ShowMoreLines")).'</a> &nbsp; &nbsp; ';
266  $out .= '</div>';
267  $out .= '<script type="text/javascript">'."\n";
268  $out .= 'jQuery(document).ready(function() {'."\n";
269  $out .= ' var aceEditor = window.ace.edit("'.$this->htmlname.'aceeditorid");
270  aceEditor.moveCursorTo('.($this->posy+1).','.$this->posx.');
271  aceEditor.gotoLine('.($this->posy+1).','.$this->posx.');
272  var StatusBar = window.ace.require("ace/ext/statusbar").StatusBar; // Init status bar. Need lib ext-statusbar
273  var statusBar = new StatusBar(aceEditor, document.getElementById("statusBar'.$this->htmlname.'")); // Init status bar. Need lib ext-statusbar
274 
275  var oldNbOfLines = 0;
276  jQuery(".morelines'.$this->htmlname.'").click(function() {
277  var aceEditorClicked = window.ace.edit("'.$this->htmlname.'aceeditorid");
278  currentline = aceEditorClicked.getOption("maxLines");
279  if (oldNbOfLines == 0)
280  {
281  oldNbOfLines = currentline;
282  }
283  console.log("We click on more lines, oldNbOfLines is "+oldNbOfLines+", we have currently "+currentline);
284  if (currentline < 500)
285  {
286  aceEditorClicked.setOptions({ maxLines: 500 });
287  }
288  else
289  {
290  aceEditorClicked.setOptions({ maxLines: oldNbOfLines });
291  }
292  });
293  })';
294  $out .= '</script>'."\n";
295  }
296 
297  $out .= '<pre id="'.$this->htmlname.'aceeditorid" style="'.($this->width ? 'width: '.$this->width.'px; ' : '');
298  $out .= ($this->height ? ' height: '.$this->height.'px; ' : '');
299  //$out.=" min-height: 100px;";
300  $out .= '">';
301  $out .= htmlspecialchars($this->content);
302  $out .= '</pre>';
303  $out .= '<input type="hidden" id="'.$this->htmlname.'_x" name="'.$this->htmlname.'_x">';
304  $out .= '<input type="hidden" id="'.$this->htmlname.'_y" name="'.$this->htmlname.'_y">';
305  $out .= '<textarea id="'.$this->htmlname.'" name="'.$this->htmlname.'" style="width:0px; height: 0px; display: none;">';
306  $out .= htmlspecialchars($this->content);
307  $out .= '</textarea>';
308 
309  $out .= '<script type="text/javascript">'."\n";
310  $out .= 'var aceEditor = window.ace.edit("'.$this->htmlname.'aceeditorid");
311 
312  aceEditor.session.setMode("ace/mode/'.$format.'");
313  aceEditor.setOptions({
314  enableBasicAutocompletion: true, // the editor completes the statement when you hit Ctrl + Space. Need lib ext-language_tools.js
315  enableLiveAutocompletion: false, // the editor completes the statement while you are typing. Need lib ext-language_tools.js
316  showPrintMargin: false, // hides the vertical limiting strip
317  minLines: 10,
318  maxLines: '.(empty($this->height) ? '34' : (round($this->height / 10))).',
319  fontSize: "110%" // ensures that the editor fits in the environment
320  });
321 
322  // defines the style of the editor
323  aceEditor.setTheme("ace/theme/chrome");
324  // hides line numbers (widens the area occupied by error and warning messages)
325  //aceEditor.renderer.setOption("showLineNumbers", false);
326  // ensures proper autocomplete, validation and highlighting of JavaScript code
327  //aceEditor.getSession().setMode("ace/mode/javascript_expression");
328  '."\n";
329 
330  $out .= 'jQuery(document).ready(function() {
331  jQuery(".buttonforacesave").click(function() {
332  console.log("We click on savefile button for component '.dol_escape_js($this->htmlname).'");
333  var aceEditor = window.ace.edit("'.dol_escape_js($this->htmlname).'aceeditorid");
334  if (aceEditor) {
335  var cursorPos = aceEditor.getCursorPosition();
336  //console.log(cursorPos);
337  if (cursorPos) {
338  jQuery("#'.dol_escape_js($this->htmlname).'_x").val(cursorPos.column);
339  jQuery("#'.dol_escape_js($this->htmlname).'_y").val(cursorPos.row);
340  }
341  //console.log(aceEditor.getSession().getValue());
342  // Inject content of editor into the original HTML field.
343  jQuery("#'.dol_escape_js($this->htmlname).'").val(aceEditor.getSession().getValue());
344  /*if (jQuery("#'.dol_escape_js($this->htmlname).'").html().length > 0) return true;
345  else return false;*/
346  return true;
347  } else {
348  console.log("Failed to retrieve js object ACE from its name");
349  return false;
350  }
351  });
352  })';
353  $out .= '</script>'."\n";
354  }
355 
356  if (empty($found)) {
357  $out .= 'Error, unknown value for tool '.$this->tool.' in DolEditor Create function.';
358  }
359 
360  if ($noprint) {
361  return $out;
362  } else {
363  print $out;
364  }
365  }
366 }
dol_escape_htmltag
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0)
Returns text escaped for inclusion in HTML alt or title tags, or into values of HTML input fields.
Definition: functions.lib.php:1493
dol_nl2br
dol_nl2br($stringtoencode, $nl2brmode=0, $forxml=false)
Replace CRLF in string with a HTML BR tag.
Definition: functions.lib.php:7160
dol_escape_js
dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
Returns text escaped for inclusion into javascript code.
Definition: functions.lib.php:1444
DolEditor\__construct
__construct($htmlname, $content, $width='', $height=200, $toolbarname='Basic', $toolbarlocation='In', $toolbarstartexpanded=false, $uselocalbrowser=1, $okforextendededitor=true, $rows=0, $cols=0, $readonly=0, $poscursor=array())
Create an object to build an HTML area to edit a large string content.
Definition: doleditor.class.php:71
dol_syslog
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
Definition: functions.lib.php:1628
isModEnabled
isModEnabled($module)
Is Dolibarr module enabled.
Definition: functions.lib.php:137
DolEditor\Create
Create($noprint=0, $morejs='', $disallowAnyContent=true, $titlecontent='', $option='', $moreparam='', $morecss='')
Output edit area inside the HTML stream.
Definition: doleditor.class.php:133
dol_textishtml
dol_textishtml($msg, $option=0)
Return if a text is a html content.
Definition: functions.lib.php:7475
type
if(preg_match('/crypted:/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
Definition: repair.php:119
DolEditor
Class to manage a WYSIWYG editor.
Definition: doleditor.class.php:30