Skip to content

Commit e742f43

Browse files
committedJul 6, 2024
CKEditor 5
Ref zencart#6209
1 parent 2fa3d15 commit e742f43

File tree

3 files changed

+359
-38
lines changed

3 files changed

+359
-38
lines changed
 

‎admin/includes/ckeditor.php

+245-38
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,258 @@
11
<?php
2+
23
/**
3-
* @copyright Copyright 2003-2023 Zen Cart Development Team
4+
* CKEditor 5
5+
*
6+
* Custom config can be set up in /editors/ckeditor5/config.js
7+
*
8+
* @copyright Copyright 2003-2024 Zen Cart Development Team
49
* @copyright Portions Copyright 2010 Kuroi Web Design
510
* @license http://www.zen-cart.com/license/2_0.txt GNU Public License V2.0
6-
* @version $Id: Scott C Wilson 2022 Oct 16 Modified in v1.5.8a $
11+
* @version $Id: Modified 2024-07-01 $
12+
*
13+
* @var language $lng
14+
*
15+
* Ref: https://ckeditor.com/docs/ckeditor5/latest/getting-started/installation/quick-start.html#installing-ckeditor-5-from-cdn
16+
* Ref: https://github.com/ckeditor/ckeditor5/releases
717
*/
818
if (!defined('IS_ADMIN_FLAG')) {
9-
die('Illegal Access');
19+
die('Illegal Access');
20+
}
21+
22+
// To "upgrade" to a newer version, set the number here. See https://github.com/ckeditor/ckeditor5/releases for latest.
23+
const CKEDITOR_VERSION = '42.0.0';
24+
25+
26+
// for compatibility with pre-ZC-v2.0.0 where class is PSR-autoloaded
27+
if (!isset($lng)) {
28+
if (!class_exists('language')) {
29+
include(DIR_FS_CATALOG . DIR_WS_CLASSES . 'language.php');
30+
}
31+
$lng = new language;
1032
}
11-
// prepare list of languages supported by this website, so we can tell CKEditor
12-
$var = zen_get_languages();
13-
$jsLanguageLookupArray = "var lang = new Array;\n";
14-
foreach ($var as $key)
15-
{
16-
$jsLanguageLookupArray .= " lang[" . $key['id'] . "] = '" . $key['code'] . "';\n";
33+
// Get an array of languages: [1=>'en', 2=>'fr', etc] to match up textarea ID suffix to know which language the editor should use for that field.
34+
if (method_exists($lng, 'get_language_list')) {
35+
$langArray = $lng->get_language_list();
36+
} else {
37+
// fallback for compatibility with pre-ZC-v2.0.0
38+
$langArray = [];
39+
foreach ($lng->catalog_languages as $lang) {
40+
$langArray[$lang['id']] = $lang['code'];
41+
}
1742
}
1843
?>
19-
<script>window.jQuery || document.write('<script src="https://code.jquery.com/jquery-3.6.1.min.js" integrity="sha256-o88AwQnZB+VDvE9tvIXrMQaPlFFSUTR+nldQm1LuPXQ=" crossorigin="anonymous"><\/script>');</script>
20-
<script>window.jQuery || document.write('<script src="includes/javascript/jquery.min.js"><\/script>');</script>
21-
<script src="https://cdn.ckeditor.com/4.22.1/standard-all/ckeditor.js" title="CKEditorCDN"></script>
22-
23-
<script title="ckEditor-Initialize">
24-
jQuery(document).ready(function() {
25-
<?php echo $jsLanguageLookupArray; ?>
26-
// Activate on every textarea field that has the editorHook class and does not have the noEditor class
27-
jQuery('textarea.editorHook').each(function() {
28-
if (!jQuery(this).hasClass('noEditor'))
29-
{
30-
// handle multi-language variants of fields
31-
index = jQuery(this).attr('name').match(/\d+/);
32-
if (index == null) index = <?php echo $_SESSION['languages_id'] ?>;
33-
34-
CKEDITOR.replace(jQuery(this).attr('name'),
35-
{
36-
customConfig: '<?php echo (function_exists('zen_catalog_base_link') ? zen_catalog_base_link() : '/') . DIR_WS_EDITORS . 'ckeditor/config.js'; ?>',
37-
language: lang[index]
38-
});
44+
45+
<link title="CKEditorCSS" rel="stylesheet" href="https://cdn.ckeditor.com/ckeditor5/<?= CKEDITOR_VERSION ?>/ckeditor5.css">
46+
<style>
47+
/* for Bootstrap compatibility to prevent interfering with tables */
48+
.ck-content .table {
49+
width: auto;
50+
}
51+
</style>
52+
<script title="CKEditor5CDN" type="importmap">
53+
{
54+
"imports": {
55+
"ckeditor5": "https://cdn.ckeditor.com/ckeditor5/<?= CKEDITOR_VERSION ?>/ckeditor5.js",
56+
"ckeditor5/": "https://cdn.ckeditor.com/ckeditor5/<?= CKEDITOR_VERSION ?>/"
57+
}
58+
}
59+
</script>
60+
<script title="CKEditor-custom-config" src="<?= (function_exists('zen_catalog_base_link') ? zen_catalog_base_link() : '/') . DIR_WS_EDITORS . 'ckeditor5/config.js' ?>"></script>
61+
<script title="CKEditor-initialize" type="module">
62+
const langArray = <?= json_encode($langArray) ?>;
63+
const defaultLang = '<?= DEFAULT_LANGUAGE ?>';
64+
const sessionLangId = '<?= $_SESSION['languages_id'] ?>';
65+
const sessionLangCode = '<?= $_SESSION['languages_code'] ?>';
66+
67+
// Get list of textarea fields that have the editorHook class and do not have the noEditor class
68+
const editorElements = document.querySelectorAll('.editorHook:not(.noEditor)');
69+
70+
import {
71+
Alignment,
72+
Autoformat,
73+
AutoImage,
74+
AutoLink,
75+
BlockQuote,
76+
Bold,
77+
ClassicEditor,
78+
Clipboard,
79+
Code,
80+
CodeBlock,
81+
Essentials,
82+
FindAndReplace,
83+
Font,
84+
GeneralHtmlSupport,
85+
Heading,
86+
HtmlEmbed,
87+
Image,
88+
ImageCaption,
89+
ImageInsert,
90+
ImageResize,
91+
ImageStyle,
92+
ImageToolbar,
93+
Indent,
94+
IndentBlock,
95+
Italic,
96+
LinkImage,
97+
List,
98+
MediaEmbed,
99+
Paragraph,
100+
PasteFromMarkdownExperimental,
101+
PasteFromOffice,
102+
RemoveFormat,
103+
SelectAll,
104+
ShowBlocks,
105+
SourceEditing,
106+
SpecialCharacters,
107+
SpecialCharactersEssentials,
108+
Strikethrough,
109+
Style,
110+
Subscript,
111+
Superscript,
112+
Table,
113+
TableCellProperties,
114+
TableColumnResize,
115+
TableProperties,
116+
TableToolbar,
117+
TextPartLanguage,
118+
Underline,
119+
Undo
120+
} from 'ckeditor5';
121+
122+
<?php
123+
// import translations needed
124+
foreach ($langArray as $langCode) {
125+
echo "import " . $langCode . "Translation from 'ckeditor5/translations/" . $langCode . ".js';\n";
126+
}
127+
?>
128+
const uiLanguages = {
129+
<?php
130+
foreach ($langArray as $langCode) {
131+
echo '"' . $langCode . '": ' . $langCode . 'Translation,';
132+
}
133+
?>
134+
};
135+
136+
137+
const editorConfig = {
138+
plugins: [
139+
Essentials, Font, Bold, Italic, Underline, Strikethrough, Subscript, Superscript, Code,
140+
Clipboard, PasteFromOffice, Autoformat, PasteFromMarkdownExperimental, FindAndReplace,
141+
CodeBlock, Heading, Paragraph, Undo, BlockQuote, Indent, IndentBlock, List, SelectAll,
142+
Alignment, GeneralHtmlSupport, HtmlEmbed, Style, SourceEditing, MediaEmbed, TextPartLanguage, ShowBlocks,
143+
SpecialCharacters, SpecialCharactersEssentials, RemoveFormat,
144+
Table, TableToolbar, TableProperties, TableCellProperties, TableColumnResize,
145+
Image, AutoImage, ImageInsert, ImageToolbar, ImageCaption, ImageStyle, ImageResize, LinkImage
146+
],
147+
toolbar: {
148+
items: [
149+
'undo', 'redo', 'findAndReplace',
150+
'|', 'link', 'insertImage', 'mediaEmbed', 'insertTable',
151+
'|', 'heading', 'fontsize', /* 'fontfamily', */ 'specialCharacters',
152+
'|', 'showBlocks',
153+
// 'sourceEditing' may or may not be desired. Do not use if admins might paste content from untrusted sources
154+
'sourceEditing',
155+
// using htmlEmbed poses a security risk: do not use if Admins might paste content from untrusted sources; A sanitizer should be configured if htmlEmbed is enabled.
156+
// 'htmlEmbed',
157+
'-', 'bold', 'italic', 'underline', 'strikethrough',
158+
// 'superscript', 'subscript',
159+
'code',
160+
'|', 'removeFormat',
161+
'|', 'numberedList', 'bulletedList', 'indent', 'outdent', 'blockQuote', 'alignment',
162+
'|', 'fontColor', 'fontBackgroundColor',
163+
],
164+
shouldNotGroupWhenFull: true
165+
},
166+
167+
image: {
168+
insert: {
169+
integrations: ['url'/*, 'upload', 'assetManager'*/],
170+
171+
// If "type" setting is omitted, the editor defaults to 'block'.
172+
type: 'auto'
173+
},
174+
// image toolbar layout:
175+
toolbar: [
176+
'imageStyle:block',
177+
'imageStyle:side',
178+
'|',
179+
'toggleImageCaption',
180+
'imageTextAlternative',
181+
'|',
182+
'linkImage',
183+
'|',
184+
'imageStyle:inline',
185+
'imageStyle:block',
186+
'imageStyle:wrapText',
187+
],
188+
},
189+
190+
link: {
191+
defaultProtocol: 'https://',
192+
allowedProtocols: [ 'https?', 'tel', 'sms', 'mailto'],
193+
decorators: {
194+
openInNewTab: {
195+
mode: 'manual',
196+
label: 'Open in a new tab',
197+
attributes: {
198+
target: '_blank',
199+
rel: 'noopener noreferrer'
200+
}
201+
}
202+
}
203+
},
204+
205+
table: {
206+
contentToolbar: [
207+
'tableColumn', 'tableRow', 'mergeTableCells',
208+
'tableProperties', 'tableCellProperties',
209+
],
210+
},
211+
};
212+
213+
214+
// console.log(editorElements);
215+
// Loop through the target elements and activate the Editor on each.
216+
for (const editorElement of editorElements) {
217+
218+
// Determine language to use for this element's content and the UI
219+
const contentLangCode = langArray[editorElement.name.match(/\d+/) ?? sessionLangId];
220+
const langConfig = {
221+
language: {
222+
// ui: sessionLangCode,
223+
content: contentLangCode,
39224
}
40-
});
41-
});
225+
};
226+
227+
const translationsConfig = {
228+
translations: [
229+
uiLanguages[contentLangCode],
230+
// uiLanguages[sessionLangCode],
231+
]
232+
};
233+
234+
// In case the override/custom config.js doesn't load or is not present, fallback to empty object.
235+
let customConfig = {};
236+
if (typeof myCKEditorConfig !== 'undefined') {
237+
customConfig = myCKEditorConfig;
238+
}
239+
240+
// Combine configs: First use the master config above, then any custom overrides from /editors/ckeditor5/config.js, and then override language for specific fields
241+
const currentEditorConfig = {...editorConfig, ...translationsConfig, ...customConfig, ...langConfig};
242+
//console.log(currentEditorConfig);
243+
244+
// Instantiate this editor instance, before looping to the next one on the page.
245+
ClassicEditor
246+
.create(editorElement, currentEditorConfig)
247+
.then(editor => {
248+
window.editor = editor;
249+
})
250+
.catch(error => {
251+
// console.error(error.stack);
252+
console.error(error);
253+
});
254+
}
42255
</script>
43256

44257
<?php
45-
// Other options:
46-
// - Edit your /editors/ckeditor/config.js file to control the toolbar buttons, add additional plugins, control the UI color, etc
47-
//
48-
// Advanced:
49-
// https://ckeditor.com/docs/ckeditor4/latest/features/styles.html#the-stylesheet-parser-plugin
50-
// - set custom styles in the /editors/ckeditor/styles.js file
51-
// - import a template-specific stylesheet from catalog-side, using config.contentsCss setting, so dialogs "look like catalog-side" in admin editor
258+
// - Edit your /editors/ckeditor5/config.js file to control the toolbar buttons, add additional plugins, etc

‎admin/includes/css/stylesheet.css

+6
Original file line numberDiff line numberDiff line change
@@ -953,3 +953,9 @@ tbody>tr>td.align-bottom {
953953
margin-top: 3rem !important;
954954
}
955955
/**/
956+
957+
/* CKEditor */
958+
.ck-editor__editable {
959+
min-height: 250px;
960+
max-height: 650px;
961+
}

‎editors/ckeditor5/config.js

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
let myCKEditorConfig = {
2+
// For complete reference see:
3+
// https://ckeditor.com/docs/ckeditor5/latest/api/module_core_editor_editorconfig-EditorConfig.html
4+
5+
// If you have a paid commercial license for premium features, enter the key here, and uncomment the line:
6+
// licenseKey: '<YOUR_LICENSE_KEY>',
7+
8+
9+
//// IMPORTANT NOTE: every section enabled here will replace the "entire" matching section from the master configuration
10+
//// ... therefore, if there is something you wish to adjust in a small way, copy that complete section from the
11+
//// master ckeditor.php editorConfig defaults and then make your alterations to that.
12+
13+
14+
// https://ckeditor.com/docs/ckeditor5/latest/getting-started/setup/menubar.html
15+
menuBar: {
16+
isVisible: true
17+
},
18+
19+
// Custom toolbar configurations can be added below.
20+
//
21+
// Beware: defining 'toolbar' will completely replace the already-configured toolbar, so you must define a complete toolbar set.
22+
// toolbar: {
23+
// },
24+
25+
// https://ckeditor.com/docs/ckeditor5/latest/features/font.html
26+
// fontFamily: {
27+
// options: [
28+
// 'default',
29+
// 'Ubuntu, Arial, sans-serif',
30+
// 'Ubuntu Mono, Courier New, Courier, monospace'
31+
// ],
32+
// }
33+
// ,
34+
35+
// Limit to certain font size choices, or expand available options
36+
fontSize: {
37+
options: [
38+
8, 9, 10, 11, 12, 'default', 13, 14, 15, 16, 17, 18, 19, 20, 22, 24, 26, 28, 36, 48, 72,
39+
],
40+
41+
// Allow all font sizes including those that are unknown to CKEditor.
42+
supportAllValues: true
43+
},
44+
45+
46+
// https://ckeditor.com/docs/ckeditor5/latest/features/html/general-html-support.html
47+
// htmlSupport: {
48+
// allow: [ /* HTML features to allow. */ ],
49+
// disallow: [ /* HTML features to disallow. */ ]
50+
// }
51+
52+
// findAndReplace: {
53+
// uiType: 'dropdown'
54+
// },
55+
56+
// https://ckeditor.com/docs/ckeditor5/latest/features/style.html
57+
// style: {
58+
// definitions: [
59+
// Styles definitions.
60+
// ...
61+
// {
62+
// name: 'Article category',
63+
// element: 'h3',
64+
// classes: [ 'category' ]
65+
// },
66+
// {
67+
// name: 'Info box',
68+
// element: 'p',
69+
// classes: [ 'info-box' ]
70+
// },
71+
// ]
72+
// },
73+
74+
// https://ckeditor.com/docs/ckeditor5/latest/features/link.html
75+
// link: {
76+
// defaultProtocol: 'https://',
77+
// allowedProtocols: [ 'https?', 'tel', 'sms', 'mailto'],
78+
// decorators: {
79+
// openInNewTab: {
80+
// mode: 'manual',
81+
// label: 'Open in a new tab',
82+
// attributes: {
83+
// target: '_blank',
84+
// rel: 'noopener noreferrer'
85+
// }
86+
// }
87+
// }
88+
// },
89+
90+
// https://ckeditor.com/docs/ckeditor5/latest/features/tables/tables-styling.html
91+
// table: {
92+
// contentToolbar: [
93+
// 'tableColumn', 'tableRow', 'mergeTableCells',
94+
// 'tableProperties', 'tableCellProperties',
95+
// ],
96+
//
97+
// tableProperties: {
98+
// // The configuration of the TableProperties plugin.
99+
// // ...
100+
// },
101+
//
102+
// tableCellProperties: {
103+
// // The configuration of the TableCellProperties plugin.
104+
// // ...
105+
// }
106+
// },
107+
108+
}

0 commit comments

Comments
 (0)
Please sign in to comment.