[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * @package Joomla.Plugin 5 * @subpackage Editors.tinymce 6 * 7 * @copyright (C) 2021 Open Source Matters, Inc. <https://www.joomla.org> 8 * @license GNU General Public License version 2 or later; see LICENSE.txt 9 */ 10 11 namespace Joomla\Plugin\Editors\TinyMCE\PluginTraits; 12 13 use Joomla\CMS\Filesystem\Folder; 14 use Joomla\CMS\Filter\InputFilter; 15 use Joomla\CMS\HTML\HTMLHelper; 16 use Joomla\CMS\Language\Text; 17 use Joomla\CMS\Layout\LayoutHelper; 18 use Joomla\CMS\Session\Session; 19 use Joomla\CMS\Uri\Uri; 20 use Joomla\Registry\Registry; 21 use stdClass; 22 23 // phpcs:disable PSR1.Files.SideEffects 24 \defined('_JEXEC') or die; 25 // phpcs:enable PSR1.Files.SideEffects 26 27 /** 28 * Handles the onDisplay event for the TinyMCE editor. 29 * 30 * @since 4.1.0 31 */ 32 trait DisplayTrait 33 { 34 use GlobalFilters; 35 use KnownButtons; 36 use ResolveFiles; 37 use ToolbarPresets; 38 use XTDButtons; 39 40 /** 41 * Display the editor area. 42 * 43 * @param string $name The name of the editor area. 44 * @param string $content The content of the field. 45 * @param string $width The width of the editor area. 46 * @param string $height The height of the editor area. 47 * @param int $col The number of columns for the editor area. 48 * @param int $row The number of rows for the editor area. 49 * @param boolean $buttons True and the editor buttons will be displayed. 50 * @param string $id An optional ID for the textarea. If not supplied the name is used. 51 * @param string $asset The object asset 52 * @param object $author The author. 53 * @param array $params Associative array of editor parameters. 54 * 55 * @return string 56 */ 57 public function onDisplay( 58 $name, 59 $content, 60 $width, 61 $height, 62 $col, 63 $row, 64 $buttons = true, 65 $id = null, 66 $asset = null, 67 $author = null, 68 $params = [] 69 ) { 70 $id = empty($id) ? $name : $id; 71 $user = $this->app->getIdentity(); 72 $language = $this->app->getLanguage(); 73 $doc = $this->app->getDocument(); 74 $id = preg_replace('/(\s|[^A-Za-z0-9_])+/', '_', $id); 75 $nameGroup = explode('[', preg_replace('/\[\]|\]/', '', $name)); 76 $fieldName = end($nameGroup); 77 $scriptOptions = []; 78 $externalPlugins = []; 79 $options = $doc->getScriptOptions('plg_editor_tinymce'); 80 $theme = 'silver'; 81 82 // Data object for the layout 83 $textarea = new stdClass(); 84 $textarea->name = $name; 85 $textarea->id = $id; 86 $textarea->class = 'mce_editable joomla-editor-tinymce'; 87 $textarea->cols = $col; 88 $textarea->rows = $row; 89 $textarea->width = is_numeric($width) ? $width . 'px' : $width; 90 $textarea->height = is_numeric($height) ? $height . 'px' : $height; 91 $textarea->content = $content; 92 $textarea->readonly = !empty($params['readonly']); 93 94 // Render Editor markup 95 $editor = '<div class="js-editor-tinymce">'; 96 $editor .= LayoutHelper::render('joomla.tinymce.textarea', $textarea); 97 $editor .= !$this->app->client->mobile ? LayoutHelper::render('joomla.tinymce.togglebutton') : ''; 98 $editor .= '</div>'; 99 100 // Prepare the instance specific options 101 if (empty($options['tinyMCE'][$fieldName])) { 102 $options['tinyMCE'][$fieldName] = []; 103 } 104 105 // Width and height 106 if ($width && empty($options['tinyMCE'][$fieldName]['width'])) { 107 $options['tinyMCE'][$fieldName]['width'] = $width; 108 } 109 110 if ($height && empty($options['tinyMCE'][$fieldName]['height'])) { 111 $options['tinyMCE'][$fieldName]['height'] = $height; 112 } 113 114 // Set editor to readonly mode 115 if (!empty($params['readonly'])) { 116 $options['tinyMCE'][$fieldName]['readonly'] = 1; 117 } 118 119 // The ext-buttons 120 if (empty($options['tinyMCE'][$fieldName]['joomlaExtButtons'])) { 121 $btns = $this->tinyButtons($id, $buttons); 122 123 $options['tinyMCE'][$fieldName]['joomlaMergeDefaults'] = true; 124 $options['tinyMCE'][$fieldName]['joomlaExtButtons'] = $btns; 125 } 126 127 $doc->addScriptOptions('plg_editor_tinymce', $options, false); 128 // Setup Default (common) options for the Editor script 129 130 // Check whether we already have them 131 if (!empty($options['tinyMCE']['default'])) { 132 return $editor; 133 } 134 135 $ugroups = array_combine($user->getAuthorisedGroups(), $user->getAuthorisedGroups()); 136 137 // Prepare the parameters 138 $levelParams = new Registry(); 139 $extraOptions = new stdClass(); 140 $toolbarParams = new stdClass(); 141 $extraOptionsAll = (array) $this->params->get('configuration.setoptions', []); 142 $toolbarParamsAll = (array) $this->params->get('configuration.toolbars', []); 143 144 // Sort the array in reverse, so the items with lowest access level goes first 145 krsort($extraOptionsAll); 146 147 // Get configuration depend from User group 148 foreach ($extraOptionsAll as $set => $val) { 149 $val = (object) $val; 150 $val->access = empty($val->access) ? [] : $val->access; 151 152 // Check whether User in one of allowed group 153 foreach ($val->access as $group) { 154 if (isset($ugroups[$group])) { 155 $extraOptions = $val; 156 $toolbarParams = (object) $toolbarParamsAll[$set]; 157 } 158 } 159 } 160 161 // load external plugins 162 if (isset($extraOptions->external_plugins) && $extraOptions->external_plugins) { 163 foreach (json_decode(json_encode($extraOptions->external_plugins), true) as $external) { 164 // get the path for readability 165 $path = $external['path']; 166 167 // if we have a name and path, add it to the list 168 if ($external['name'] != '' && $path != '') { 169 $externalPlugins[$external['name']] = substr($path, 0, 1) == '/' ? Uri::root() . substr($path, 1) : $path; 170 } 171 } 172 } 173 174 // Merge the params 175 $levelParams->loadObject($toolbarParams); 176 $levelParams->loadObject($extraOptions); 177 178 // Set the selected skin 179 $skin = $levelParams->get($this->app->isClient('administrator') ? 'skin_admin' : 'skin', 'oxide'); 180 181 // Check that selected skin exists. 182 $skin = Folder::exists(JPATH_ROOT . '/media/vendor/tinymce/skins/ui/' . $skin) ? $skin : 'oxide'; 183 184 if (!$levelParams->get('lang_mode', 1)) { 185 // Admin selected language 186 $langPrefix = $levelParams->get('lang_code', 'en'); 187 } else { 188 // Reflect the current language 189 if (file_exists(JPATH_ROOT . '/media/vendor/tinymce/langs/' . $language->getTag() . '.js')) { 190 $langPrefix = $language->getTag(); 191 } elseif (file_exists(JPATH_ROOT . '/media/vendor/tinymce/langs/' . substr($language->getTag(), 0, strpos($language->getTag(), '-')) . '.js')) { 192 $langPrefix = substr($language->getTag(), 0, strpos($language->getTag(), '-')); 193 } else { 194 $langPrefix = 'en'; 195 } 196 } 197 198 $use_content_css = $levelParams->get('content_css', 1); 199 $content_css_custom = $levelParams->get('content_css_custom', ''); 200 $content_css = null; 201 202 // Loading of css file for 'styles' dropdown 203 if ($content_css_custom) { 204 /** 205 * If URL, just pass it to $content_css 206 * else, assume it is a file name in the current template folder 207 */ 208 $content_css = strpos($content_css_custom, 'http') !== false 209 ? $content_css_custom 210 : $this->includeRelativeFiles('css', $content_css_custom); 211 } else { 212 // Process when use_content_css is Yes and no custom file given 213 $content_css = $use_content_css ? $this->includeRelativeFiles('css', 'editor' . (JDEBUG ? '' : '.min') . '.css') : $content_css; 214 } 215 216 $ignore_filter = false; 217 218 // Text filtering 219 if ($levelParams->get('use_config_textfilters', 0)) { 220 // Use filters from com_config 221 $filter = static::getGlobalFilters($user); 222 $ignore_filter = $filter === false; 223 $blockedTags = !empty($filter->blockedTags) ? $filter->blockedTags : []; 224 $blockedAttributes = !empty($filter->blockedAttributes) ? $filter->blockedAttributes : []; 225 $tagArray = !empty($filter->tagsArray) ? $filter->tagsArray : []; 226 $attrArray = !empty($filter->attrArray) ? $filter->attrArray : []; 227 $invalid_elements = implode(',', array_merge($blockedTags, $blockedAttributes, $tagArray, $attrArray)); 228 229 // Valid elements are all entries listed as allowed in com_config, which are now missing in the filter blocked properties 230 $default_filter = InputFilter::getInstance(); 231 $valid_elements = implode(',', array_diff($default_filter->blockedTags, $blockedTags)); 232 233 $extended_elements = ''; 234 } else { 235 // Use filters from TinyMCE params 236 $invalid_elements = trim($levelParams->get('invalid_elements', 'script,applet,iframe')); 237 $extended_elements = trim($levelParams->get('extended_elements', '')); 238 $valid_elements = trim($levelParams->get('valid_elements', '')); 239 } 240 241 // The param is true for vertical resizing only, false or both 242 $resizing = (bool) $levelParams->get('resizing', true); 243 $resize_horizontal = (bool) $levelParams->get('resize_horizontal', true); 244 245 if ($resizing && $resize_horizontal) { 246 $resizing = 'both'; 247 } 248 249 // Set of always available plugins 250 $plugins = [ 251 'autolink', 252 'lists', 253 'importcss', 254 'quickbars', 255 ]; 256 257 // Allowed elements 258 $elements = [ 259 'hr[id|title|alt|class|width|size|noshade]', 260 ]; 261 $elements = $extended_elements ? array_merge($elements, explode(',', $extended_elements)) : $elements; 262 263 // Prepare the toolbar/menubar 264 $knownButtons = static::getKnownButtons(); 265 266 // Check if there no value at all 267 if (!$levelParams->get('menu') && !$levelParams->get('toolbar1') && !$levelParams->get('toolbar2')) { 268 // Get from preset 269 $presets = static::getToolbarPreset(); 270 271 /** 272 * Predefine group as: 273 * Set 0: for Administrator, Editor, Super Users (4,7,8) 274 * Set 1: for Registered, Manager (2,6), all else are public 275 */ 276 switch (true) { 277 case isset($ugroups[4]) || isset($ugroups[7]) || isset($ugroups[8]): 278 $preset = $presets['advanced']; 279 break; 280 281 case isset($ugroups[2]) || isset($ugroups[6]): 282 $preset = $presets['medium']; 283 break; 284 285 default: 286 $preset = $presets['simple']; 287 } 288 289 $levelParams->loadArray($preset); 290 } 291 292 $menubar = (array) $levelParams->get('menu', []); 293 $toolbar1 = (array) $levelParams->get('toolbar1', []); 294 $toolbar2 = (array) $levelParams->get('toolbar2', []); 295 296 // Make an easy way to check which button is enabled 297 $allButtons = array_merge($toolbar1, $toolbar2); 298 $allButtons = array_combine($allButtons, $allButtons); 299 300 // Check for button-specific plugins 301 foreach ($allButtons as $btnName) { 302 if (!empty($knownButtons[$btnName]['plugin'])) { 303 $plugins[] = $knownButtons[$btnName]['plugin']; 304 } 305 } 306 307 // Template 308 $templates = []; 309 310 if (!empty($allButtons['template'])) { 311 // Do we have a custom content_template_path 312 $template_path = $levelParams->get('content_template_path'); 313 $template_path = $template_path ? '/templates/' . $template_path : '/media/vendor/tinymce/templates'; 314 315 $filepaths = Folder::exists(JPATH_ROOT . $template_path) 316 ? Folder::files(JPATH_ROOT . $template_path, '\.(html|txt)$', false, true) 317 : []; 318 319 foreach ($filepaths as $filepath) { 320 $fileinfo = pathinfo($filepath); 321 $filename = $fileinfo['filename']; 322 $full_filename = $fileinfo['basename']; 323 324 if ($filename === 'index') { 325 continue; 326 } 327 328 $title = $filename; 329 $title_upper = strtoupper($filename); 330 $description = ' '; 331 332 if ($language->hasKey('PLG_TINY_TEMPLATE_' . $title_upper . '_TITLE')) { 333 $title = Text::_('PLG_TINY_TEMPLATE_' . $title_upper . '_TITLE'); 334 } 335 336 if ($language->hasKey('PLG_TINY_TEMPLATE_' . $title_upper . '_DESC')) { 337 $description = Text::_('PLG_TINY_TEMPLATE_' . $title_upper . '_DESC'); 338 } 339 340 $templates[] = [ 341 'title' => $title, 342 'description' => $description, 343 'url' => Uri::root(true) . $template_path . '/' . $full_filename, 344 ]; 345 } 346 } 347 348 // Check for extra plugins, from the setoptions form 349 foreach (['wordcount' => 1, 'advlist' => 1, 'autosave' => 1, 'textpattern' => 0] as $pName => $def) { 350 if ($levelParams->get($pName, $def)) { 351 $plugins[] = $pName; 352 } 353 } 354 355 // Use CodeMirror in the code view instead of plain text to provide syntax highlighting 356 if ($levelParams->get('sourcecode', 1)) { 357 $externalPlugins['highlightPlus'] = HTMLHelper::_('script', 'plg_editors_tinymce/plugins/highlighter/plugin-es5.min.js', ['relative' => true, 'version' => 'auto', 'pathOnly' => true]); 358 } 359 360 $dragdrop = $levelParams->get('drag_drop', 1); 361 362 if ($dragdrop && $user->authorise('core.create', 'com_media')) { 363 $externalPlugins['jdragndrop'] = HTMLHelper::_('script', 'plg_editors_tinymce/plugins/dragdrop/plugin.min.js', ['relative' => true, 'version' => 'auto', 'pathOnly' => true]); 364 $uploadUrl = Uri::base(false) . 'index.php?option=com_media&format=json&url=1&task=api.files'; 365 $uploadUrl = $this->app->isClient('site') ? htmlentities($uploadUrl, ENT_NOQUOTES, 'UTF-8', false) : $uploadUrl; 366 367 Text::script('PLG_TINY_ERR_UNSUPPORTEDBROWSER'); 368 Text::script('ERROR'); 369 Text::script('PLG_TINY_DND_ADDITIONALDATA'); 370 Text::script('PLG_TINY_DND_ALTTEXT'); 371 Text::script('PLG_TINY_DND_LAZYLOADED'); 372 Text::script('PLG_TINY_DND_EMPTY_ALT'); 373 374 $scriptOptions['parentUploadFolder'] = $levelParams->get('path', ''); 375 $scriptOptions['csrfToken'] = Session::getFormToken(); 376 $scriptOptions['uploadUri'] = $uploadUrl; 377 378 // @TODO have a way to select the adapter, similar to $levelParams->get('path', ''); 379 $scriptOptions['comMediaAdapter'] = 'local-images:'; 380 } 381 382 // Convert pt to px in dropdown 383 $scriptOptions['fontsize_formats'] = '8px 10px 12px 14px 18px 24px 36px'; 384 385 // select the languages for the "language of parts" menu 386 if (isset($extraOptions->content_languages) && $extraOptions->content_languages) { 387 foreach (json_decode(json_encode($extraOptions->content_languages), true) as $content_language) { 388 // if we have a language name and a language code then add to the menu 389 if ($content_language['content_language_name'] != '' && $content_language['content_language_code'] != '') { 390 $ctemp[] = array('title' => $content_language['content_language_name'], 'code' => $content_language['content_language_code']); 391 } 392 } 393 $scriptOptions['content_langs'] = array_merge($ctemp); 394 } 395 396 // User custom plugins and buttons 397 $custom_plugin = trim($levelParams->get('custom_plugin', '')); 398 $custom_button = trim($levelParams->get('custom_button', '')); 399 400 if ($custom_plugin) { 401 $plugins = array_merge($plugins, explode(strpos($custom_plugin, ',') !== false ? ',' : ' ', $custom_plugin)); 402 } 403 404 if ($custom_button) { 405 $toolbar1 = array_merge($toolbar1, explode(strpos($custom_button, ',') !== false ? ',' : ' ', $custom_button)); 406 } 407 408 // Merge the two toolbars for backwards compatibility 409 $toolbar = array_merge($toolbar1, $toolbar2); 410 411 // Build the final options set 412 $scriptOptions = array_merge( 413 $scriptOptions, 414 [ 415 'deprecation_warnings' => JDEBUG ? true : false, 416 'suffix' => JDEBUG ? '' : '.min', 417 'baseURL' => Uri::root(true) . '/media/vendor/tinymce', 418 'directionality' => $language->isRtl() ? 'rtl' : 'ltr', 419 'language' => $langPrefix, 420 'autosave_restore_when_empty' => false, 421 'skin' => $skin, 422 'theme' => $theme, 423 'schema' => 'html5', 424 425 // Toolbars 426 'menubar' => empty($menubar) ? false : implode(' ', array_unique($menubar)), 427 'toolbar' => empty($toolbar) ? null : 'jxtdbuttons ' . implode(' ', $toolbar), 428 429 'plugins' => implode(',', array_unique($plugins)), 430 431 // Quickbars 432 'quickbars_image_toolbar' => false, 433 'quickbars_insert_toolbar' => false, 434 'quickbars_selection_toolbar' => 'bold italic underline | H2 H3 | link blockquote', 435 436 // Cleanup/Output 437 'browser_spellcheck' => true, 438 'entity_encoding' => $levelParams->get('entity_encoding', 'raw'), 439 'verify_html' => !$ignore_filter, 440 'paste_as_text' => (bool) $levelParams->get('paste_as_text', false), 441 442 'valid_elements' => $valid_elements, 443 'extended_valid_elements' => implode(',', $elements), 444 'invalid_elements' => $invalid_elements, 445 446 // URL 447 'relative_urls' => (bool) $levelParams->get('relative_urls', true), 448 'remove_script_host' => false, 449 450 // Drag and drop Images always FALSE, reverting this allows for inlining the images 451 'paste_data_images' => false, 452 453 // Layout 454 'content_css' => $content_css, 455 'document_base_url' => Uri::root(true) . '/', 456 'image_caption' => true, 457 'importcss_append' => true, 458 'height' => $this->params->get('html_height', '550px'), 459 'width' => $this->params->get('html_width', ''), 460 'elementpath' => (bool) $levelParams->get('element_path', true), 461 'resize' => $resizing, 462 'templates' => $templates, 463 'external_plugins' => empty($externalPlugins) ? null : $externalPlugins, 464 'contextmenu' => (bool) $levelParams->get('contextmenu', true) ? null : false, 465 'toolbar_sticky' => true, 466 'toolbar_mode' => $levelParams->get('toolbar_mode', 'sliding'), 467 468 // Image plugin options 469 'a11y_advanced_options' => true, 470 'image_advtab' => (bool) $levelParams->get('image_advtab', false), 471 'image_title' => true, 472 473 // Drag and drop specific 474 'dndEnabled' => $dragdrop, 475 476 // Disable TinyMCE Branding 477 'branding' => false, 478 ] 479 ); 480 481 if ($levelParams->get('newlines')) { 482 // Break 483 $scriptOptions['force_br_newlines'] = true; 484 $scriptOptions['forced_root_block'] = ''; 485 } else { 486 // Paragraph 487 $scriptOptions['force_br_newlines'] = false; 488 $scriptOptions['forced_root_block'] = 'p'; 489 } 490 491 $scriptOptions['rel_list'] = [ 492 ['title' => 'None', 'value' => ''], 493 ['title' => 'Alternate', 'value' => 'alternate'], 494 ['title' => 'Author', 'value' => 'author'], 495 ['title' => 'Bookmark', 'value' => 'bookmark'], 496 ['title' => 'Help', 'value' => 'help'], 497 ['title' => 'License', 'value' => 'license'], 498 ['title' => 'Lightbox', 'value' => 'lightbox'], 499 ['title' => 'Next', 'value' => 'next'], 500 ['title' => 'No Follow', 'value' => 'nofollow'], 501 ['title' => 'No Referrer', 'value' => 'noreferrer'], 502 ['title' => 'Prefetch', 'value' => 'prefetch'], 503 ['title' => 'Prev', 'value' => 'prev'], 504 ['title' => 'Search', 'value' => 'search'], 505 ['title' => 'Tag', 'value' => 'tag'], 506 ]; 507 508 $scriptOptions['style_formats'] = [ 509 [ 510 'title' => Text::_('PLG_TINY_MENU_CONTAINER'), 511 'items' => [ 512 ['title' => 'article', 'block' => 'article', 'wrapper' => true, 'merge_siblings' => false], 513 ['title' => 'aside', 'block' => 'aside', 'wrapper' => true, 'merge_siblings' => false], 514 ['title' => 'section', 'block' => 'section', 'wrapper' => true, 'merge_siblings' => false], 515 ], 516 ], 517 ]; 518 519 $scriptOptions['style_formats_merge'] = true; 520 $options['tinyMCE']['default'] = $scriptOptions; 521 522 $doc->addScriptOptions('plg_editor_tinymce', $options); 523 524 return $editor; 525 } 526 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |