Découvrez les nouveautés de cette version : Fonctionnalités, améliorations et évolutions vous attendent ! 👉 Cliquez ici pour en savoir plus

Modifications pour le document DiagramEditSheet

Modifié par Admin le 19/03/2025 - 19:24

Depuis la version 7.1
modifié par Admin
sur 08/11/2024 - 07:52
Commentaire de modification : Install extension [com.xwiki.diagram:application-diagram/1.21]
À la version 8.1
modifié par Admin
sur 10/02/2025 - 13:05
Commentaire de modification : Install extension [com.xwiki.diagram:application-diagram/1.22.0]

Résumé

Détails

XWiki.JavaScriptExtension[2]
Code
... ... @@ -42,6 +42,32 @@
42 42   // Do nothing.
43 43   }
44 44  
45 + // Override the Graph.updateSvgLinks method to ensure that the links are preserved in the final SVG.
46 + // We need to directly modify the original method because drawio does not provide a parameter to override the default
47 + // behavior or a smaller method that only performs the replacement.
48 + Graph.prototype.updateSvgLinks = function (node, target, removeCustom) {
49 + var links = node.getElementsByTagName('a');
50 + for (var i = 0; i < links.length; i++) {
51 + if (links[i].getAttribute('target') == null) {
52 + var href = links[i].getAttribute('href');
53 + if (href == null) {
54 + href = links[i].getAttribute('xlink:href');
55 + }
56 +
57 + if (href != null) {
58 + if (target != null && /^https?:\/\//.test(href)) {
59 + // If the href starts with http or https, set the target attribute.
60 + links[i].setAttribute('target', target);
61 + } else if (this.isCustomLink(href)) {
62 + // Handle custom links
63 + let absoluteLink = $('<a/>').attr('href', diagramLinkHandler.getURLFromCustomLink(href)).prop('href');
64 + links[i].setAttribute('href', absoluteLink);
65 + }
66 + }
67 + }
68 + }
69 + };
70 +
45 45   // Overwrite Graph.getSvg in order to replace XWiki custom links with absolute URLs.
46 46   // Also fix the text fallback for viewers with no support for foreignObjects.
47 47   var originalGraphGetSVG = Graph.prototype.getSvg;
... ... @@ -101,318 +101,6 @@
101 101   return converter;
102 102   };
103 103  
104 - // Copied from the drawio version 24.5.4 to include a fix for importing a file in an empty diagram. This method should
105 - // be removed once we upgrade to a drawio version >= 24.4.0. Being a big method, the structure was altered just to
106 - // make it shorter.
107 - var importFilesNew = function(files, x, y, maxSize, fn, resultFn, filterFn, barrierFn,
108 - resizeDialog, maxBytes, resampleThreshold, ignoreEmbeddedXml, evt) {
109 - maxSize = (maxSize != null) ? maxSize : this.maxImageSize;
110 - maxBytes = (maxBytes != null) ? maxBytes : this.maxImageBytes;
111 - var crop = x != null && y != null;
112 - var resizeImages = true;
113 - x = (x != null) ? x : 0;
114 - y = (y != null) ? y : 0;
115 - // Checks if large images are imported
116 - var largeImages = false;
117 - if (!mxClient.IS_CHROMEAPP && files != null) {
118 - var thresh = resampleThreshold || this.resampleThreshold;
119 - for (var i = 0; i < files.length; i++) {
120 - if (files[i].type.substring(0, 9) !== 'image/svg' &&
121 - files[i].type.substring(0, 6) === 'image/' &&
122 - files[i].size > thresh) {
123 - largeImages = true;
124 - break;
125 - }
126 - }
127 - }
128 - var doImportFiles = mxUtils.bind(this, function() {
129 - var graph = this.editor.graph;
130 - var gs = graph.gridSize;
131 - fn = (fn != null) ? fn : mxUtils.bind(this, function(data, mimeType, x, y, w, h, filename, done, file) {
132 - try {
133 - if (data != null && data.substring(0, 10) == '<mxlibrary') {
134 - this.spinner.stop();
135 - this.loadLibrary(new LocalLibrary(this, data, filename));
136 - this.showSidebar();
137 - return null;
138 - }
139 - else {
140 - // Drop on empty file ignores drop location
141 - if (this.isCompatibleString(data) && files.length == 1 && evt != null &&
142 - evt.type == 'drop' && this.isBlankFile() && !this.canUndo()) {
143 - crop = false;
144 - x = 0;
145 - y = 0;
146 - }
147 - return this.importFile(data, mimeType, x, y, w, h, filename,
148 - done, file, crop, ignoreEmbeddedXml, evt);
149 - }
150 - }
151 - catch (e) {
152 - this.handleError(e);
153 - return null;
154 - }
155 - });
156 - resultFn = (resultFn != null) ? resultFn : mxUtils.bind(this, function(cells) {
157 - graph.setSelectionCells(cells);
158 - });
159 - if (this.spinner.spin(document.body, mxResources.get('loading'))) {
160 - var count = files.length;
161 - var remain = count;
162 - var queue = [];
163 - // Barrier waits for all files to be loaded asynchronously
164 - var barrier = mxUtils.bind(this, function(index, fnc) {
165 - queue[index] = fnc;
166 - if (--remain == 0) {
167 - this.spinner.stop();
168 - if (barrierFn != null) {
169 - barrierFn(queue);
170 - }
171 - else {
172 - var cells = [];
173 - graph.getModel().beginUpdate();
174 - try {
175 - for (var j = 0; j < queue.length; j++) {
176 - var tmp = queue[j]();
177 - if (tmp != null) {
178 - cells = cells.concat(tmp);
179 - }
180 - }
181 - }
182 - finally {
183 - graph.getModel().endUpdate();
184 - }
185 - }
186 - resultFn(cells);
187 - }
188 - });
189 - for (var i = 0; i < count; i++) {
190 - (mxUtils.bind(this, function(index) {
191 - var file = files[index];
192 - if (file != null) {
193 - var reader = new FileReader();
194 - reader.onload = mxUtils.bind(this, function(e) {
195 - if (filterFn == null || filterFn(file)) {
196 - try {
197 - if (file.type.substring(0, 6) == 'image/') {
198 - if (file.type.substring(0, 9) == 'image/svg') {
199 - // Checks if SVG contains content attribute
200 - var data = Graph.clipSvgDataUri(e.target.result);
201 - var comma = data.indexOf(',');
202 - var svgText = decodeURIComponent(escape(atob(data.substring(comma + 1))));
203 - var root = mxUtils.parseXml(svgText);
204 - var svgs = root.getElementsByTagName('svg');
205 - if (svgs.length > 0) {
206 - var svgRoot = svgs[0];
207 - var cont = (ignoreEmbeddedXml) ? null : svgRoot.getAttribute('content');
208 - if (cont != null && cont.charAt(0) != '<' && cont.charAt(0) != '%') {
209 - cont = unescape((window.atob) ? atob(cont) : Base64.decode(cont, true));
210 - }
211 - if (cont != null && cont.charAt(0) == '%') {
212 - cont = decodeURIComponent(cont);
213 - }
214 - if (cont != null && (cont.substring(0, 8) === '<mxfile ' ||
215 - cont.substring(0, 14) === '<mxGraphModel ')) {
216 - barrier(index, mxUtils.bind(this, function() {
217 - return fn(cont, 'text/xml', x + index * gs, y + index * gs, 0, 0, file.name);
218 - }));
219 - }
220 - else {
221 - // SVG needs special handling to add viewbox if missing and
222 - // find initial size from SVG attributes (only for IE11)
223 - barrier(index, mxUtils.bind(this, function() {
224 - try {
225 - // Parses SVG and find width and height
226 - if (root != null) {
227 - var svgs = root.getElementsByTagName('svg');
228 - if (svgs.length > 0) {
229 - var svgRoot = svgs[0];
230 - var w = svgRoot.getAttribute('width');
231 - var h = svgRoot.getAttribute('height');
232 - if (w != null && w.charAt(w.length - 1) != '%') {
233 - w = parseFloat(w);
234 - }
235 - else {
236 - w = NaN;
237 - }
238 - if (h != null && h.charAt(h.length - 1) != '%') {
239 - h = parseFloat(h);
240 - }
241 - else {
242 - h = NaN;
243 - }
244 - // Check if viewBox attribute already exists
245 - var vb = svgRoot.getAttribute('viewBox');
246 - if (vb == null || vb.length == 0) {
247 - svgRoot.setAttribute('viewBox', '0 0 ' + w + ' ' + h);
248 - }
249 - // Uses width and height from viewbox for
250 - // missing width and height attributes
251 - else if (isNaN(w) || isNaN(h)) {
252 - var tokens = vb.split(' ');
253 - if (tokens.length > 3) {
254 - w = parseFloat(tokens[2]);
255 - h = parseFloat(tokens[3]);
256 - }
257 - }
258 - data = Editor.createSvgDataUri(mxUtils.getXml(svgRoot));
259 - var s = Math.min(1, Math.min(maxSize / Math.max(1, w)), maxSize / Math.max(1, h));
260 - var cells = fn(data, file.type, x + index * gs, y + index * gs, Math.max(
261 - 1, Math.round(w * s)), Math.max(1, Math.round(h * s)), file.name);
262 - // Hack to fix width and height asynchronously
263 - if (cells != null && (isNaN(w) || isNaN(h))) {
264 - var img = new Image();
265 - img.onload = mxUtils.bind(this, function() {
266 - w = Math.max(1, img.width);
267 - h = Math.max(1, img.height);
268 - cells[0].geometry.width = w;
269 - cells[0].geometry.height = h;
270 - svgRoot.setAttribute('viewBox', '0 0 ' + w + ' ' + h);
271 - data = Editor.createSvgDataUri(mxUtils.getXml(svgRoot));
272 - var semi = data.indexOf(';');
273 - if (semi > 0) {
274 - data = data.substring(0, semi) + data.substring(data.indexOf(',', semi + 1));
275 - }
276 - graph.setCellStyles('image', data, [cells[0]]);
277 - });
278 - img.src = Editor.createSvgDataUri(mxUtils.getXml(svgRoot));
279 - }
280 - return cells;
281 - }
282 - }
283 - }
284 - catch (e) {
285 - // ignores any SVG parsing errors
286 - }
287 - return null;
288 - }));
289 - }
290 - }
291 - else {
292 - barrier(index, mxUtils.bind(this, function() {
293 - return null;
294 - }));
295 - }
296 - }
297 - else {
298 - // Checks if PNG+XML is available to bypass code below
299 - var containsModel = false;
300 - if (file.type == 'image/png') {
301 - var xml = (ignoreEmbeddedXml) ? null : this.extractGraphModelFromPng(e.target.result);
302 - if (xml != null && xml.length > 0) {
303 - var img = new Image();
304 - img.src = e.target.result;
305 - barrier(index, mxUtils.bind(this, function() {
306 - return fn(xml, 'text/xml', x + index * gs, y + index * gs,
307 - img.width, img.height, file.name);
308 - }));
309 - containsModel = true;
310 - }
311 - }
312 - // Additional asynchronous step for finding image size
313 - if (!containsModel) {
314 - // Cannot load local files in Chrome App
315 - if (mxClient.IS_CHROMEAPP) {
316 - this.spinner.stop();
317 - this.showError(mxResources.get('error'), mxResources.get('dragAndDropNotSupported'),
318 - mxResources.get('cancel'), mxUtils.bind(this, function()
319 - {
320 - // Hides the dialog
321 - }), null, mxResources.get('ok'), mxUtils.bind(this, function()
322 - {
323 - // Redirects to import function
324 - this.actions.get('import').funct();
325 - })
326 - );
327 - }
328 - else {
329 - this.loadImage(e.target.result, mxUtils.bind(this, function(img) {
330 - this.resizeImage(img, e.target.result, mxUtils.bind(this, function(data2, w2, h2) {
331 - barrier(index, mxUtils.bind(this, function() {
332 - // Refuses to insert images above a certain size as they kill the app
333 - if (data2 != null && data2.length < maxBytes) {
334 - var s = (!resizeImages || !this.isResampleImageSize(
335 - file.size, resampleThreshold)) ? 1 :
336 - Math.min(1, Math.min(maxSize / w2, maxSize / h2));
337 - return fn(data2, file.type, x + index * gs, y + index * gs,
338 - Math.round(w2 * s), Math.round(h2 * s), file.name);
339 - }
340 - else {
341 - this.handleError({message: mxResources.get('imageTooBig')});
342 - return null;
343 - }
344 - }));
345 - }), resizeImages, maxSize, resampleThreshold, file.size);
346 - }), mxUtils.bind(this, function() {
347 - this.handleError({message: mxResources.get('invalidOrMissingFile')});
348 - }));
349 - }
350 - }
351 - }
352 - }
353 - else {
354 - var data = e.target.result;
355 - fn(data, file.type, x + index * gs, y + index * gs, 240, 160, file.name, function(cells) {
356 - barrier(index, function() {
357 - return cells;
358 - });
359 - }, file);
360 - }
361 - }
362 - catch (e) {
363 - // Ignores file parsing error
364 - barrier(index, mxUtils.bind(this, function() {
365 - return null;
366 - }));
367 - if (window.console != null) {
368 - console.error(e, file);
369 - }
370 - }
371 - }
372 - else {
373 - // Ignores file and decrements counter
374 - barrier(index, mxUtils.bind(this, function()
375 - {
376 - return null;
377 - }));
378 - }
379 - });
380 - // Handles special cases
381 - if (/(\.v(dx|sdx?))($|\?)/i.test(file.name) || /(\.vs(x|sx?))($|\?)/i.test(file.name)) {
382 - fn(null, file.type, x + index * gs, y + index * gs, 240, 160, file.name, function(cells) {
383 - barrier(index, function() {
384 - return cells;
385 - });
386 - }, file);
387 - }
388 - else if (file.type.substring(0, 5) == 'image' || file.type == 'application/pdf') {
389 - reader.readAsDataURL(file);
390 - }
391 - else {
392 - reader.readAsText(file);
393 - }
394 - }
395 - }))(i);
396 - }
397 - }
398 - });
399 - if (largeImages) {
400 - // Workaround for lost files array in async code
401 - var tmp = [];
402 - for (var i = 0; i < files.length; i++) {
403 - tmp.push(files[i]);
404 - }
405 - files = tmp;
406 - this.confirmImageResize(function(doResize) {
407 - resizeImages = doResize;
408 - doImportFiles();
409 - }, resizeDialog);
410 - }
411 - else {
412 - doImportFiles();
413 - }
414 - };
415 -
416 416   // Override for uploading the image as attachment instead of encode it to Base64.
417 417   var originalImportFiles = EditorUi.prototype.importFiles;
418 418   EditorUi.prototype.importFiles = function(files, x, y, maxSize, fn, resultFn, filterFn, barrierFn, resizeDialog,
... ... @@ -442,7 +442,7 @@
442 442   }
443 443   };
444 444   }
445 - importFilesNew.apply(this, importFilesArgs);
159 + originalImportFiles.apply(this, importFilesArgs);
446 446   };
447 447  
448 448   // Add support for inserting images by specifying the XWiki attachment reference.
... ... @@ -576,6 +576,15 @@
576 576   };
577 577  });
578 578  
293 +define('diagramMenuTranslations', {
294 + prefix: 'diagram.editor.menu.',
295 + keys: [
296 + // File menu.
297 + 'print.label',
298 + 'print.title'
299 + ]
300 +});
301 +
579 579  /**
580 580   * Integrates draw.io diagram editor in XWiki.
581 581   */
... ... @@ -585,11 +585,12 @@
585 585   'diagram-utils',
586 586   'diagram-url-io',
587 587   'diagram-config',
311 + 'xwiki-l10n!diagramMenuTranslations',
588 588   'diagram-graph-xml-filter',
589 589   'diagram-link-editor',
590 590   'diagram-image-editor',
591 591   'diagram-external-services'
592 -], function($, diagramStore, diagramUtils, diagramUrlIO, diagramConfig) {
316 +], function($, diagramStore, diagramUtils, diagramUrlIO, diagramConfig, l10n) {
593 593  
594 594   // These variables are used to decide if an image should be uploaded at original resolution or
595 595   // should be declined for being too big.
... ... @@ -621,6 +621,7 @@
621 621  
622 622   var fixEditorUI = function(editorUI) {
623 623   cleanMenu(editorUI);
348 + renameMenu(editorUI);
624 624   fixKeyboardShortcutsAction(editorUI);
625 625   fixEditorButtons($(editorUI.container));
626 626   removeThemeButton();
... ... @@ -660,11 +660,31 @@
660 660   var originalAddSubmenu = Menus.prototype.addSubmenu;
661 661   Menus.prototype.addSubmenu = function(name, menu, parent, label) {
662 662   var subMenu = this.get(name);
663 - if (subMenu && subMenu.visible !== false) {
664 - originalAddSubmenu.apply(this, arguments);
388 + if (subMenu && subMenu.isEnabled() !== false) {
389 + return originalAddSubmenu.apply(this, arguments);
665 665   }
666 666   };
667 667  
393 +
394 + /*
395 + * Map with all the menu items that we want to have a title.
396 + */
397 + const titleMap = new Map([
398 + ['print', l10n['print.title']]
399 + ]);
400 +
401 + /*
402 + * Update the title of the menu items.
403 + */
404 + var originalAddMenuItem = Menus.prototype.addMenuItem;
405 + Menus.prototype.addMenuItem = function(menu, key, parent, trigger, sprite, label) {
406 + let item = originalAddMenuItem.apply(this, arguments);
407 + if (item != null && titleMap.has(key)) {
408 + item.title = titleMap.get(key);
409 + }
410 + return item;
411 + };
412 +
668 668   // Remove the language picker because the diagram editor is configured to use the XWiki language.
669 669   var originalCreateMenubar = Menus.prototype.createMenubar;
670 670   Menus.prototype.createMenubar = function(container) {
... ... @@ -684,6 +684,24 @@
684 684   };
685 685  
686 686   //
432 + // Rename menu options to fit our needs.
433 + //
434 + var renameMenu = function(editorUI) {
435 + const menuItems = [
436 + // File menu
437 + ['print', l10n['print.label']]
438 + ];
439 +
440 + // Iterate over the array of tuples
441 + menuItems.forEach(function([menuKey, newLabel]) {
442 + var action = editorUI.actions.actions[menuKey];
443 + if (action) {
444 + action.label = newLabel;
445 + }
446 + });
447 + };
448 +
449 + //
687 687   // Clean the editor menu by removing the features that are not needed.
688 688   //
689 689   var cleanMenu = function(editorUI) {
... ... @@ -696,7 +696,7 @@
696 696   // Help menu
697 697   'downloadDesktop', 'useOffline',
698 698   // ExportAs
699 - 'exportHtml'
462 + 'exportHtml', 'exportPdf'
700 700   ].forEach(function(actionName) {
701 701   var action = editorUI.actions.actions[actionName];
702 702   if (action) {