| @@ -10,34 +10,36 @@ | ||
| 10 | 10 | use Joomla\CMS\Factory; |
| 11 | 11 | |
| 12 | 12 | class EditorHelper extends AcymObject |
| 13 | 13 | { |
| 14 | - var $width = '95%'; | |
| 15 | - var $height = '600'; | |
| 14 | + private string $width = '95%'; | |
| 15 | + private string $height = '600'; | |
| 16 | 16 | |
| 17 | - var $cols = 100; | |
| 18 | - var $rows = 30; | |
| 17 | + private int $cols = 100; | |
| 18 | + private int $rows = 30; | |
| 19 | 19 | |
| 20 | - var $myEditor; | |
| 21 | - var $editor = ''; | |
| 22 | - var $name = 'editor_content'; | |
| 23 | - var $settings = 'editor_settings'; | |
| 24 | - var $stylesheet = 'editor_stylesheet'; | |
| 25 | - var $thumbnail = 'editor_thumbnail'; | |
| 26 | - var $content = ''; | |
| 27 | - var $editorContent = ''; | |
| 28 | - var $editorConfig = []; | |
| 29 | - var $mailId = 0; | |
| 30 | - var $automation = false; | |
| 31 | - var $walkThrough = false; | |
| 32 | - var $emailsTest; | |
| 20 | + private $myEditor; | |
| 21 | + public string $editor = ''; | |
| 33 | 22 | |
| 34 | - var $data = []; | |
| 35 | - var $defaultTemplate = ''; | |
| 23 | + private string $editorContent = ''; | |
| 24 | + private array $editorConfig = []; | |
| 25 | + private array $emailsTest; | |
| 26 | + private string $name = 'editor_content'; | |
| 36 | 27 | |
| 37 | - public $autoSave; | |
| 28 | + public string $settings = 'editor_settings'; | |
| 29 | + public string $stylesheet = 'editor_stylesheet'; | |
| 30 | + public string $content = ''; | |
| 31 | + public int $mailId = 0; | |
| 32 | + public bool $automation = false; | |
| 33 | + public bool $walkThrough = false; | |
| 38 | 34 | |
| 39 | - public function display() | |
| 35 | + // Used in the d&d | |
| 36 | + public array $data = []; | |
| 37 | + private string $defaultTemplate = ''; | |
| 38 | + | |
| 39 | + public string $autoSave; | |
| 40 | + | |
| 41 | + public function display(): void | |
| 40 | 42 | { |
| 41 | 43 | if ($this->isDragAndDrop()) { |
| 42 | 44 | ob_start(); |
| 43 | 45 | include acym_getPartial('editor', 'default_template'); |
| @@ -70,13 +72,17 @@ | ||
| 70 | 72 | acym_setVar('automation', $this->automation ? 1 : 0); |
| 71 | 73 | |
| 72 | 74 | include acym_getPartial('editor', 'editor_wysid'); |
| 73 | 75 | } else { |
| 76 | + // Outside of the Acy wrapper to prevent foundation from breaking the 100 different editors we could have here | |
| 74 | 77 | |
| 78 | + // Close acym_content div | |
| 75 | 79 | if (acym_isLeftMenuNecessary()) echo '</div>'; |
| 76 | 80 | |
| 81 | + // Close acym_wrapper div and open the no foundation div | |
| 77 | 82 | echo '</div><div class="acym_no_foundation">'; |
| 78 | 83 | |
| 84 | + // Display the editor | |
| 79 | 85 | $dtextType = new DtextType(); |
| 80 | 86 | $dtextType->display(['withButton' => false]); |
| 81 | 87 | |
| 82 | 88 | $method = 'displayWordPress'; |
| @@ -81,18 +87,20 @@ | ||
| 81 | 87 | |
| 82 | 88 | $method = 'displayWordPress'; |
| 83 | 89 | $this->$method(); |
| 84 | 90 | |
| 91 | + // The view renderer will close the acym_content div, but since we already closed it, we open a fake one | |
| 85 | 92 | if (acym_isLeftMenuNecessary()) echo '<div>'; |
| 93 | + // The view renderer will close the acym_wrapper div, which will be the acym_no_foundation div in this case | |
| 86 | 94 | } |
| 87 | 95 | } |
| 88 | 96 | |
| 89 | - public function isDragAndDrop() | |
| 97 | + public function isDragAndDrop(): bool | |
| 90 | 98 | { |
| 91 | - return strpos($this->content, 'acym__wysid__template') !== false || $this->editor == 'acyEditor'; | |
| 99 | + return strpos($this->content, 'acym__wysid__template') !== false || $this->editor === 'acyEditor'; | |
| 92 | 100 | } |
| 93 | 101 | |
| 94 | - private function displayJoomla() | |
| 102 | + private function displayJoomla(): void | |
| 95 | 103 | { |
| 96 | 104 | $dtextType = new DtextType(); |
| 97 | 105 | $dtextType->displayButton( |
| 98 | 106 | [ |
| @@ -110,11 +118,13 @@ | ||
| 110 | 118 | $this->myEditor = Editor::getInstance($this->editor); |
| 111 | 119 | } |
| 112 | 120 | $this->myEditor->initialise(); |
| 113 | 121 | |
| 122 | + // We allow the background parameter on tr,table and td. | |
| 114 | 123 | $this->editorConfig['extended_elements'] = 'table[background|cellspacing|cellpadding|width|align|bgcolor|border|style|class|id],tr[background|width|bgcolor|style|class|id|valign],td[background|width|align|bgcolor|valign|colspan|rowspan|height|style|class|id|nowrap]'; |
| 115 | 124 | |
| 116 | 125 | if (!empty($this->mailId)) { |
| 126 | + //Load Css editor | |
| 117 | 127 | $cssurl = acym_completeLink((acym_isAdmin() ? '' : 'front').'mails&task=loadCSS&id='.$this->mailId.'&time='.time()); |
| 118 | 128 | $classMail = new MailClass(); |
| 119 | 129 | $filepath = $classMail->createTemplateFile($this->mailId); |
| 120 | 130 | |
| @@ -121,11 +131,13 @@ | ||
| 121 | 131 | if ($this->editor === 'tinymce') { |
| 122 | 132 | $this->editorConfig['content_css_custom'] = $cssurl.'&local=http'; |
| 123 | 133 | $this->editorConfig['content_css'] = '0'; |
| 124 | 134 | |
| 135 | + // We disable this for Joomla 4 because it might do a fatal due to Joomla 4 TinyMCE params not well formatted | |
| 125 | 136 | if (!ACYM_J40) { |
| 137 | + // Joomla broke the custom css feature in the v3.9.21 so we have to do this | |
| 126 | 138 | $access = []; |
| 127 | - for ($i = 1 ; $i < 20 ; $i++) { | |
| 139 | + for ($i = 1; $i < 20; $i++) { | |
| 128 | 140 | $access[] = $i; |
| 129 | 141 | } |
| 130 | 142 | |
| 131 | 143 | $this->editorConfig['configuration'] = (object)[ |
| @@ -139,22 +151,29 @@ | ||
| 139 | 151 | ], |
| 140 | 152 | ]; |
| 141 | 153 | } |
| 142 | 154 | } elseif ($this->editor === 'jckeditor' || $this->editor === 'fckeditor') { |
| 155 | + //For jckeditor, we need to create a fake template.css file... lets do that on the template/css/folder | |
| 143 | 156 | $this->editorConfig['content_css_custom'] = $filepath; |
| 144 | 157 | $this->editorConfig['content_css'] = '0'; |
| 145 | 158 | $this->editorConfig['editor_css'] = '0'; |
| 146 | 159 | } else { |
| 160 | + //We still create the file so that the user can link to it manually | |
| 147 | 161 | $fileurl = str_replace(DS, '/', ACYM_MEDIA_FOLDER).'templates/css/template_'.$this->mailId.'.css?time='.time(); |
| 148 | 162 | $this->editorConfig['custom_css_url'] = $cssurl; |
| 149 | 163 | $this->editorConfig['custom_css_file'] = $fileurl; |
| 150 | 164 | $this->editorConfig['custom_css_path'] = $filepath; |
| 151 | - acym_setVar('acycssfile', $fileurl); | |
| 165 | + | |
| 166 | + // Can be useful for JCE integration for example | |
| 167 | + if (!empty($filepath)) { | |
| 168 | + acym_setVar('acycssfile', $fileurl); | |
| 169 | + } | |
| 152 | 170 | } |
| 153 | 171 | } |
| 154 | 172 | |
| 173 | + // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped -- Displaying the CMS editor, cannot be escaped. For Joomla editors. | |
| 155 | 174 | if (empty($this->editorContent)) { |
| 156 | - $this->content = acym_escape($this->content); | |
| 175 | + $this->content = esc_html($this->content); | |
| 157 | 176 | ob_start(); |
| 158 | 177 | |
| 159 | 178 | echo $this->myEditor->display( |
| 160 | 179 | $this->name, |
| @@ -177,11 +196,12 @@ | ||
| 177 | 196 | acym_addScript(true, 'function acyOnSaveEditor(){'.$this->myEditor->save($this->name).'}'); |
| 178 | 197 | } |
| 179 | 198 | |
| 180 | 199 | echo $this->editorContent; |
| 200 | + // phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped | |
| 181 | 201 | } |
| 182 | 202 | |
| 183 | - private function displayWordPress() | |
| 203 | + private function displayWordPress(): void | |
| 184 | 204 | { |
| 185 | 205 | add_filter('mce_external_plugins', [$this, 'addPlugins']); |
| 186 | 206 | add_filter('mce_buttons', [$this, 'addButtons']); |
| 187 | 207 | add_filter('mce_buttons_2', [$this, 'addButtonsToolbar']); |
| @@ -210,22 +230,21 @@ | ||
| 210 | 230 | |
| 211 | 231 | wp_editor($this->content, $this->name, $options); |
| 212 | 232 | } |
| 213 | 233 | |
| 214 | - public function addDtextButton($editor_id = 'content') | |
| 234 | + public function addDtextButton(string $editor_id = 'content'): void | |
| 215 | 235 | { |
| 216 | 236 | static $instance = 0; |
| 217 | 237 | ++$instance; |
| 218 | 238 | |
| 219 | - $img = '<i class="acymicon-chevrons"></i> '; | |
| 220 | - | |
| 221 | 239 | printf( |
| 222 | - '<button type="button" class="button" id="acym__dtext__button" data-editor="%s">%s</button>', | |
| 240 | + '<button type="button" class="button" id="acym__dtext__button" data-editor="%s"><i class="acymicon-chevrons"></i> %s</button>', | |
| 223 | 241 | esc_attr($editor_id), |
| 224 | - $img.acym_translation('ACYM_INSERT_DYNAMIC_TEXT') | |
| 242 | + esc_html(acym_translation('ACYM_INSERT_DYNAMIC_TEXT')) | |
| 225 | 243 | ); |
| 226 | 244 | } |
| 227 | 245 | |
| 246 | + // Used in partial editor | |
| 228 | 247 | private function getWYSIDSettings(): string |
| 229 | 248 | { |
| 230 | 249 | $ctrl = acym_getVar('string', 'ctrl'); |
| 231 | 250 | if ($this->isResetCampaign() || !in_array($ctrl, ['dashboard', 'mails', 'campaigns', 'frontmails', 'frontcampaigns'])) { |
| @@ -253,8 +272,9 @@ | ||
| 253 | 272 | |
| 254 | 273 | return empty($settings) ? '{}' : $settings; |
| 255 | 274 | } |
| 256 | 275 | |
| 276 | + // Used in partial editor | |
| 257 | 277 | private function getWYSIDStylesheet(): string |
| 258 | 278 | { |
| 259 | 279 | $ctrl = acym_getVar('string', 'ctrl'); |
| 260 | 280 | if ($this->isResetCampaign() || !in_array($ctrl, ['mails', 'campaigns', 'frontmails', 'frontcampaigns'])) { |
| @@ -288,29 +308,20 @@ | ||
| 288 | 308 | |
| 289 | 309 | return empty($stylesheet) ? '' : $stylesheet; |
| 290 | 310 | } |
| 291 | 311 | |
| 292 | - private function isResetCampaign() | |
| 312 | + private function isResetCampaign(): bool | |
| 293 | 313 | { |
| 294 | 314 | $fromId = acym_getVar('int', 'from', 0); |
| 295 | 315 | |
| 296 | - return -1 == $fromId; | |
| 316 | + return -1 === $fromId; | |
| 297 | 317 | } |
| 298 | 318 | |
| 299 | - private function getWYSIDThumbnail() | |
| 319 | + /** | |
| 320 | + * Methods used to add buttons to the WordPress editor | |
| 321 | + */ | |
| 322 | + private function addButtonAtPosition(array &$buttons, string $newButton, string $after): void | |
| 300 | 323 | { |
| 301 | - if ($this->thumbnail != 'editor_thumbnail') return $this->thumbnail; | |
| 302 | - | |
| 303 | - $id = acym_getVar('int', 'id'); | |
| 304 | - if (empty($id)) return null; | |
| 305 | - | |
| 306 | - $thumbnail = acym_loadResult('SELECT thumbnail FROM #__acym_mail WHERE id = '.intval($id)); | |
| 307 | - | |
| 308 | - return empty($thumbnail) ? '' : $thumbnail; | |
| 309 | - } | |
| 310 | - | |
| 311 | - private function addButtonAtPosition(&$buttons, $newButton, $after) | |
| 312 | - { | |
| 313 | 324 | $position = array_search($after, $buttons); |
| 314 | 325 | |
| 315 | 326 | if ($position === false) { |
| 316 | 327 | array_push($buttons, 'separator', $newButton); |
| @@ -318,9 +329,9 @@ | ||
| 318 | 329 | array_splice($buttons, $position + 1, 0, $newButton); |
| 319 | 330 | } |
| 320 | 331 | } |
| 321 | 332 | |
| 322 | - public function addPlugins($plugins) | |
| 333 | + public function addPlugins(array $plugins): array | |
| 323 | 334 | { |
| 324 | 335 | $plugins['table'] = ACYM_JS.'tinymce/table.min.js'; |
| 325 | 336 | |
| 326 | 337 | return $plugins; |
| @@ -325,9 +336,9 @@ | ||
| 325 | 336 | |
| 326 | 337 | return $plugins; |
| 327 | 338 | } |
| 328 | 339 | |
| 329 | - public function addButtons($buttons) | |
| 340 | + public function addButtons(array $buttons): array | |
| 330 | 341 | { |
| 331 | 342 | $position = array_search('wp_more', $buttons); |
| 332 | 343 | if ($position !== false) { |
| 333 | 344 | $buttons[$position] = ''; |
| @@ -343,9 +354,9 @@ | ||
| 343 | 354 | |
| 344 | 355 | return $buttons; |
| 345 | 356 | } |
| 346 | 357 | |
| 347 | - public function addButtonsToolbar($buttons) | |
| 358 | + public function addButtonsToolbar(array $buttons): array | |
| 348 | 359 | { |
| 349 | 360 | $position = array_search('strikethrough', $buttons); |
| 350 | 361 | if ($position !== false) { |
| 351 | 362 | $buttons[$position] = ''; |
| @@ -354,16 +365,21 @@ | ||
| 354 | 365 | |
| 355 | 366 | return $buttons; |
| 356 | 367 | } |
| 357 | 368 | |
| 358 | - public function getSettingsStyle($settings) | |
| 369 | + public function getSettingsStyle($settings): string | |
| 359 | 370 | { |
| 360 | - if (empty($settings)) return ''; | |
| 371 | + if (empty($settings)) { | |
| 372 | + return ''; | |
| 373 | + } | |
| 374 | + | |
| 361 | 375 | if (is_string($settings) && substr($settings, 0, 2) === '{"') { |
| 362 | 376 | $settings = json_decode($settings, true); |
| 363 | 377 | } |
| 364 | 378 | |
| 365 | - if (!is_array($settings)) return ''; | |
| 379 | + if (!is_array($settings)) { | |
| 380 | + return ''; | |
| 381 | + } | |
| 366 | 382 | |
| 367 | 383 | $styles = ''; |
| 368 | 384 | foreach ($settings as $element => $rules) { |
| 369 | 385 | $values = ''; |
| @@ -382,9 +398,9 @@ | ||
| 382 | 398 | |
| 383 | 399 | return $styles; |
| 384 | 400 | } |
| 385 | 401 | |
| 386 | - private function getDefaultColors() | |
| 402 | + private function getDefaultColors(): string | |
| 387 | 403 | { |
| 388 | 404 | $mailSettings = new \stdClass(); |
| 389 | 405 | if (isset($this->data['mail']->mail_settings) && !empty($this->data['mail']->mail_settings)) { |
| 390 | 406 | $mailSettings = json_decode($this->data['mail']->mail_settings, false); |