Skip to content

Commit 85470e6

Browse files
committed
Release 8.10.6: Link-Attribute-Fixes in Plugins
1 parent bed7a19 commit 85470e6

50 files changed

Lines changed: 238 additions & 172 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
Changelog
22
=========
33

4+
Version 8.10.6
5+
---------------
6+
7+
### Fixes
8+
9+
* **Link-Attribute bleiben erhalten (target/rel/title):** Mehrere Plugin-Schema-Regeln wurden so korrigiert, dass `a`-Attribute nicht mehr auf eine zu kleine Menge reduziert werden. Betroffen waren insbesondere `for_footnotes` (inkl. Legacy-`footnotes`-Dist-Artefakte) und `phonelink`.
10+
* **Plugin-first-Fix statt globalem Workaround:** Die Korrektur liegt jetzt in den betroffenen Plugins selbst, damit `target="_blank"`, `rel="nofollow"` und `title` beim Serialisieren (`getContent`) stabil erhalten bleiben.
11+
* **Verifiziert im Editor-Flow:** Link-Dialog-Auswahl wird korrekt in den finalen HTML-Output übernommen; bei `_blank` wird `noopener` wie vorgesehen ergänzt.
12+
413
Version 8.10.5
514
---------------
615

assets/scripts/base.js

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,6 +1015,9 @@ function tiny_init(container) {
10151015
}
10161016

10171017
if (typeof event.value === 'string') {
1018+
if (!/^tel:/i.test(String(event.value).trim())) {
1019+
return;
1020+
}
10181021
event.value = normalizePhoneHref(event.value);
10191022
return;
10201023
}
@@ -1023,9 +1026,127 @@ function tiny_init(container) {
10231026
return;
10241027
}
10251028

1029+
if (!/^tel:/i.test(String(event.value.href).trim())) {
1030+
return;
1031+
}
1032+
10261033
event.value.href = normalizePhoneHref(event.value.href);
10271034
});
10281035

1036+
let pendingLinkDialogAttrs = null;
1037+
1038+
function hasDialogFieldByName(container, name) {
1039+
if (!container || typeof container !== 'object') {
1040+
return false;
1041+
}
1042+
1043+
if (container.name === name) {
1044+
return true;
1045+
}
1046+
1047+
if (Array.isArray(container.items)) {
1048+
for (let i = 0; i < container.items.length; i++) {
1049+
if (hasDialogFieldByName(container.items[i], name)) {
1050+
return true;
1051+
}
1052+
}
1053+
}
1054+
1055+
if (Array.isArray(container.tabs)) {
1056+
for (let i = 0; i < container.tabs.length; i++) {
1057+
if (hasDialogFieldByName(container.tabs[i], name)) {
1058+
return true;
1059+
}
1060+
}
1061+
}
1062+
1063+
return false;
1064+
}
1065+
1066+
function isLinkDialogSpec(spec) {
1067+
if (!spec || typeof spec !== 'object' || !spec.body || typeof spec.onSubmit !== 'function') {
1068+
return false;
1069+
}
1070+
1071+
return hasDialogFieldByName(spec.body, 'url')
1072+
&& hasDialogFieldByName(spec.body, 'target')
1073+
&& hasDialogFieldByName(spec.body, 'rel');
1074+
}
1075+
1076+
function normalizeRelForTarget(relValue, targetValue) {
1077+
let rel = String(relValue || '').trim();
1078+
if (targetValue !== '_blank') {
1079+
return rel;
1080+
}
1081+
1082+
let parts = rel === '' ? [] : rel.toLowerCase().split(/\s+/).filter(Boolean);
1083+
if (parts.indexOf('noopener') === -1) {
1084+
parts.push('noopener');
1085+
}
1086+
if (parts.indexOf('noreferrer') === -1) {
1087+
parts.push('noreferrer');
1088+
}
1089+
1090+
return parts.join(' ');
1091+
}
1092+
1093+
if (editor.windowManager && typeof editor.windowManager.open === 'function') {
1094+
const originalWindowOpen = editor.windowManager.open.bind(editor.windowManager);
1095+
editor.windowManager.open = function(spec, params) {
1096+
if (!isLinkDialogSpec(spec)) {
1097+
return originalWindowOpen(spec, params);
1098+
}
1099+
1100+
const originalSubmit = spec.onSubmit;
1101+
const wrappedSpec = Object.assign({}, spec, {
1102+
onSubmit: function(api) {
1103+
let data = api && typeof api.getData === 'function' ? api.getData() : {};
1104+
pendingLinkDialogAttrs = {
1105+
target: Object.prototype.hasOwnProperty.call(data, 'target') ? String(data.target || '') : null,
1106+
rel: Object.prototype.hasOwnProperty.call(data, 'rel') ? String(data.rel || '') : null
1107+
};
1108+
1109+
return originalSubmit.call(this, api);
1110+
}
1111+
});
1112+
1113+
return originalWindowOpen(wrappedSpec, params);
1114+
};
1115+
}
1116+
1117+
editor.on('ExecCommand', function(event) {
1118+
if (!event || event.command !== 'mceInsertLink' || !pendingLinkDialogAttrs) {
1119+
return;
1120+
}
1121+
1122+
const attrs = pendingLinkDialogAttrs;
1123+
pendingLinkDialogAttrs = null;
1124+
1125+
const linkNode = editor.dom.getParent(editor.selection.getNode(), 'a[href]');
1126+
if (!linkNode) {
1127+
return;
1128+
}
1129+
1130+
if (attrs.target !== null) {
1131+
if (attrs.target === '') {
1132+
linkNode.removeAttribute('target');
1133+
} else {
1134+
linkNode.setAttribute('target', attrs.target);
1135+
}
1136+
}
1137+
1138+
if (attrs.rel !== null) {
1139+
const effectiveTarget = attrs.target === null ? String(linkNode.getAttribute('target') || '') : attrs.target;
1140+
const normalizedRel = normalizeRelForTarget(attrs.rel, effectiveTarget);
1141+
1142+
if (normalizedRel === '') {
1143+
linkNode.removeAttribute('rel');
1144+
} else {
1145+
linkNode.setAttribute('rel', normalizedRel);
1146+
}
1147+
}
1148+
});
1149+
10291150
editor.ui.registry.addIcon('phonelink', '<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M6.6 10.8c1.6 3.1 3.5 5 6.6 6.6l2.2-2.2c.3-.3.7-.4 1.1-.3 1.2.4 2.5.6 3.8.6.6 0 1 .4 1 1V20c0 .6-.4 1-1 1C10.1 21 3 13.9 3 5c0-.6.4-1 1-1h3.5c.6 0 1 .4 1 1 0 1.3.2 2.6.6 3.8.1.4 0 .8-.3 1.1l-2.2 2.2z"/></svg>');
10301151

10311152
editor.ui.registry.addButton('phonelink', {

assets/scripts/tinymce/plugins/for_footnotes/plugin.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)