2006-07-22 16:09:48 +00:00
< ? php
2008-03-21 00:27:37 +00:00
/* Copyright ( C ) 2006 - 2008 Laurent Destailleur < eldy @ users . sourceforge . net >
2021-04-22 22:45:07 +00:00
* Copyright ( C ) 2021 Gaëtan MAISON < gm @ ilad . org >
2026-04-20 10:36:21 +00:00
* Copyright ( C ) 2024 - 2026 MDW < mdeweerd @ users . noreply . github . com >
2024-08-14 11:58:17 +00:00
* Copyright ( C ) 2024 Frédéric France < frederic . france @ free . fr >
2006-07-22 16:09:48 +00:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
2013-01-16 14:36:08 +00:00
* the Free Software Foundation ; either version 3 of the License , or
2006-07-22 16:09:48 +00:00
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
2019-09-23 19:55:30 +00:00
* along with this program . If not , see < https :// www . gnu . org / licenses />.
* or see https :// www . gnu . org /
2006-07-22 16:09:48 +00:00
*/
/**
2011-10-24 07:29:17 +00:00
* \file htdocs / core / class / doleditor . class . php
2010-06-18 19:40:58 +00:00
* \brief Class to manage a WYSIWYG editor
2011-01-31 16:35:55 +00:00
*/
2006-07-22 16:09:48 +00:00
/**
2012-03-12 14:09:46 +00:00
* Class to manage a WYSIWYG editor .
2025-01-17 16:09:47 +00:00
* Usage : $doleditor = new DolEditor ( 'body' , $message , 320 , 'toolbar_mailing' );
2012-03-12 14:09:46 +00:00
* $doleditor -> Create ();
2008-08-07 06:33:35 +00:00
*/
2006-07-22 16:09:48 +00:00
class DolEditor
{
2024-10-26 16:24:40 +00:00
/**
* @ var string
*/
2020-10-31 13:32:18 +00:00
public $tool ; // Store the selected tool
2010-08-27 00:09:58 +00:00
2020-10-31 13:32:18 +00:00
// If using fckeditor
2024-10-26 16:24:40 +00:00
/**
* @ var Object - Note : seems to be unused
*/
2020-10-31 13:32:18 +00:00
public $editor ;
2009-01-31 02:55:32 +00:00
2020-10-31 13:32:18 +00:00
// If not using fckeditor
2024-10-26 16:24:40 +00:00
/**
2025-01-02 09:15:30 +00:00
* @ var ? string
2024-10-26 16:24:40 +00:00
*/
2020-10-31 13:32:18 +00:00
public $content ;
2024-10-26 16:24:40 +00:00
/**
* @ var string
*/
2020-10-31 13:32:18 +00:00
public $htmlname ;
2024-10-26 16:24:40 +00:00
/**
* @ var string
*/
2020-10-31 13:32:18 +00:00
public $toolbarname ;
2024-10-26 16:24:40 +00:00
/**
* @ var bool
*/
2020-10-31 13:32:18 +00:00
public $toolbarstartexpanded ;
2024-10-26 16:24:40 +00:00
/**
* @ var int
*/
2020-10-31 13:32:18 +00:00
public $rows ;
2024-10-26 16:24:40 +00:00
/**
* @ var string
*/
2020-10-31 13:32:18 +00:00
public $cols ;
2024-10-26 16:24:40 +00:00
/**
* @ var int
*/
2020-10-31 13:32:18 +00:00
public $height ;
2024-10-26 16:24:40 +00:00
/**
* @ var string
*/
2020-10-31 13:32:18 +00:00
public $width ;
2024-10-26 16:24:40 +00:00
/**
* @ var int < 0 , 1 >| bool
*/
2023-02-22 20:26:59 +00:00
public $uselocalbrowser ;
2024-10-26 16:24:40 +00:00
/**
* @ var int < 0 , 1 >
*/
2020-10-31 13:32:18 +00:00
public $readonly ;
2024-10-26 16:24:40 +00:00
/**
* @ var int
*/
2022-02-27 13:46:40 +00:00
public $posx ;
2024-10-26 16:24:40 +00:00
/**
* @ var int
*/
2022-02-27 13:46:40 +00:00
public $posy ;
2010-08-27 00:09:58 +00:00
2009-01-31 02:55:32 +00:00
2020-10-31 13:32:18 +00:00
/**
* Create an object to build an HTML area to edit a large string content
*
2024-05-28 21:52:33 +00:00
* @ param string $htmlname HTML name of WYSIWYG field
* @ param string $content Content of WYSIWYG field
* @ param int | string $width Width in pixel of edit area ( auto by default )
* @ param int $height Height in pixel of edit area ( 200 px by default )
2025-10-29 12:28:53 +00:00
* @ param string $toolbarname Name of the toolbar set to use ( 'Basic' , 'dolibarr_details[_encoded]' = the less featured , 'dolibarr_notes[_encoded]' for notes content , 'dolibarr_mailings[_encoded]' for emailing content , 'dolibarr_readonly' ) .
* @ param string $notused Deprecated . Not used
2024-10-26 16:24:40 +00:00
* @ param bool $toolbarstartexpanded Bar is visible or not at start
2026-04-21 14:47:51 +00:00
* @ param bool | int $uselocalbrowser Enabled to add links to local object with a local media filemanager . - 1 or true = auto , false = only external images URL can be added into content , or images saved inline with src = " data:... " with a cut / paste .
2024-10-26 16:24:40 +00:00
* @ param bool | int | string $okforextendededitor 1 or True = Allow usage of extended editor tool if qualified ( like ckeditor ) . If 'textarea' , force use of simple textarea . If 'ace' , force use of Ace .
2024-05-28 21:52:33 +00:00
* Warning : If you use 'ace' , don ' t forget to also include ace . js in page header . Also , the button " save " must have class = " buttonforacesave " .
2026-04-25 10:03:27 +00:00
* @ param int $rows Size of rows for textarea text tool
* @ param string $cols Size of cols for textarea text tool ( textarea number of cols '70' or percent recommended 'x%' )
2024-10-26 16:24:40 +00:00
* @ param int < 0 , 1 > $readonly 0 = Read / Edit , 1 = Read only
* @ param array { x ? : string , y ? : string , find ? : string } $poscursor Array for initial cursor position array ( 'x' => x , 'y' => y ) .
2024-05-28 21:52:33 +00:00
* array ( 'find' => 'word' ) can be used to go to line were the word has been found
2010-06-24 19:00:21 +00:00
*/
2026-04-20 10:36:21 +00:00
public function __construct ( $htmlname , $content , $width = '' , $height = 200 , $toolbarname = 'Basic' , $notused = '' , $toolbarstartexpanded = false , $uselocalbrowser = - 1 , $okforextendededitor = true , $rows = 0 , $cols = '' , $readonly = 0 , $poscursor = array ()) // @phpstan-ignore constructor.unusedParameter
2020-10-31 13:32:18 +00:00
{
2023-11-24 09:23:21 +00:00
global $conf ;
2009-01-31 02:55:32 +00:00
2024-12-29 18:45:13 +00:00
dol_syslog ( get_class ( $this ) . " ::DolEditor htmlname= " . $htmlname . " width= " . $width . " height= " . $height . " toolbarname= " . $toolbarname . " uselocalbrowser= " . $uselocalbrowser );
2026-04-21 14:47:51 +00:00
if ( $uselocalbrowser ) {
2024-12-29 18:45:13 +00:00
// This may not be supported by new generation of WYSIWYG editors.
$uselocalbrowser = getDolGlobalInt ( " WYSIWYG_ALLOW_UPLOAD_MEDIA_FILES " );
}
2009-01-31 02:55:32 +00:00
2021-02-23 21:03:23 +00:00
if ( ! $rows ) {
$rows = round ( $height / 20 );
}
if ( ! $cols ) {
2023-12-04 11:04:36 +00:00
$cols = ( $width ? round ( $width / 6 ) : 80 );
2021-02-23 21:03:23 +00:00
}
2019-12-06 08:26:49 +00:00
$shorttoolbarname = preg_replace ( '/_encoded$/' , '' , $toolbarname );
2011-11-05 20:24:55 +00:00
2020-10-31 13:32:18 +00:00
// Name of extended editor to use (FCKEDITOR_EDITORNAME can be 'ckeditor' or 'fckeditor')
$defaulteditor = 'ckeditor' ;
2025-04-16 23:31:06 +00:00
$this -> tool = getDolGlobalString ( 'FCKEDITOR_EDITORNAME' , $defaulteditor );
2020-10-31 13:32:18 +00:00
$this -> uselocalbrowser = $uselocalbrowser ;
$this -> readonly = $readonly ;
2009-01-31 02:55:32 +00:00
2020-10-31 13:32:18 +00:00
// Check if extended editor is ok. If not we force textarea
2024-05-28 21:52:33 +00:00
if (( ! isModEnabled ( 'fckeditor' ) && $okforextendededitor !== 'ace' ) || empty ( $okforextendededitor )) {
2021-02-23 21:03:23 +00:00
$this -> tool = 'textarea' ;
}
if ( $okforextendededitor === 'ace' ) {
$this -> tool = 'ace' ;
}
2020-10-31 13:32:18 +00:00
//if ($conf->dol_use_jmobile) $this->tool = 'textarea'; // ckeditor and ace seems ok with mobile
2024-06-22 13:20:11 +00:00
if ( empty ( $conf -> use_javascript_ajax )) { // If no javascript, we force use of textarea
$this -> tool = 'textarea' ;
}
2011-08-14 23:54:09 +00:00
2024-10-26 16:24:40 +00:00
if ( isset ( $poscursor [ 'find' ])) {
2024-05-01 16:04:18 +00:00
$posy = 0 ;
$lines = explode ( " \n " , $content );
$nblines = count ( $lines );
for ( $i = 0 ; $i < $nblines ; $i ++ ) {
if ( preg_match ( '/' . $poscursor [ 'find' ] . '/' , $lines [ $i ])) {
$posy = $i ;
break ;
}
}
2024-10-26 16:24:40 +00:00
if ( $posy != 0 ) {
$poscursor [ 'y' ] = $posy ;
}
2024-05-01 16:04:18 +00:00
}
2020-10-31 13:32:18 +00:00
// Define some properties
2026-04-24 10:08:47 +00:00
if ( in_array ( $this -> tool , array ( 'textarea' , 'ckeditor' , 'tinymce' , 'ace' ))) {
if ( in_array ( $this -> tool , array ( 'ckeditor' , 'tinymce' )) && ! dol_textishtml ( $content )) { // We force content to be into HTML if we are using an advanced editor if content is not HTML.
2020-10-31 13:32:18 +00:00
$this -> content = dol_nl2br ( $content );
2021-02-23 21:03:23 +00:00
} else {
2020-10-31 13:32:18 +00:00
$this -> content = $content ;
}
$this -> htmlname = $htmlname ;
$this -> toolbarname = $shorttoolbarname ;
$this -> toolbarstartexpanded = $toolbarstartexpanded ;
$this -> rows = max ( ROWS_3 , $rows );
$this -> cols = ( preg_match ( '/%/' , $cols ) ? $cols : max ( 40 , $cols )); // If $cols is a percent, we keep it, otherwise, we take max
2022-02-27 13:46:40 +00:00
$this -> height = $height ;
2020-10-31 13:32:18 +00:00
$this -> width = $width ;
2022-02-27 13:46:40 +00:00
$this -> posx = empty ( $poscursor [ 'x' ]) ? 0 : $poscursor [ 'x' ];
$this -> posy = empty ( $poscursor [ 'y' ]) ? 0 : $poscursor [ 'y' ];
2020-10-31 13:32:18 +00:00
}
}
2006-07-22 16:09:48 +00:00
2020-10-31 13:32:18 +00:00
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Output edit area inside the HTML stream .
* Output depends on this -> tool ( fckeditor , ckeditor , textarea , ... )
*
2025-01-17 16:09:47 +00:00
* @ param int $noprint 1 = Return HTML string instead of printing it to output
* @ param string $morejs Add more js . For example : " .on( \ 'saveSnapshot \ ', function(e) { alert( \ 'ee \ '); }); " . Used by CKEditor only .
* @ param boolean | string $restrictContent Decide which content is allowed . Used by CKEditor only .
* true = restrict allowed content to a predefined list of allowed elements .
* false = allow anything
* string = true + add values into string
* @ param string $titlecontent Show title content before editor area . Used by ACE editor only .
* @ param string $option For ACE editor , set the source language ( 'html' , 'php' , 'javascript' , 'json' , ... )
* @ param string $moreparam Add extra tags to the textarea
* @ param string $morecss Add extra css to the textarea
2020-10-31 13:32:18 +00:00
* @ return void | string
*/
2025-01-17 16:09:47 +00:00
public function Create ( $noprint = 0 , $morejs = '' , $restrictContent = true , $titlecontent = '' , $option = '' , $moreparam = '' , $morecss = '' )
2020-10-31 13:32:18 +00:00
{
// phpcs:enable
global $conf , $langs ;
2011-01-31 19:28:35 +00:00
2020-10-31 13:32:18 +00:00
$fullpage = false ;
2025-01-17 16:09:47 +00:00
2025-10-17 11:41:01 +00:00
// syntax is [] for attributes and {} for value inside style
2026-07-27 21:59:46 +00:00
$extraAllowedContent = 'a[target]{text-decoration};' ;
2025-09-23 16:15:40 +00:00
$extraAllowedContent .= 'section[contenteditable,id];' ;
2025-10-17 11:41:01 +00:00
$extraAllowedContent .= 'table{border-spacing};' ;
$extraAllowedContent .= 'td{padding};' ;
$extraAllowedContent .= 'p{margin-left,margin-right,margin-top,margin-bottom,padding,line-height};' ;
2026-07-27 21:59:46 +00:00
$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}' ;
2025-09-23 16:15:40 +00:00
2025-01-17 16:09:47 +00:00
if ( is_string ( $restrictContent )) {
$extraAllowedContent = $restrictContent ;
}
2021-02-23 21:03:23 +00:00
if ( isset ( $conf -> global -> FCKEDITOR_ALLOW_ANY_CONTENT )) {
2025-01-17 16:09:47 +00:00
$restrictContent = ! getDolGlobalString ( 'FCKEDITOR_ALLOW_ANY_CONTENT' ); // Only predefined list of html tags are allowed or all
2020-10-31 13:32:18 +00:00
}
2016-09-15 21:23:10 +00:00
2020-10-31 13:32:18 +00:00
$found = 0 ;
2019-12-06 08:26:49 +00:00
$out = '' ;
2010-08-27 00:09:58 +00:00
2025-03-19 14:21:48 +00:00
$this -> content = ( string ) $this -> content ; // to avoid htmlspecialchars(): Passing null to parameter #1 ($string) of type string is deprecated
2024-12-26 10:38:38 +00:00
2026-04-24 10:08:47 +00:00
if ( in_array ( $this -> tool , array ( 'textarea' , 'ckeditor' , 'tinymce' ))) {
2020-10-31 13:32:18 +00:00
$found = 1 ;
2025-04-16 22:27:52 +00:00
2026-04-25 13:23:56 +00:00
$out .= " \n " . '<!-- Output textarea ' . dol_string_nohtmltag ( $this -> tool ) . ' ' . dol_string_nohtmltag ( $this -> htmlname ) . ' toolbarname = ' . dol_string_nohtmltag ( $this -> toolbarname ) . ' -->' . " \n " ;
2025-04-16 22:27:52 +00:00
2025-01-17 16:09:47 +00:00
// Note: We do not put the attribute 'disabled' tag because on a read form, it change style with grey.
2024-10-03 10:35:06 +00:00
$out .= '<textarea id="' . $this -> htmlname . '" name="' . $this -> htmlname . '"' ;
$out .= ' rows="' . $this -> rows . '"' ;
2026-04-25 10:03:27 +00:00
if ( preg_match ( '/%/' , $this -> cols )) { // ->cols is a percent
$out .= ' style="margin-top: 5px; width: ' ;
if ( $this -> tool == 'tinymce' ) {
$out .= $this -> cols ;
} else {
$out .= 'calc(' . $this -> cols . ' - 20px)' ; // The with in percent minus the margin.
}
2026-04-27 16:23:07 +00:00
$out .= '"' ;
2026-04-25 10:03:27 +00:00
} else {
2026-04-25 13:23:56 +00:00
$out .= ' cols="' . $this -> cols . '"' ;
2026-04-25 10:03:27 +00:00
}
2024-10-03 10:35:06 +00:00
$out .= ' ' . ( $moreparam ? $moreparam : '' );
2025-04-16 22:27:52 +00:00
$out .= ' class="flat ' . dol_string_nohtmltag ( $this -> toolbarname ) . ' ' . $morecss . '">' ;
2025-01-02 08:54:17 +00:00
$out .= htmlspecialchars ( $this -> content );
2020-10-31 13:32:18 +00:00
$out .= '</textarea>' ;
2011-01-31 19:28:35 +00:00
2022-08-31 20:02:31 +00:00
if ( $this -> tool == 'ckeditor' && ! empty ( $conf -> use_javascript_ajax ) && isModEnabled ( 'fckeditor' )) {
2021-02-23 21:03:23 +00:00
if ( ! defined ( 'REQUIRE_CKEDITOR' )) {
define ( 'REQUIRE_CKEDITOR' , '1' );
}
2011-02-01 18:49:31 +00:00
2023-01-09 11:30:56 +00:00
$skin = getDolGlobalString ( 'FCKEDITOR_SKIN' , 'moono-lisa' ); // default with ckeditor 4.6 : moono-lisa
2021-12-13 00:19:48 +00:00
2023-08-08 20:03:58 +00:00
$pluginstodisable = 'elementspath,save,flash,div,anchor' ;
if ( ! getDolGlobalString ( 'FCKEDITOR_ENABLE_SPECIALCHAR' )) {
$pluginstodisable .= ',specialchar' ;
2023-08-09 16:33:43 +00:00
}
2021-12-13 00:19:48 +00:00
if ( ! empty ( $conf -> dol_optimize_smallscreen )) {
$pluginstodisable .= ',scayt,wsc,find,undo' ;
}
2023-11-27 10:24:19 +00:00
if ( ! getDolGlobalString ( 'FCKEDITOR_ENABLE_WSC' )) { // spellchecker has end of life december 2021
2021-12-13 00:19:48 +00:00
$pluginstodisable .= ',wsc' ;
}
2023-11-27 10:24:19 +00:00
if ( ! getDolGlobalString ( 'FCKEDITOR_ENABLE_PDF' )) {
2022-05-16 18:28:00 +00:00
$pluginstodisable .= ',exportpdf' ;
}
2023-12-10 15:19:05 +00:00
if ( getDolGlobalInt ( 'MAIN_DISALLOW_URL_INTO_DESCRIPTIONS' ) == 2 ) {
2023-11-14 00:32:42 +00:00
$this -> uselocalbrowser = 0 ; // Can't use browser to navigate into files. Only links with "<img src=data:..." are allowed.
}
2021-12-13 00:19:48 +00:00
$scaytautostartup = '' ;
2023-11-27 10:24:19 +00:00
if ( getDolGlobalString ( 'FCKEDITOR_ENABLE_SCAYT_AUTOSTARTUP' )) {
2021-04-22 22:45:07 +00:00
$scaytautostartup = 'scayt_autoStartup: true,' ;
2021-12-13 00:19:48 +00:00
$scaytautostartup .= 'scayt_sLang: \'' . dol_escape_js ( $langs -> getDefaultLang ()) . '\',' ;
2021-04-22 22:45:07 +00:00
} else {
2021-12-13 00:19:48 +00:00
$pluginstodisable .= ',scayt' ;
2021-04-22 22:45:07 +00:00
}
2013-03-24 18:00:58 +00:00
2020-10-31 13:32:18 +00:00
$htmlencode_force = preg_match ( '/_encoded$/' , $this -> toolbarname ) ? 'true' : 'false' ;
2011-04-09 15:12:20 +00:00
2025-01-17 16:09:47 +00:00
$out .= '<!-- Output ckeditor disallowAnyContent=' . dol_escape_htmltag (( string ) $restrictContent ) . ' toolbarname=' . dol_escape_htmltag ( $this -> toolbarname ) . ' -->' . " \n " ;
2024-10-03 10:35:06 +00:00
//$out .= '<style>#cke_1_top { height: 34px !important; }</style>';
2023-02-18 14:10:05 +00:00
$out .= '<script nonce="' . getNonce () . ' " type= " text / javascript " >
2012-03-16 15:56:18 +00:00
$ ( document ) . ready ( function () {
2019-12-04 14:55:22 +00:00
/* console.log("Run ckeditor"); */
2011-07-06 12:44:51 +00:00
/* if (CKEDITOR.loadFullCore) CKEDITOR.loadFullCore(); */
2021-12-13 00:19:48 +00:00
/* should be editor=CKEDITOR.replace but what if there is several editors ? */
2023-01-03 16:09:08 +00:00
tmpeditor = CKEDITOR . replace ( \ '' . dol_escape_js ( $this -> htmlname ) . ' \ ' ,
2011-02-01 18:49:31 +00:00
{
2025-01-17 16:09:47 +00:00
/* property: xxx is same than CKEDITOR.config.property = xxx */
2022-05-16 18:28:00 +00:00
customConfig : ckeditorConfig ,
2023-01-03 16:09:08 +00:00
removePlugins : \ '' . dol_escape_js ( $pluginstodisable ) . ' \ ' ,
2023-12-23 09:43:19 +00:00
versionCheck : false ,
2022-05-16 18:28:00 +00:00
readOnly : '.($this->readonly ? ' true ' : ' false ').' ,
2023-01-03 16:09:08 +00:00
htmlEncodeOutput : '.dol_escape_js($htmlencode_force).' ,
2026-04-25 13:23:56 +00:00
allowedContent : '.($restrictContent ? ' false ' : ' true ').' , /* Advanced Content Filter (ACF) is on when allowedContent is false */
2025-01-17 16:09:47 +00:00
extraAllowedContent : \ '' . dol_escape_js ( $extraAllowedContent ) . ' \ ' , /* Allow a tag with attribute target, allow section tag and allow the style float and display into div to default other allowed tags */
disallowedContent : \ ' \ ' , /* Tags that are not allowed */
2022-05-16 18:28:00 +00:00
fullPage : '.($fullpage ? ' true ' : ' false ').' , /* if true, the html, header and body tags are kept */
2023-01-03 16:09:08 +00:00
toolbar : \ '' . dol_escape_js ( $this -> toolbarname ) . ' \ ' ,
2011-01-31 22:22:35 +00:00
toolbarStartupExpanded : '.($this->toolbarstartexpanded ? ' true ' : ' false ').' ,
2023-01-03 16:09:08 +00:00
width : '.($this->width ? ' \ '' . dol_escape_js ( $this -> width ) . '\'' : '\'\'' ) . ' ,
2024-10-26 16:24:40 +00:00
height : '.dol_escape_js((string) $this->height).' ,
2023-01-03 16:09:08 +00:00
skin : \ '' . dol_escape_js ( $skin ) . ' \ ' ,
2021-04-22 22:45:07 +00:00
'.$scaytautostartup.'
2023-01-03 16:09:08 +00:00
language : \ '' . dol_escape_js ( $langs -> defaultlang ) . ' \ ' ,
textDirection : \ '' . dol_escape_js ( $langs -> trans ( " DIRECTION " )) . ' \ ' ,
2021-12-13 00:19:48 +00:00
on : {
2024-01-29 12:00:33 +00:00
instanceReady : function ( ev ) {
2024-09-06 00:59:21 +00:00
console . log ( \ 'ckeditor ' . dol_escape_js ( $this -> htmlname ) . ' instanceReady\ ' );
/* If we found the attribute required on source div, we remove it (not compatible with ckeditor) */
2025-01-17 16:09:47 +00:00
/* Disabled, because attribute "required" should never be used on fields for doleditor */
2024-09-06 00:59:21 +00:00
/* jQuery("#'.dol_escape_js($this->htmlname).'").attr("required", false); */
2011-04-09 15:12:20 +00:00
// Output paragraphs as <p>Text</p>.
2021-12-13 00:19:48 +00:00
this . dataProcessor . writer . setRules ( \ ' p\ ' , {
indent : false ,
breakBeforeOpen : true ,
breakAfterOpen : false ,
breakBeforeClose : false ,
breakAfterClose : true
});
2024-01-29 12:00:33 +00:00
},
},
2024-08-28 16:50:18 +00:00
disableNativeSpellChecker : '.(getDolGlobalString(' CKEDITOR_NATIVE_SPELLCHECKER ') ? ' false ' : ' true ' );
2019-12-04 14:49:22 +00:00
2021-02-23 21:03:23 +00:00
if ( $this -> uselocalbrowser ) {
2020-10-31 13:32:18 +00:00
$out .= ',' . " \n " ;
// To use filemanager with old fckeditor (GPL)
2024-01-29 12:00:33 +00:00
// Note: ckeditorFilebrowserBrowseUrl and ckeditorFilebrowserImageBrowseUrl are defined in header by main.inc.php. They include url to browser with url of upload connector in parameter
2020-10-31 13:32:18 +00:00
$out .= ' filebrowserBrowseUrl : ckeditorFilebrowserBrowseUrl,' ;
$out .= ' filebrowserImageBrowseUrl : ckeditorFilebrowserImageBrowseUrl,' ;
$out .= " \n " ;
$out .= ' filebrowserWindowWidth : \ ' 900 \ ' ,
2011-04-22 22:13:34 +00:00
filebrowserWindowHeight : \ ' 500 \ ' ,
filebrowserImageWindowWidth : \ ' 900 \ ' ,
filebrowserImageWindowHeight : \ '500\'' ;
2020-10-31 13:32:18 +00:00
}
2021-12-13 00:19:48 +00:00
$out .= ' })' . $morejs ; // end CKEditor.replace
2026-04-25 13:23:56 +00:00
// Show the CKEditor javascript object once loaded is ready (For debug)
2021-12-13 00:19:48 +00:00
//$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]); }); ';
$out .= '});' . " \n " ; // end document.ready
2020-10-31 13:32:18 +00:00
$out .= '</script>' . " \n " ;
}
2026-04-24 10:08:47 +00:00
if ( $this -> tool == 'tinymce' && ! empty ( $conf -> use_javascript_ajax ) && isModEnabled ( 'fckeditor' )) {
if ( ! defined ( 'REQUIRE_TINYMCE' )) {
define ( 'REQUIRE_TINYMCE' , '1' );
}
$htmlencode_force = preg_match ( '/_encoded$/' , $this -> toolbarname ) ? 'true' : 'false' ;
$pickerenabled = $this -> uselocalbrowser ? 'true' : 'false' ;
$out .= '<!-- Output tinymce toolbarname=' . dol_escape_htmltag ( $this -> toolbarname ) . ' -->' . " \n " ;
$out .= '<script nonce="' . getNonce () . '" type="text/javascript">' . " \n " ;
$out .= '$(document).ready(function () {' . " \n " ;
$out .= ' if (typeof tinymce === "undefined") {' . " \n " ;
2026-04-24 11:10:12 +00:00
$out .= ' console.error("TinyMCE library not loaded. Check that .../includes/tinymce/tinymce/tinymce.min.js exists and FCKEDITOR_EDITORNAME=tinymce.");' . " \n " ;
2026-04-24 10:08:47 +00:00
$out .= ' return;' . " \n " ;
$out .= ' }' . " \n " ;
$out .= ' var toolbarName = "' . dol_escape_js ( $this -> toolbarname ) . '";' . " \n " ;
$out .= ' var toolbars = (window.dolTinymceToolbars || {});' . " \n " ;
$out .= ' var toolbarStr = toolbars[toolbarName] || toolbars["dolibarr_details"] || "undo redo | bold italic | link | code";' . " \n " ;
$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 " ;
2026-04-25 10:03:27 +00:00
if ( ! getDolGlobalString ( 'FCKEDITOR_ENABLE_SPECIALCHAR' )) {
$out .= 'pluginsStr = pluginsStr.replace(\' charmap\', \'\');' . " \n " ;
}
2026-04-24 10:08:47 +00:00
$out .= ' var tinyLang = "' . dol_escape_js ( $langs -> defaultlang ) . '";' . " \n " ;
$out .= ' var tinyConf = {' . " \n " ;
$out .= ' selector: "textarea#' . dol_escape_js ( $this -> htmlname ) . '",' . " \n " ;
$out .= ' toolbar: toolbarStr,' . " \n " ;
$out .= ' plugins: pluginsStr,' . " \n " ;
$out .= ' menubar: false,' . " \n " ;
2026-04-24 11:10:12 +00:00
if ( ! getDolGlobalString ( 'WYSIWYG_TINYMCE_ENABLE_STATUS_BAR' )) {
$out .= ' statusbar: false,' . " \n " ;
}
2026-04-24 10:08:47 +00:00
$out .= ' branding: false,' . " \n " ;
$out .= ' promotion: false,' . " \n " ;
$out .= ' license_key: "gpl",' . " \n " ;
$out .= ' readonly: ' . ( $this -> readonly ? 'true' : 'false' ) . ',' . " \n " ;
2026-04-25 10:03:27 +00:00
$out .= ' height: ' . (( int ) $this -> height + 80 ) . ',' . " \n " ;
2026-04-24 10:08:47 +00:00
if ( $this -> width ) {
$out .= ' width: "' . dol_escape_js ( $this -> width ) . '",' . " \n " ;
}
$out .= ' directionality: "' . dol_escape_js ( strtolower ( $langs -> trans ( " DIRECTION " )) === 'rtl' ? 'rtl' : 'ltr' ) . '",' . " \n " ;
$out .= ' forced_root_block: false,' . " \n " ; // equivalent of CKEDITOR.ENTER_BR
$out .= ' convert_urls: false,' . " \n " ;
$out .= ' relative_urls: false,' . " \n " ;
$out .= ' entity_encoding: "raw",' . " \n " ;
$out .= ' protect: [/<\?[\s\S]*?\?>/g],' . " \n " ; // protect PHP tags like CKEditor did
$out .= ' paste_data_images: true,' . " \n " ;
2026-05-07 10:11:48 +00:00
$fontlist = 'arial,tahoma,verdana,helvetica' ;
if ( getDolGlobalString ( 'THEME_FONT_FAMILY' )) {
$fontlist = getDolGlobalString ( 'THEME_FONT_FAMILY' ) . ', ' . $fontlist ;
}
$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 . ' } \',' ;
2026-04-25 13:23:56 +00:00
$out .= ' toolbar_mode: "sliding",' . " \n " ;
2026-04-24 10:08:47 +00:00
$out .= ' browser_spellcheck: ' . ( getDolGlobalString ( 'CKEDITOR_NATIVE_SPELLCHECKER' ) ? 'true' : 'false' ) . ',' . " \n " ;
$out .= ' language: (tinyLang && tinyLang.indexOf("en") === 0) ? "en" : tinyLang,' . " \n " ;
$out .= ' htmlEncodeOutput: ' . dol_escape_js ( $htmlencode_force ) . ',' . " \n " ;
if ( $pickerenabled === 'true' ) {
$out .= ' file_picker_types: "file image media",' . " \n " ;
$out .= ' file_picker_callback: function (cb, value, meta) {' . " \n " ;
$out .= ' var url = (meta.filetype === "image") ? window.tinymceFilebrowserImageBrowseUrl : window.tinymceFilebrowserBrowseUrl;' . " \n " ;
$out .= ' if (!url) { return; }' . " \n " ;
$out .= ' var w = window.open(url, "tinymcePicker", "width=900,height=500");' . " \n " ;
$out .= ' window.dolTinymcePickerCb = function (picked) { if (picked) { cb(picked); } if (w) { w.close(); } };' . " \n " ;
$out .= ' },' . " \n " ;
}
$out .= ' setup: function (editor) {' . " \n " ;
$out .= ' editor.on("init", function () {' . " \n " ;
$out .= ' console.log("tinymce ' . dol_escape_js ( $this -> htmlname ) . ' instanceReady");' . " \n " ;
$out .= ' });' . " \n " ;
$out .= ' }' . " \n " ;
$out .= ' };' . " \n " ;
$out .= ' tinymce.init(tinyConf)' . $morejs . ';' . " \n " ;
$out .= '});' . " \n " ;
$out .= '</script>' . " \n " ;
}
2020-10-31 13:32:18 +00:00
}
2017-07-18 10:00:18 +00:00
2020-10-31 13:32:18 +00:00
// Output editor ACE
// Warning: ace.js and ext-statusbar.js must be loaded by the parent page.
2021-02-23 21:03:23 +00:00
if ( preg_match ( '/^ace/' , $this -> tool )) {
2020-10-31 13:32:18 +00:00
$found = 1 ;
2019-12-06 08:26:49 +00:00
$format = $option ;
2017-07-18 01:18:16 +00:00
2024-10-13 00:01:14 +00:00
$out .= " \n " . '<!-- Output Ace editor ' . dol_string_nohtmltag ( $this -> htmlname ) . ' -->' . " \n " ;
2017-07-18 12:20:37 +00:00
2021-02-23 21:03:23 +00:00
if ( $titlecontent ) {
2020-10-31 13:32:18 +00:00
$out .= '<div class="aceeditorstatusbar" id="statusBar' . $this -> htmlname . '">' . $titlecontent ;
2023-12-21 11:09:54 +00:00
$out .= ' - <span id="morelines" class="right classlink cursorpointer morelines' . $this -> htmlname . '">' . dol_escape_htmltag ( $langs -> trans ( " ShowMoreLines " )) . '</span> ' ;
2020-10-31 13:32:18 +00:00
$out .= '</div>' ;
2023-02-18 14:10:05 +00:00
$out .= '<script nonce="' . getNonce () . '" type="text/javascript">' . " \n " ;
2020-10-31 13:32:18 +00:00
$out .= 'jQuery(document).ready(function() {' . " \n " ;
2024-09-14 00:44:26 +00:00
$out .= ' var aceEditor = window.ace.edit("' . dol_escape_all ( $this -> htmlname ) . ' aceeditorid " );
2024-03-12 22:15:16 +00:00
aceEditor . moveCursorTo ( '.($this->posy + 1).' , '.$this->posx.' );
aceEditor . gotoLine ( '.($this->posy + 1).' , '.$this->posx.' );
2017-07-19 14:38:00 +00:00
var StatusBar = window . ace . require ( " ace/ext/statusbar " ) . StatusBar ; // Init status bar. Need lib ext-statusbar
2024-09-14 00:44:26 +00:00
var statusBar = new StatusBar ( aceEditor , document . getElementById ( " statusBar'.dol_escape_all( $this->htmlname ).' " )); // Init status bar. Need lib ext-statusbar
2022-02-27 13:46:40 +00:00
var oldNbOfLines = 0 ;
2024-09-14 00:44:26 +00:00
jQuery ( " .morelines'.dol_escape_all( $this->htmlname ).' " ) . click ( function () {
2017-12-10 18:54:04 +00:00
var aceEditorClicked = window . ace . edit ( " '. $this->htmlname .'aceeditorid " );
currentline = aceEditorClicked . getOption ( " maxLines " );
2017-12-10 18:03:18 +00:00
if ( oldNbOfLines == 0 )
{
oldNbOfLines = currentline ;
}
2017-12-10 18:54:04 +00:00
console . log ( " We click on more lines, oldNbOfLines is " + oldNbOfLines + " , we have currently " + currentline );
2017-12-10 18:03:18 +00:00
if ( currentline < 500 )
{
2017-12-10 18:54:04 +00:00
aceEditorClicked . setOptions ({ maxLines : 500 });
2017-12-10 18:03:18 +00:00
}
else
{
2017-12-10 18:54:04 +00:00
aceEditorClicked . setOptions ({ maxLines : oldNbOfLines });
2017-12-10 18:03:18 +00:00
}
2017-07-18 12:20:37 +00:00
});
}) ' ;
2020-10-31 13:32:18 +00:00
$out .= '</script>' . " \n " ;
2017-07-18 12:20:37 +00:00
}
2020-04-20 13:36:08 +00:00
2020-10-31 13:32:18 +00:00
$out .= '<pre id="' . $this -> htmlname . 'aceeditorid" style="' . ( $this -> width ? 'width: ' . $this -> width . 'px; ' : '' );
$out .= ( $this -> height ? ' height: ' . $this -> height . 'px; ' : '' );
//$out.=" min-height: 100px;";
$out .= '">' ;
2025-01-02 08:54:17 +00:00
$out .= htmlspecialchars ( $this -> content );
2020-10-31 13:32:18 +00:00
$out .= '</pre>' ;
2022-02-10 13:34:04 +00:00
$out .= '<input type="hidden" id="' . $this -> htmlname . '_x" name="' . $this -> htmlname . '_x">' ;
$out .= '<input type="hidden" id="' . $this -> htmlname . '_y" name="' . $this -> htmlname . '_y">' ;
2020-10-31 13:32:18 +00:00
$out .= '<textarea id="' . $this -> htmlname . '" name="' . $this -> htmlname . '" style="width:0px; height: 0px; display: none;">' ;
2025-01-02 08:54:17 +00:00
$out .= htmlspecialchars ( $this -> content );
2020-10-31 13:32:18 +00:00
$out .= '</textarea>' ;
2017-07-18 10:00:18 +00:00
2023-02-18 14:10:05 +00:00
$out .= '<script nonce="' . getNonce () . '" type="text/javascript">' . " \n " ;
2020-10-31 13:32:18 +00:00
$out .= 'var aceEditor = window.ace.edit("' . $this -> htmlname . ' aceeditorid " );
2017-07-18 10:00:18 +00:00
2024-11-23 12:55:12 +00:00
aceEditor . session . setMode ( " ace/mode/'. $format .' " );
aceEditor . setReadOnly ( '.($this->readonly ? ' true ' : ' false ').' );
2017-07-18 10:00:18 +00:00
aceEditor . setOptions ({
2024-11-23 12:55:12 +00:00
enableBasicAutocompletion : true , // the editor completes the statement when you hit Ctrl + Space. Need lib ext-language_tools.js
2024-09-14 00:44:26 +00:00
enableLiveAutocompletion : false , // the editor completes the statement while you are typing. Need lib ext-language_tools.js
//enableSnippets: true, // ???
showPrintMargin : false , // hides the vertical limiting strip
minLines : 10 ,
2025-08-21 21:54:11 +00:00
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.
2024-09-14 00:44:26 +00:00
fontSize : " 110% " // ensures that the editor fits in the environment
2017-07-18 10:00:18 +00:00
});
// defines the style of the editor
aceEditor . setTheme ( " ace/theme/chrome " );
2024-09-14 00:44:26 +00:00
2017-07-18 10:00:18 +00:00
// hides line numbers (widens the area occupied by error and warning messages)
//aceEditor.renderer.setOption("showLineNumbers", false);
// ensures proper autocomplete, validation and highlighting of JavaScript code
2024-09-14 00:44:26 +00:00
//aceEditor.getSession().setMode("ace/mode/javascript_expression");'."\n";
// If page is PAGE_CONTENT to edit HTML web page in web site module, we add some autocompletion
if ( $this -> htmlname == 'PAGE_CONTENT' ) {
$out .= '
// Add custom function in the autocompletion
var customCompleter = {
getCompletions : function ( editor , session , pos , prefix , callback ) {
var wordList = [
{ caption : \ ' dol_escape_all\ ' , value : \ ' dol_escape_all ( string ) \ ' , meta : \ ' custom\ ' },
{ caption : \ ' dol_escape_js\ ' , value : \ ' dol_escape_js ( string ) \ ' , meta : \ ' custom\ ' },
{ caption : \ ' includeContainer\ ' , value : \ ' includeContainer ( alias_of_container_to_include ) \ ' , meta : \ ' custom\ ' },
{ caption : \ ' redirectToContainer\ ' , value : \ ' redirectToContainer ( alias_of_container_to_redirect_to ) \ ' , meta : \ ' custom\ ' },
{ caption : \ ' getImageFromHtmlContent\ ' , value : \ ' getImageFromHtmlContent ( websitepage -> htmlcontent ) \ ' , meta : \ ' custom\ ' },
];
callback ( null , wordList . map ( function ( word ) {
return {
caption : word . caption ,
value : word . value ,
meta : word . meta
};
}));
}
};
aceEditor . completers = [ customCompleter ];
2017-07-18 10:00:18 +00:00
' . " \n " ;
2024-09-14 00:44:26 +00:00
}
2017-07-18 10:00:18 +00:00
2024-09-14 00:44:26 +00:00
$out .= 'jQuery(document).ready(function() {' ;
$out .= ' jQuery ( " .buttonforacesave " ) . click ( function () {
2026-04-29 00:09:39 +00:00
console . log ( " We click on button (with class .buttonforacesave) that must fill ace fields for component '.dol_escape_js( $this->htmlname ).' " );
2022-03-02 23:16:55 +00:00
var aceEditor = window . ace . edit ( " '.dol_escape_js( $this->htmlname ).'aceeditorid " );
2022-02-10 13:34:04 +00:00
if ( aceEditor ) {
var cursorPos = aceEditor . getCursorPosition ();
2022-03-02 13:44:14 +00:00
//console.log(cursorPos);
2022-02-10 13:34:04 +00:00
if ( cursorPos ) {
2022-03-02 23:16:55 +00:00
jQuery ( " #'.dol_escape_js( $this->htmlname ).'_x " ) . val ( cursorPos . column );
jQuery ( " #'.dol_escape_js( $this->htmlname ).'_y " ) . val ( cursorPos . row );
2022-02-10 13:34:04 +00:00
}
2024-11-23 12:55:12 +00:00
//console.log(aceEditor.getSession().getValue());
2022-02-10 13:34:04 +00:00
// Inject content of editor into the original HTML field.
2022-03-02 23:16:55 +00:00
jQuery ( " #'.dol_escape_js( $this->htmlname ).' " ) . val ( aceEditor . getSession () . getValue ());
/* if ( jQuery ( " #'.dol_escape_js( $this->htmlname ).' " ) . html () . length > 0 ) return true ;
2022-02-10 13:34:04 +00:00
else return false ; */
2022-03-02 13:44:14 +00:00
return true ;
2022-02-10 13:34:04 +00:00
} else {
console . log ( " Failed to retrieve js object ACE from its name " );
return false ;
}
2024-11-23 12:55:12 +00:00
});
2017-07-18 01:18:16 +00:00
}) ' ;
2020-10-31 13:32:18 +00:00
$out .= '</script>' . " \n " ;
}
2010-08-27 00:09:58 +00:00
2021-02-23 21:03:23 +00:00
if ( empty ( $found )) {
2020-10-31 13:32:18 +00:00
$out .= 'Error, unknown value for tool ' . $this -> tool . ' in DolEditor Create function.' ;
}
2011-07-06 12:44:51 +00:00
2021-02-23 21:03:23 +00:00
if ( $noprint ) {
return $out ;
} else {
print $out ;
}
2020-10-31 13:32:18 +00:00
}
2006-07-22 16:09:48 +00:00
}