Changes for page InplaceEditing
From version 3.1
edited by Nazzareno Pompei
on 22/12/2021 09:31
on 22/12/2021 09:31
Change comment:
Install extension [org.xwiki.platform:xwiki-platform-edit-ui/13.10.1]
Summary
-
Page properties (2 modified, 0 added, 0 removed)
-
Objects (3 modified, 1 added, 0 removed)
Details
- Page properties
-
- Author
-
... ... @@ -1,1 +1,1 @@ 1 -XWiki.N azzarenoPompei1 +XWiki.NPompei - Content
-
... ... @@ -9,15 +9,11 @@ 9 9 #jsonResponse($editConfirmation) 10 10 #else 11 11 ## Lock the document for editing. 12 - #set ($ lockParams= {12 + #set ($discard = $response.sendRedirect($tdoc.getURL('lock', $escapetool.url({ 13 13 'ajax': 1, 14 14 'action': $request.lockAction, 15 15 'language': $tdoc.realLocale 16 - }) 17 - #if ($request.force == 'true') 18 - #set ($lockParams.force = 1) 19 - #end 20 - #set ($discard = $response.sendRedirect($tdoc.getURL('lock', $escapetool.url($lockParams)))) 16 + })))) 21 21 #end 22 22 #end 23 23 {{/velocity}}
- XWiki.JavaScriptExtension[0]
-
- Code
-
... ... @@ -1,11 +7,9 @@ 1 -(function(config) { 2 - "use strict"; 3 - 4 -const paths = config.paths; 5 -const l10n = config.l10n; 6 - 7 7 require.config({ 8 - paths: paths.js 2 + paths: { 3 + 'actionButtons': $jsontool.serialize($xwiki.getSkinFile('js/xwiki/actionbuttons/actionButtons.js', true)), 4 + // Required in case the user needs to resolve merge conflicts on save. 5 + 'diff': $jsontool.serialize($xwiki.getSkinFile('uicomponents/viewers/diff.js')) 6 + } 9 9 }); 10 10 11 11 define('xwiki-document-api', ['jquery'], function($) { ... ... @@ -16,36 +16,13 @@ 16 16 17 17 return { 18 18 /** 19 - * @return this document's plain title 20 - */ 21 - getPlainTitle() { 22 - return $('<div/>').html(this.renderedTitle || this.title).text(); 23 - }, 24 - 25 - /** 26 26 * @return this document's real locale 27 27 */ 28 28 getRealLocale: function() { 29 - var realLocale = this.language; 30 - if (typeof realLocale !== 'string' || realLocale === '') { 31 - realLocale = this.getDefaultLocale(); 32 - } 33 - return realLocale; 20 + return this.language || (this.translations && this.translations['default']) || $('html').attr('lang'); 34 34 }, 35 35 36 36 /** 37 - * @return this document's default locale 38 - */ 39 - getDefaultLocale: function() { 40 - if (this.translations && typeof this.translations['default'] === 'string') { 41 - return this.translations['default']; 42 - } else { 43 - // The default locale is not specified. Use the UI locale. 44 - return $('html').attr('lang'); 45 - } 46 - }, 47 - 48 - /** 49 49 * @return the URL that can be used to perform the specified action on this document 50 50 */ 51 51 getURL: function(action, queryString, fragment) { ... ... @@ -80,33 +80,25 @@ 80 80 */ 81 81 render: function(forView) { 82 82 var queryString = { 83 - xpage: ' get',58 + xpage: 'rendercontent', 84 84 outputTitle: true, 60 + outputSyntax: forView ? null : 'annotatedxhtml', 85 85 language: this.getRealLocale(), 86 86 // Make sure the response is not retrieved from cache (IE11 doesn't obey the caching HTTP headers). 87 87 timestamp: new Date().getTime() 88 88 }; 89 - if (!forView) { 90 - // We need the annotated XHTML when editing in order to be able to protect the rendering transformations and to 91 - // be able to recreate the wiki syntax. 92 - queryString.outputSyntax = 'annotatedxhtml'; 93 - // Currently, only the macro transformations are protected and thus can be edited. 94 - // See XRENDERING-78: Add markers to modified XDOM by Transformations/Macros 95 - queryString.transformations = 'macro'; 96 - } 97 97 var thisXWikiDocument = this; 98 98 return $.get(this.getURL('view'), queryString).fail(function() { 99 - new XWiki.widgets.Notification(l10n['edit.inplace.page.renderFailed'], 'error'); 67 + new XWiki.widgets.Notification( 68 + $jsontool.serialize($services.localization.render('edit.inplace.page.renderFailed')), 69 + 'error' 70 + ); 100 100 }).then(function(html) { 101 - // Render succeeded. 102 102 var container = $('<div/>').html(html); 103 103 return $.extend(thisXWikiDocument, { 104 104 renderedTitle: container.find('#document-title h1').html(), 105 105 renderedContent: container.find('#xwikicontent').html() 106 106 }); 107 - }, function() { 108 - // Render failed. 109 - return thisXWikiDocument; 110 110 }); 111 111 }, 112 112 ... ... @@ -121,15 +121,11 @@ 121 121 // Make sure the response is not retrieved from cache (IE11 doesn't obey the caching HTTP headers). 122 122 timestamp: new Date().getTime() 123 123 }).then(function(newXWikiDocument) { 124 - // Reload succeeded. 125 125 // Resolve the document reference. 126 126 thisXWikiDocument.documentReference = XWiki.Model.resolve(newXWikiDocument.id, XWiki.EntityType.DOCUMENT); 127 127 // We were able to load the document so it's not new. 128 128 thisXWikiDocument.isNew = false; 129 129 return $.extend(thisXWikiDocument, newXWikiDocument); 130 - }, function() { 131 - // Reload failed. 132 - return thisXWikiDocument; 133 133 }); 134 134 }, 135 135 ... ... @@ -152,19 +152,8 @@ 152 152 // Make sure the response is not retrieved from cache (IE11 doesn't obey the caching HTTP headers). 153 153 timestamp: new Date().getTime() 154 154 }).then(function() { 155 - // Lock succeeded. 156 156 thisXWikiDocument.locked = action; 157 157 return thisXWikiDocument; 158 - }, function(response) { 159 - // Lock failed. 160 - delete thisXWikiDocument.locked; 161 - // Check if the user can force the lock. 162 - var lockConfirmation = response.responseJSON; 163 - if (response.status === 423 && lockConfirmation) { 164 - // The user can force the lock, but needs confirmation. 165 - thisXWikiDocument.lockConfirmation = lockConfirmation; 166 - } 167 - return thisXWikiDocument; 168 168 }); 169 169 }, 170 170 ... ... @@ -187,25 +187,6 @@ 187 187 // See https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Synchronous_and_Asynchronous_Requests 188 188 $.ajax({type: 'GET', url: url, async: false}); 189 189 } 190 - }, 191 - 192 - /** 193 - * Makes sure this document matches the current UI locale. 194 - */ 195 - translate: function() { 196 - const realLocale = this.getRealLocale(); 197 - const uiLocale = $('html').attr('lang'); 198 - if (realLocale && realLocale !== uiLocale) { 199 - this.language = uiLocale; 200 - // Set the original document locale. 201 - this.translations = this.translations || {}; 202 - this.translations['default'] = realLocale; 203 - // Update the document fields that are not 'shared' with the original document. 204 - this.isNew = true; 205 - delete this.version; 206 - delete this.majorVersion; 207 - delete this.minorVersion; 208 - } 209 209 } 210 210 }; 211 211 }); ... ... @@ -222,7 +222,9 @@ 222 222 'xwiki-events-bridge' 223 223 ], function($, xcontext, xwikiDocumentAPI) { 224 224 var preload = function() { 225 - paths.css.forEach(loadCSS); 158 + loadCSS($jsontool.serialize($xwiki.getSkinFile('js/xwiki/actionbuttons/actionButtons.css', true))); 159 + // Required in case the user needs to resolve merge conflicts on save. 160 + loadCSS($jsontool.serialize($xwiki.getSkinFile('uicomponents/viewers/diff.css', true))); 226 226 return initActionButtons(); 227 227 }; 228 228 ... ... @@ -242,12 +242,6 @@ 242 242 }); 243 243 }; 244 244 245 - var translatePage = function() { 246 - return editInPlace({ 247 - afterEdit: createTranslation 248 - }); 249 - }; 250 - 251 251 var editSection = function(sectionId) { 252 252 return editInPlace({ 253 253 lockFailed: function() { ... ... @@ -296,38 +296,26 @@ 296 296 lockFailed: function() {} 297 297 }, options); 298 298 $('#xwikicontent').addClass('loading'); 299 - // Lock the document first. 300 300 return lock(currentXWikiDocument).fail(options.lockFailed) 301 - // Then load the document only if we managed to lock it. 302 302 .then(load) 303 - // Then load the editors only if we managed to load the document. 304 304 .then(edit).done(options.afterEdit).always(function() { 305 305 $('#xwikicontent').removeClass('loading'); 306 - // Then wait for an action (save, cancel, reload) only if the editors were loaded successfuly. 307 307 }).then(maybeSave) 308 - // Then unlock the document both when the edit ended with success and with a failure. 309 - .then(unlock, unlock) 310 - // Finally view the document both when the edit ended with success and with a failure. 311 - .then(view, view) 312 - .always(function(xwikiDocument) { 313 - // Update the current document for the next edit session. 314 - currentXWikiDocument = xwikiDocument; 315 - }); 233 + .then(unlock) 234 + .then(view); 316 316 }; 317 317 318 318 var lock = function(xwikiDocument) { 319 - return xwikiDocument.lock().then(null, function(xwikiDocument) { 238 + return xwikiDocument.lock().then(null, function(response) { 239 + var confirmation = response.responseJSON; 320 320 // If the document was already locked then we need to ask the user if they want to force the lock. 321 - if (xwikiDocument.lockConfirmation) { 322 - var confirmation = xwikiDocument.lockConfirmation; 323 - delete xwikiDocument.lockConfirmation; 324 - return maybeForceLock(confirmation).then($.proxy(xwikiDocument, 'lock', 'edit', true), function() { 325 - // Cancel the edit action. 326 - return xwikiDocument; 327 - }); 241 + if (response.status === 423 && confirmation) { 242 + return maybeForceLock(confirmation).then($.proxy(xwikiDocument, 'lock', 'edit', true)); 328 328 } else { 329 - new XWiki.widgets.Notification(l10n['edit.inplace.page.lockFailed'], 'error'); 330 - return xwikiDocument; 244 + new XWiki.widgets.Notification( 245 + $jsontool.serialize($services.localization.render('edit.inplace.page.lockFailed')), 246 + 'error' 247 + ); 331 331 } 332 332 }); 333 333 }; ... ... @@ -375,7 +375,7 @@ 375 375 '</div>', 376 376 '</div>' 377 377 ].join('')); 378 - modal.find('.close').attr('aria-label', l 10n['edit.inplace.close']);295 + modal.find('.close').attr('aria-label', $jsontool.serialize($services.localization.render('edit.inplace.close'))); 379 379 modal.find('.modal-footer .btn-warning').click(function() { 380 380 // The user has confirmed they want to force the lock. 381 381 modal.data('deferred').resolve(); ... ... @@ -400,7 +400,8 @@ 400 400 renderedContent: $('#xwikicontent').html() 401 401 }, xwikiDocument); 402 402 }).fail(function() { 403 - new XWiki.widgets.Notification(l10n['edit.inplace.page.loadFailed'], 'error'); 320 + new XWiki.widgets.Notification($jsontool.serialize($services.localization.render('edit.inplace.page.loadFailed')), 321 + 'error'); 404 404 // Render the document for edit, in order to have the annotated content HTML. The annotations are used to protect 405 405 // the rendering transformations (e.g. macros) when editing the content. 406 406 }).then($.proxy(render, null, false)); ... ... @@ -439,7 +439,7 @@ 439 439 'xwiki:actions:save', 440 440 'xwiki:actions:reload', 441 441 'xwiki:actions:cancel', 442 - ].join(' '), '.xcontent.form', function(event, data) {360 + ].join(' '), function(event, data) { 443 443 deferred.resolve({ 444 444 name: event.type.substring('xwiki:actions:'.length), 445 445 document: xwikiDocument, ... ... @@ -450,18 +450,24 @@ 450 450 }; 451 451 452 452 var save = function(data) { 453 - // Push the changes to the server. 454 - return push(data.document).then(function(xwikiDocument) { 371 + // Push the changes to the server then render the document for view. We need the view HTML both if we stop editing 372 + // now and if we continue but cancel the edit later. 373 + return push(data.document).then($.proxy(render, null, true)).then(function(xwikiDocument) { 455 455 // Save succeeded. 456 - return shouldReload(xwikiDocument).then( 457 - // The document was saved with merge and thus if we want to continue eding we need to reload the editor (because 458 - // its content doesn't match the saved content). 459 - reload, 460 - // No need to reload the editor because either the action was Save & View or there was no merge on save. 461 - $.proxy(maybeContinueEditing, null, data['continue']) 462 - ); 463 - // Save failed. Continue editing because we may have unsaved content. 464 - }, maybeSave); 375 + if (data['continue']) { 376 + // Update the original version in order to be able to restore it on cancel. 377 + delete xwikiDocument.originalDocument; 378 + xwikiDocument.originalDocument = $.extend(true, {}, xwikiDocument); 379 + // Continue editing. 380 + return maybeSave(xwikiDocument); 381 + } else { 382 + // This is the final version. 383 + return xwikiDocument; 384 + } 385 + }, function(xwikiDocument) { 386 + // Save failed. Continue editing. 387 + return maybeSave(xwikiDocument); 388 + }); 465 465 }; 466 466 467 467 var push = function(xwikiDocument) { ... ... @@ -472,35 +472,11 @@ 472 472 // once it was resolved or rejected. So the first event that fires will resolve/reject the promise and the remaining 473 473 // event won't be able to change that. The remaining event listener could be called later but it won't have any 474 474 // effect. 475 - $(document).one('xwiki:document:saved', '.xcontent.form',$.proxy(deferred, 'resolve', xwikiDocument));476 - $(document).one('xwiki:document:saveFailed', '.xcontent.form',$.proxy(deferred, 'reject', xwikiDocument));477 - return deferred.promise(); 399 + $(document).one('xwiki:document:saved', $.proxy(deferred, 'resolve', xwikiDocument)); 400 + $(document).one('xwiki:document:saveFailed', $.proxy(deferred, 'reject', xwikiDocument)); 401 + return deferred.promise().then($.proxy(xwikiDocument, 'reload')); 478 478 }; 479 479 480 - var maybeContinueEditing = function(continueEditing, xwikiDocument) { 481 - var afterReloadAndRender = function(success, xwikiDocument) { 482 - if (continueEditing) { 483 - if (success) { 484 - // Update the original version in order to be able to restore it on cancel. 485 - delete xwikiDocument.originalDocument; 486 - xwikiDocument.originalDocument = $.extend(true, {}, xwikiDocument); 487 - } 488 - // Continue editing. 489 - return maybeSave(xwikiDocument); 490 - } else { 491 - // This is the final version. We stop editing even if the reload / render failed. 492 - return xwikiDocument; 493 - } 494 - }; 495 - 496 - // Reload the document JSON data (to have the new version) and render the document for view. We need the view HTML 497 - // both if we stop editing now and if we continue but cancel the edit later. 498 - return xwikiDocument.reload().then($.proxy(render, null, true)).then( 499 - $.proxy(afterReloadAndRender, null, /* success: */ true), 500 - $.proxy(afterReloadAndRender, null, /* success: */ false) 501 - ); 502 - }; 503 - 504 504 var cancel = function(xwikiDocument) { 505 505 // Simply return the original version to be restored. 506 506 return xwikiDocument.originalDocument; ... ... @@ -518,25 +518,6 @@ 518 518 // Make sure we unlock the document when the user navigates to another page. 519 519 $(window).on('unload pagehide', $.proxy(unlock, null, currentXWikiDocument)); 520 520 521 - var shouldReload = function(xwikiDocument) { 522 - var reloadEventFired = false; 523 - $(document).one('xwiki:actions:reload.maybe', '.xcontent.form', function() { 524 - reloadEventFired = true; 525 - }); 526 - var deferred = $.Deferred(); 527 - // Wait a bit to see if the reload event is fired. 528 - setTimeout(function() { 529 - // Remove the listener in case the reload event wasn't fired. 530 - $(document).off('xwiki:actions:reload.maybe'); 531 - if (reloadEventFired) { 532 - deferred.resolve(xwikiDocument); 533 - } else { 534 - deferred.reject(xwikiDocument); 535 - } 536 - }, 0); 537 - return deferred.promise(); 538 - }; 539 - 540 540 var reload = function(xwikiDocument) { 541 541 // Leave the edit mode and then re-enter. 542 542 return view(xwikiDocument, true).then(editInPlace); ... ... @@ -543,33 +543,21 @@ 543 543 }; 544 544 545 545 var view = function(xwikiDocument, reload) { 546 - var viewContent = $('#xwikicontent'); 547 547 // Destroy the editors before returning to view. 548 - viewContent.trigger('xwiki:actions:view', {document: xwikiDocument});428 + $(document).trigger('xwiki:actions:view', {document: xwikiDocument}); 549 549 $('#document-title h1').html(xwikiDocument.renderedTitle); 550 - viewContent.html(xwikiDocument.renderedContent);430 + $('#xwikicontent').html(xwikiDocument.renderedContent); 551 551 if (!reload) { 552 552 // If the user has canceled the edit then the restored page content may include the section edit links. Show them 553 553 // in case they were hidden. 554 - viewContent.children(':header').children('.edit_section').removeClass('hidden');434 + $('#xwikicontent').children(':header').children('.edit_section').removeClass('hidden'); 555 555 // Let others know that the DOM has been updated, in order to enhance it. 556 - $(document).trigger('xwiki:dom:updated', {'elements': viewContent.toArray()});436 + $(document).trigger('xwiki:dom:updated', {'elements': $('#xwikicontent').toArray()}); 557 557 } 558 - // Remove the action events scope. 559 - viewContent.closest('.form').removeClass('form'); 560 - // Update the URL. 561 - if (window.location.hash === '#edit' || window.location.hash === '#translate') { 562 - history.replaceState(null, null, '#'); 563 - } 564 564 return $.Deferred().resolve(xwikiDocument).promise(); 565 565 }; 566 566 567 567 var edit = function(xwikiDocument) { 568 - // By adding the 'form' CSS class we set the scope of the action events (e.g. xwiki:actions:beforeSave or 569 - // xwiki:actions:cancel). We need this because in view mode we can have multiple forms active on the page (e.g. one 570 - // for editing the document content in place and one for editing the document syntax in-place) and we don't want 571 - // them to interfere (e.g. canceling one form shouldn't cancel the other forms). 572 - $('#xwikicontent').closest('.xcontent').addClass('form'); 573 573 return initActionButtons(xwikiDocument).then(initTitleEditor).then(initContentEditor) 574 574 .then(startRealTimeEditingSession); 575 575 }; ... ... @@ -576,7 +576,7 @@ 576 576 577 577 var initActionButtons = function(xwikiDocument) { 578 578 if (xwikiDocument) { 579 - initTranslateButton(xwikiDocument);448 + maybeShowTranslateButton(xwikiDocument); 580 580 } 581 581 var editContent = $('#xwikicontent'); 582 582 // We need the wrapper because #xwikicontent uses Bootstrap grid (col-xs-12) which is implemented with CSS float. ... ... @@ -586,86 +586,59 @@ 586 586 var actionButtonsWrapper = editContent.nextAll('.sticky-buttons-wrapper'); 587 587 if (actionButtonsWrapper.length === 0) { 588 588 actionButtonsWrapper = $('<div class="sticky-buttons-wrapper col-xs-12">' + 589 - '<div class="inplace-editing-buttons sticky-buttons"/></div>').insertAfter(editContent).toggle(!!xwikiDocument); 590 - var actionButtons = actionButtonsWrapper.children('.sticky-buttons') 591 - .data('xwikiDocument', xwikiDocument) 592 - // Expose the fake form if an extension needs to manipulate it. 593 - .data('fakeForm', fakeForm); 458 + '<div class="inplace-editing-buttons sticky-buttons"/></div>').insertAfter(editContent); 459 + var actionButtons = actionButtonsWrapper.children('.sticky-buttons').data('xwikiDocument', xwikiDocument) 460 + .toggle(!!xwikiDocument); 594 594 return loadActionButtons(actionButtons); 595 595 } else { 596 596 // If we're editing a page.. 597 597 if (xwikiDocument) { 598 598 // ..then make sure the action buttons are displayed right away (don't wait for the user to scroll). 599 - actionButtonsWrapper.show().children('.sticky-buttons') 600 - .data('xwikiDocument', xwikiDocument) 601 - // Expose the fake form if an extension needs to manipulate it. 602 - .data('fakeForm', fakeForm) 603 - // but make sure the position of the action buttons is updated. 604 - .trigger('xwiki:dom:refresh'); 466 + actionButtonsWrapper.children('.sticky-buttons').data('xwikiDocument', xwikiDocument).show(); 605 605 // The action buttons are disabled on Save & View. We don't reload the page on Save & View and we reuse the 606 606 // action buttons so we need to re-enable them each time we enter the edit mode. 607 607 fakeForm.enable(); 470 + $(document).trigger('xwiki:dom:refresh'); 608 608 } 609 609 return $.Deferred().resolve(xwikiDocument).promise(); 610 610 } 611 611 }; 612 612 613 - var createTranslation = function(xwikiDocument) { 614 - xwikiDocument.translate(); 615 - $('#document-title-input').focus().select(); 616 - // Let the user know that they are now editing the translation of this page in the current locale. 617 - $('#document-title-input').popover({ 618 - content: l10n['edit.inplace.page.translate.messageAfter'], 619 - placement: 'bottom', 620 - trigger: 'manual' 621 - }).popover('show').one('blur', function() { 622 - // Hide the popover when the title input loses the focus. 623 - $(this).popover('hide'); 624 - }); 476 + var maybeShowTranslateButton = function(xwikiDocument) { 477 + var xwikiDocumentLocale = xwikiDocument.getRealLocale(); 478 + var uiLocale = $('html').attr('lang'); 479 + if (xwikiDocumentLocale && xwikiDocumentLocale !== uiLocale) { 480 + $('#tmTranslate').off('click.translate').on('click.translate', function(event) { 481 + event.preventDefault(); 482 + $(this).addClass('hidden'); 483 + xwikiDocument.language = uiLocale; 484 + // Update the document translation fields that are not 'shared' with the original document. 485 + xwikiDocument.isNew = true; 486 + delete xwikiDocument.version; 487 + delete xwikiDocument.majorVersion; 488 + delete xwikiDocument.minorVersion; 489 + $('#document-title-input').focus().select(); 490 + var message = $jsontool.serialize($services.localization.render('edit.inplace.page.translation', 491 + ['__locale__'])); 492 + new XWiki.widgets.Notification( 493 + message.replace('__locale__', uiLocale), 494 + 'info' 495 + ); 496 + }).removeClass('hidden'); 497 + var message = $jsontool.serialize($services.localization.render('edit.inplace.page.original', ['__locale__'])); 498 + new XWiki.widgets.Notification( 499 + message.replace('__locale__', xwikiDocumentLocale), 500 + 'info' 501 + ); 502 + } 625 625 }; 626 626 627 - var initTranslateButton = function(xwikiDocument) { 628 - // Initialize the translate button only if it's visible. 629 - const translateButton = $(config.translateButtonSelector).filter('[data-toggle="popover"]').filter(':visible'); 630 - translateButton.off('click.translate').on('click.translate', function(event) { 631 - event.preventDefault(); 632 - translateButton.parent().addClass('hidden'); 633 - createTranslation(xwikiDocument); 634 - // Let the user know that they are editing the original version of the page and not the translation corresponding 635 - // to the current locale because there isn't one created yet. 636 - }).attr({ 637 - // Backup the initial popover message to be able to restore it on view. 638 - 'data-content-view': translateButton.attr('data-content'), 639 - // Use a custom popover message dedicated to the edit action. 640 - 'data-content': l10n['edit.inplace.page.translate.messageBefore'] 641 - }).popover('show') 642 - // Hide the popover on the next click. The user can still see the message by hovering the translate button. 643 - .closest('html').one('click', function() { 644 - translateButton.popover('hide'); 645 - }); 646 - }; 647 - 648 648 var loadActionButtons = function(actionButtons) { 649 - $(document).on('xwiki:actions:view', '.xcontent.form', function(event, data) { 650 - // Blur the action buttons first to re-enable the "disabled in inputs" shortcut keys (e.g. the page edit 651 - // shortcut), then disable the action buttons in order to disable their shortcut keys while we're not editing 652 - // in-place (e.g. prevent the Save shortcut while the user is only viewing the page). Finally hide the action 653 - // buttons to have them ready for the next editing session (the user can save or cancel and then edit again 654 - // without reloading the page). 655 - actionButtons.find(':input').blur().prop('disabled', true).end().parent().hide(); 656 - // Restore the Translate button if the locale of the viewed document doesn't match the current user interface 657 - // locale (because the viewed document doesn't have a translation in the current locale). 658 - var xwikiDocumentLocale = data.document.getRealLocale(); 659 - var uiLocale = $('html').attr('lang'); 660 - if (xwikiDocumentLocale && xwikiDocumentLocale !== uiLocale) { 661 - const translateButton = $(config.translateButtonSelector).filter('[data-toggle="popover"]'); 662 - // Restore the translation button behavior for view action. 663 - translateButton.off('click.translate') 664 - // Restore the popover text for view action. 665 - .attr('data-content', translateButton.attr('data-content-view') || translateButton.attr('data-content')) 666 - // Restore the visibility. 667 - .parent().removeClass('hidden'); 668 - } 506 + $(document).on('xwiki:actions:view', function() { 507 + // Hide the action buttons and disable the shortcut keys (by disabling the buttons). 508 + actionButtons.hide().find(':input').prop('disabled', true); 509 + // Hide the translate button because it can be used only in edit mode for the moment. 510 + $('#tmTranslate').addClass('hidden'); 669 669 }); 670 670 return $.get(XWiki.currentDocument.getURL('get'), { 671 671 xpage: 'editactions' ... ... @@ -677,16 +677,10 @@ 677 677 $('<input type="hidden" name="form_token" />').val(xcontext.form_token).appendTo(actionButtons); 678 678 // We need a place where actionButtons.js can add more hidden inputs. 679 679 actionButtons.append('<div class="hidden extra"/>'); 680 - // Let the others know that the DOM has been updated, in order to enhance it. 681 - $(document).trigger('xwiki:dom:updated', {'elements': actionButtons.toArray()}); 682 682 var deferred = $.Deferred(); 683 - require([' xwiki-actionButtons', 'xwiki-diff', 'xwiki-autoSave'], function() {523 + require(['actionButtons'], function() { 684 684 overrideEditActions(); 685 685 overrideAjaxSaveAndContinue(); 686 - // Activate the auto-save feature passing our fake edit form. Note that autosave.js also creates an instance of 687 - // AutoSave but it doesn't do anything because it doesn't find a real edit form in the page. This is why we have 688 - // to create our own instance of AutoSave passing the right (fake) form. 689 - new XWiki.editors.AutoSave({form: fakeForm}); 690 690 var xwikiDocument = actionButtons.data('xwikiDocument'); 691 691 // Enable the action buttons (and their shortcut keys) only if we're editing a document. 692 692 actionButtons.find(':input').prop('disabled', !xwikiDocument); ... ... @@ -694,7 +694,10 @@ 694 694 }); 695 695 return deferred.promise(); 696 696 }, function() { 697 - new XWiki.widgets.Notification(l10n['edit.inplace.actionButtons.loadFailed'], 'error'); 533 + new XWiki.widgets.Notification( 534 + $jsontool.serialize($services.localization.render('edit.inplace.actionButtons.loadFailed')), 535 + 'error' 536 + ); 698 698 }); 699 699 }; 700 700 ... ... @@ -720,13 +720,6 @@ 720 720 insert: function(element) { 721 721 this._getActionButtons().find('.hidden.extra').append(element); 722 722 }, 723 - // Note that this method only works with single argument. 724 - append: function(element) { 725 - this.insert(element); 726 - }, 727 - down: function(selector) { 728 - return this._getActionButtons().find(selector)[0]; 729 - }, 730 730 serialize: function() { 731 731 var extra = this._getActionButtons().find(':input').serializeArray().reduce(function(extra, entry) { 732 732 var value = extra[entry.name] || []; ... ... @@ -736,28 +736,20 @@ 736 736 }, {}); 737 737 var xwikiDocument = this._getActionButtons().data('xwikiDocument'); 738 738 var formData = { 739 - title: xwikiDocument.rawTitle, 571 + title: xwikiDocument.title, 572 + content: xwikiDocument.renderedContent, 573 + RequiresHTMLConversion: 'content', 574 + content_syntax: xwikiDocument.syntax, 740 740 language: xwikiDocument.getRealLocale(), 741 741 isNew: xwikiDocument.isNew 742 742 }; 743 - if (xwikiDocument.content != xwikiDocument.originalDocument.content) { 744 - // Submit the raw (source) content. No syntax conversion is needed in this case. 745 - formData.content = xwikiDocument.content; 746 - } else { 747 - // Submit the rendered content (HTML), but make sure it is converted to the document syntax on the server. 748 - $.extend(formData, { 749 - content: xwikiDocument.renderedContent, 750 - RequiresHTMLConversion: 'content', 751 - content_syntax: xwikiDocument.syntax 752 - }); 753 - } 754 754 // Check for merge conflicts only if the document is not new and we know the current version. 755 755 if (!xwikiDocument.isNew && xwikiDocument.version) { 756 756 formData.previousVersion = xwikiDocument.version; 757 - formData.editingVersionDate = new Date(xwikiDocument.modified).getTime(); 581 + // It would have been easier to send the timestamp but that's what the Save action expects. 582 + formData.editingVersionDate = new Date(xwikiDocument.modified).toISOString(); 758 758 } 759 - // Ensure that formData information has priority over extra information. 760 - return $.extend({}, extra, formData); 584 + return $.extend(formData, extra); 761 761 } 762 762 }; 763 763 ... ... @@ -779,21 +779,17 @@ 779 779 var originalAjaxSaveAndContinue = $.extend({}, XWiki.actionButtons.AjaxSaveAndContinue.prototype); 780 780 $.extend(XWiki.actionButtons.AjaxSaveAndContinue.prototype, { 781 781 reloadEditor: function() { 782 - var actionButtons = $('.inplace-editing-buttons'); 783 - if (actionButtons.is(':visible')) { 784 - actionButtons.trigger('xwiki:actions:reload'); 606 + if ($('.inplace-editing-buttons').is(':visible')) { 607 + $(document).trigger('xwiki:actions:reload'); 785 785 } else { 786 786 return originalAjaxSaveAndContinue.reloadEditor.apply(this, arguments); 787 787 } 788 788 }, 789 - maybeRedirect: function( continueEditing) {612 + maybeRedirect: function() { 790 790 if ($('.inplace-editing-buttons').is(':visible')) { 791 - // Overwrite the default behavior so that we don't redirect when leaving the edit mode because we're already 792 - // in view mode. We still need to report a redirect (return true) if we don't continue editing, so that 793 - // actionButtons.js behaves as if a redirect was done. 794 - return !continueEditing; 614 + // Never redirect when leaving the edit mode because we're already in view mode. 615 + return false; 795 795 } else { 796 - // Fallback on the default behavior if the in-place editing buttons are hidden. 797 797 return originalAjaxSaveAndContinue.maybeRedirect.apply(this, arguments); 798 798 } 799 799 } ... ... @@ -802,8 +802,8 @@ 802 802 803 803 var initTitleEditor = function(xwikiDocument) { 804 804 var label = $('<label for="document-title-input" class="sr-only"/>') 805 - .text(l 10n['core.editors.content.titleField.label']);806 - var input = $('<input type="text" id="document-title-input"/>').val(xwikiDocument. rawTitle);625 + .text($jsontool.serialize($services.localization.render('core.editors.content.titleField.label'))); 626 + var input = $('<input type="text" id="document-title-input"/>').val(xwikiDocument.title); 807 807 var placeholder = xwikiDocument.documentReference.name; 808 808 if (placeholder === 'WebHome') { 809 809 placeholder = xwikiDocument.documentReference.parent.name; ... ... @@ -810,13 +810,13 @@ 810 810 } 811 811 input.attr('placeholder', placeholder); 812 812 $('#document-title h1').addClass('editable').empty().append([label, input]); 813 - $(document).on('xwiki:actions:beforeSave.titleEditor', '.xcontent.form', function(event) {814 - xwikiDocument. rawTitle = input.val();633 + $(document).on('xwiki:actions:beforeSave.titleEditor', function(event) { 634 + xwikiDocument.title = input.val(); 815 815 }); 816 - $(document).one('xwiki:actions:view', '.xcontent.form', function(event, data) {636 + $(document).one('xwiki:actions:view', function(event, data) { 817 817 // Destroy the title editor. 818 818 $(document).off('xwiki:actions:beforeSave.titleEditor'); 819 - $('#document-title h1').removeClass('editable').text(xwikiDocument. rawTitle);639 + $('#document-title h1').removeClass('editable').text(xwikiDocument.title); 820 820 }); 821 821 return xwikiDocument; 822 822 }; ... ... @@ -833,24 +833,24 @@ 833 833 // Keep the focus while the edit content is being prepared. 834 834 viewContent.focus(); 835 835 } 836 - var data = $.extend({}, config, { 656 + var data = { 657 + contentType: 'org.xwiki.rendering.syntax.SyntaxContent', 658 + editMode: 'wysiwyg', 837 837 document: xwikiDocument, 838 838 // The content editor is loaded on demand, asynchronously. 839 839 deferred: $.Deferred() 840 - } );841 - editContent .trigger('xwiki:actions:edit',data);842 - returndata.deferred.done(function() {662 + }; 663 + var editContentPromise = data.deferred.promise(); 664 + editContentPromise.done(function() { 843 843 editContent.show(); 844 844 viewContent.remove(); 845 845 if (withFocus) { 846 - // Restore the focus when the edit content is ready but make sure we don't scroll the page. We don't restore the 847 - // focus right away because we just made the content visible so it may not be editable yet (e.g. the WYSIWYG 848 - // editor can make the content editable only if it is visible). 849 - setTimeout(function() { 850 - editContent[0].focus({preventScroll: true}); 851 - }, 0); 668 + // Restore the focus when the edit content is ready but make sure we don't scroll the page. 669 + editContent[0].focus({preventScroll: true}); 852 852 } 853 - }).promise(); 671 + }); 672 + editContent.trigger('xwiki:actions:edit', data); 673 + return editContentPromise; 854 854 }; 855 855 856 856 var startRealTimeEditingSession = function(xwikiDocument) { ... ... @@ -869,21 +869,16 @@ 869 869 }; 870 870 871 871 return { 872 - preload, 873 - editPage, 874 - editSection, 875 - translatePage 692 + preload: preload, 693 + editPage: editPage, 694 + editSection: editSection 876 876 }; 877 877 }); 878 878 879 879 require(['jquery'], function($) { 880 - // We can edit in-place only if the #xwikicontent element is present. 881 - if (!$('#xwikicontent').length) { 882 - return; 883 - } 699 + var inplaceEditingConfig = $('div[data-inplace-editing-config]').data('inplaceEditingConfig') || {}; 700 + var wysiwygEditorModule = 'xwiki-' + inplaceEditingConfig.wysiwygEditor + '-inline'; 884 884 885 - var wysiwygEditorModule = 'xwiki-' + config.wysiwygEditor + '-inline'; 886 - 887 887 var preloadEditor = function() { 888 888 require(['editInPlace', wysiwygEditorModule], function(editInPlace) { 889 889 editInPlace.preload(); ... ... @@ -900,186 +900,44 @@ 900 900 }); 901 901 } 902 902 903 - var onInPlaceEditing = function(event) { 718 + var editButton = $('#tmEdit > a'); 719 + editButton.on('click.inPlaceEditing', function(event) { 720 + event.preventDefault(); 904 904 // Make sure the user doesn't try to re-activate the edit mode while we are in edit mode. 905 - if (editButton.hasClass('disabled')) { 906 - return; 907 - } 908 - // Disable the edit buttons and hide the section edit links. 909 - editButton.add(translateButton).addClass('disabled'); 910 - $('#xwikicontent').children(':header').children('.edit_section').addClass('hidden'); 722 + editButton.addClass('disabled'); 723 + // Load the code needed to edit in place only when the edit button is clicked. 724 + require(['editInPlace', wysiwygEditorModule], function(editInPlace) { 725 + editInPlace.editPage().always(function() { 726 + editButton.removeClass('disabled'); 727 + }); 728 + // Fallback on the standalone edit mode if we fail to load the required modules. 729 + }, $.proxy(disableInPlaceEditing, event.target)); 730 + }); 731 + 732 + // Section in-place editing. 733 + $('#xwikicontent').on('click.inPlaceEditing', '> :header > a.edit_section:not(.disabled)', function(event) { 911 911 event.preventDefault(); 912 - const handler = event.data; 913 - const data = handler.beforeEdit?.(event); 735 + // Make sure the user doesn't try to re-activate the edit mode while we are in edit mode. 736 + editButton.addClass('disabled'); 737 + // Hide the section editing links and focus the content right away. We could have replaced the section editing icon 738 + // with a loading animation / spinner but giving instant visual feedback about what is going to happen is perceived 739 + // better by the users (it feels faster). 740 + $('#xwikicontent').attr('tabindex', '0').focus().children(':header').children('.edit_section').addClass('hidden'); 741 + var heading = $(event.target).closest(':header'); 914 914 // Load the code needed to edit in place only when the edit button is clicked. 915 915 require(['editInPlace', wysiwygEditorModule], function(editInPlace) { 916 - // Re-enable the translate button because it can be used while editing to create the missing translation. 917 - translateButton.removeClass('disabled'); 918 - handler.edit(editInPlace, data).always(function() { 919 - // Restore only the edit button at the end because: 920 - // * the translate button is restored (if needed) by the editInPlace module 921 - // * the section edit links are restored when the document is rendered for view 744 + editInPlace.editSection(heading.attr('id')).always(function() { 922 922 editButton.removeClass('disabled'); 923 923 }); 924 924 // Fallback on the standalone edit mode if we fail to load the required modules. 925 925 }, $.proxy(disableInPlaceEditing, event.target)); 926 - }; 749 + }); 927 927 928 928 var disableInPlaceEditing = function() { 929 - editButton. add(translateButton).off('click.inPlaceEditing').removeClass('disabled');752 + editButton.off('click.inPlaceEditing').removeClass('disabled'); 930 930 $('#xwikicontent').off('click.inPlaceEditing').removeAttr('tabindex').children(':header').children('.edit_section') 931 931 .removeClass('hidden'); 932 932 // Fallback on the standalone edit mode. 933 933 $(this).click(); 934 934 }; 935 - 936 - var editButton = $(config.editButtonSelector); 937 - editButton.on('click.inPlaceEditing', { 938 - beforeEdit: function() { 939 - history.replaceState(null, null, '#edit'); 940 - }, 941 - edit: function(editInPlace) { 942 - return editInPlace.editPage(); 943 - } 944 - }, onInPlaceEditing).attr('data-editor', 'inplace'); 945 - 946 - var translateButton = $(config.translateButtonSelector); 947 - translateButton.on('click.inPlaceEditing', { 948 - beforeEdit: function() { 949 - history.replaceState(null, null, '#translate'); 950 - translateButton.parent().addClass('hidden'); 951 - }, 952 - edit: function(editInPlace) { 953 - return editInPlace.translatePage(); 954 - } 955 - }, onInPlaceEditing); 956 - 957 - // Section in-place editing. 958 - $('#xwikicontent').on('click.inPlaceEditing', '> :header > a.edit_section:not(.disabled)', { 959 - beforeEdit: function(event) { 960 - // Focus the content right away to give the user instant visual feedback about what is going to happen. 961 - $('#xwikicontent').attr('tabindex', '0').focus(); 962 - // Return the id of the edited section. 963 - return $(event.target).closest(':header').attr('id'); 964 - }, 965 - edit: function(editInPlace, sectionId) { 966 - return editInPlace.editSection(sectionId); 967 - } 968 - }, onInPlaceEditing); 969 - 970 - if (window.location.hash === '#edit') { 971 - editButton.click(); 972 - } else if (window.location.hash === '#translate') { 973 - translateButton.click(); 974 - } 975 975 }); 976 - 977 -require(['jquery'], function($) { 978 - // Backup the document title before each editing session in order to catch changes. 979 - var previousPlainTitle; 980 - $('#xwikicontent').on('xwiki:actions:edit', function(event, data) { 981 - previousPlainTitle = data.document.getPlainTitle(); 982 - }); 983 - 984 - // Update the UI after each editing session. 985 - $(document).on('xwiki:actions:view', function(event, data) { 986 - var xwikiDocument = data.document; 987 - updateDocAuthorAndDate(xwikiDocument); 988 - updateDocExtraTabs(xwikiDocument); 989 - updateDrawer(xwikiDocument); 990 - updateContentMenu(xwikiDocument); 991 - if (xwikiDocument.getPlainTitle() !== previousPlainTitle) { 992 - updateDocTrees(xwikiDocument); 993 - updateLinks(xwikiDocument); 994 - } 995 - }); 996 - 997 - var updateDocAuthorAndDate = function(xwikiDocument) { 998 - var urlWithSelector = xwikiDocument.getURL('get', 'xpage=contentheader') + ' .xdocLastModification'; 999 - $('.xdocLastModification').load(urlWithSelector, function() { 1000 - // load() replaces the content of the specified container but we want to replace the container itself. We can't do 1001 - // this from the selector, e.g. by using '.xdocLastModification > *' because we lose the text nodes. 1002 - $(this).children().unwrap(); 1003 - }); 1004 - }; 1005 - 1006 - var updateDocExtraTabs = function(xwikiDocument) { 1007 - // Reload the selected tab and force the reload of the hidden tabs next time they are selected. 1008 - $('#docextrapanes').children().addClass('empty').empty(); 1009 - var selectedTab = $('#docExtraTabs .active[data-template]'); 1010 - if (selectedTab.length) { 1011 - var docExtraId = selectedTab.attr('id'); 1012 - docExtraId = docExtraId.substring(0, docExtraId.length - 'tab'.length); 1013 - XWiki.displayDocExtra(docExtraId, selectedTab.data('template'), false); 1014 - } 1015 - }; 1016 - 1017 - // Update the document trees (e.g. breadcrumb, navigation) if they have nodes that correspond to the edited document. 1018 - // Note that we want to update the internal tree data not just the link label. This is especially useful if we're 1019 - // going to implement refactoring operations (rename) in the document tree. 1020 - var updateDocTrees = function(xwikiDocument) { 1021 - var plainTitle = xwikiDocument.getPlainTitle(); 1022 - $('.jstree-xwiki').each(function() { 1023 - $(this).jstree?.(true)?.set_text?.('document:' + xwikiDocument.id, plainTitle); 1024 - }); 1025 - }; 1026 - 1027 - // Update the links that target the edited document and whose label matches the document title. Note that this can 1028 - // update links whose label was not generated dynamically (e.g. with server side scripting) based on the document 1029 - // title. For instance there could be links with hard-coded labels or with labels generated using a translatin key 1030 - // (like in the Applications panel). For simplicity we assume that if the link matches the document URL and its 1031 - // previous title then it needs to be updated, but this happens only at the UI level. 1032 - var updateLinks = function(xwikiDocument) { 1033 - var docURL = xwikiDocument.getURL(); 1034 - var newPlainTitle = xwikiDocument.getPlainTitle(); 1035 - // Exclude the links from the document content. 1036 - // Update the links that contain only text (no child elements) otherwise we can lose UI elements (e.g. icons). 1037 - $('a').not('#xwikicontent a').not(':has(*)').filter(function() { 1038 - var linkURL = $(this).attr('href')?.split(/[?#]/, 1)[0]; 1039 - return linkURL === docURL && $(this).text() === previousPlainTitle; 1040 - }).text(newPlainTitle); 1041 - }; 1042 - 1043 - // Update the list of available document translations in the drawer menu. This is needed for instance when a new 1044 - // translation is created using the in-place editor. 1045 - var updateDrawer = function(xwikiDocument) { 1046 - var languageMenu = $('#tmLanguages_menu'); 1047 - var locale = xwikiDocument.getRealLocale(); 1048 - // Look for the language query string parameter, either inside or at the end. 1049 - var localeSelector = 'a[href*="language=' + locale + '&"], a[href$="language=' + locale + '"]'; 1050 - // Check if the language menu is present (multilingual is on) and the document locale is not listed. 1051 - if (languageMenu.length && !languageMenu.find(localeSelector).length) { 1052 - // If we get here then it means a new document translation was created and it needs to be listed in the drawer. 1053 - $('<div/>').load(xwikiDocument.getURL('get', $.param({ 1054 - 'xpage': 'xpart', 1055 - 'vm': 'drawer.vm', 1056 - 'useLayoutVars': true 1057 - // Pass the query string from the current URL so that it gets included in the translation URL. 1058 - // XWIKI-11314: Changing the current language from the UI does not preserve the query string of the current URL 1059 - })) + '&' + location.search.substring(1) + ' #tmLanguages_menu', function() { 1060 - $(this).find('a').each(function() { 1061 - // Clean the query string. 1062 - $(this).attr('href', $(this).attr('href').replace(/&?(xpage=xpart|vm=drawer\.vm|useLayoutVars=true)/g, '') 1063 - .replace('?&', '?')); 1064 - }); 1065 - languageMenu.replaceWith($(this).children()); 1066 - }); 1067 - } 1068 - }; 1069 - 1070 - // Update the links from the content menu to point to the real document locale. This is needed especially when a new 1071 - // document translation is created in-place. 1072 - var updateContentMenu = function(xwikiDocument) { 1073 - var realLocale = xwikiDocument.getRealLocale(); 1074 - var defaultLocale = xwikiDocument.getDefaultLocale(); 1075 - if (realLocale != defaultLocale) { 1076 - var defaultLocaleRegex = new RegExp('(\\blanguage=)' + defaultLocale + '($|&|#)'); 1077 - $('#contentmenu a[href*="language=' + defaultLocale + '"]').each(function() { 1078 - $(this).attr('href', $(this).attr('href').replace(defaultLocaleRegex, '$1' + realLocale + '$2')); 1079 - }); 1080 - } 1081 - }; 1082 -}); 1083 - 1084 -})(JSON.parse(document.querySelector('[data-inplace-editing-config]')?.getAttribute('data-inplace-editing-config')) || 1085 - {}); - Parse content
-
... ... @@ -1,1 +1,1 @@ 1 - No1 +Yes
- XWiki.StyleSheetExtension[0]
-
- Code
-
... ... @@ -6,7 +6,6 @@ 6 6 margin-bottom: @line-height-computed / 4; 7 7 } 8 8 9 -@document-title-input-padding-vertical: @line-height-computed / 4 - 1; 10 10 input#document-title-input { 11 11 /* Preserve the heading styles. */ 12 12 border: 1px solid transparent; ... ... @@ -13,10 +13,9 @@ 13 13 box-shadow: none; 14 14 color: inherit; 15 15 font-size: inherit; 16 - /* It seems it's not enough to set the line height for the text input. We also need to set its height. */ 17 - height: @font-size-document-title * @headings-line-height + 2 * (1 + @document-title-input-padding-vertical); 15 + height: auto; 18 18 line-height: @headings-line-height; 19 - padding: @ document-title-input-padding-vertical(ceil(@grid-gutter-width / 2) - 1);17 + padding: (@line-height-computed / 4 - 1) (ceil(@grid-gutter-width / 2) - 1); 20 20 width: 100%; 21 21 } 22 22 ... ... @@ -49,8 +49,3 @@ 49 49 #xwikicontent { 50 50 padding-top: @line-height-computed * 0.75; 51 51 } 52 - 53 -.sticky-buttons-wrapper { 54 - /* Leave some space for the bottom box shadow of the editing area. */ 55 - margin-top: 7px; 56 -}
- XWiki.UIExtensionClass[0]
-
- Executed Content
-
... ... @@ -1,67 +9,12 @@ 1 -{{velocity output="false"}} 2 -## TODO: Remove this when XWIKI-18511 (Add support for passing a query string when calling getSkinFile) is implemented. 3 -#macro (getSkinFileWithParams $file $params) 4 -#set ($url = $xwiki.getSkinFile($file, true)) 5 -$url#if ($url.contains('?'))&#else?#end$escapetool.url($params) 6 -#end 7 -{{/velocity}} 8 - 9 9 {{velocity}} 10 10 {{html clean="false"}} 11 -#if ($ services.edit.document.inPlaceEditingEnabled() && $hasEdit && $xcontext.action == 'view' && !$doc.isNew())3 +#if ($xcontext.action == 'view' && !$doc.isNew()) 12 12 ## We support in-place editing only for the WYSIWYG edit mode ATM. 13 13 #getDefaultDocumentEditor($defaultEditMode) 14 14 #if ($defaultEditMode == 'wysiwyg') 15 - #set ($l10nKeys = [ 16 - 'edit.inplace.page.renderFailed', 17 - 'edit.inplace.page.lockFailed', 18 - 'edit.inplace.close', 19 - 'edit.inplace.page.loadFailed', 20 - 'edit.inplace.actionButtons.loadFailed', 21 - 'core.editors.content.titleField.label', 22 - ['edit.inplace.page.translate.messageBefore', $doc.realLocale.getDisplayName($xcontext.locale), 23 - $xcontext.locale.getDisplayName($xcontext.locale)], 24 - ['edit.inplace.page.translate.messageAfter', $xcontext.locale.getDisplayName($xcontext.locale)] 25 - ]) 26 - #set ($l10n = {}) 27 - #foreach ($key in $l10nKeys) 28 - #set ($params = $key.subList(1, $key.size())) 29 - #if ($params) 30 - #set ($discard = $l10n.put($key[0], $services.localization.render($key[0], $params))) 31 - #else 32 - #set ($discard = $l10n.put($key, $services.localization.render($key))) 33 - #end 34 - #end 35 - ## See stylesheets.vm 36 - #set ($cssParams = { 37 - 'skin': $xwiki.skin, 38 - 'colorTheme': $services.model.serialize($themeDoc.documentReference, 'default') 39 - }) 40 - #set ($jsParams = {'language': $xcontext.locale}) 41 - ## We have to explicitly enable the source mode for in-line edit because the latest version of the content editor 42 - ## could be installed on an older version of XWiki where the in-place editor didn't support the source mode (so the 43 - ## content editor cannot enable the source mode by default). 44 44 #set ($inplaceEditingConfig = { 45 - 'contentType': 'org.xwiki.rendering.syntax.SyntaxContent', 46 46 'editMode': $defaultEditMode, 47 - 'wysiwygEditor': $services.edit.syntaxContent.defaultWysiwygEditor.descriptor.id, 48 - 'editButtonSelector': '#tmEdit > a', 49 - 'translateButtonSelector': '#tmTranslate > a', 50 - 'enableSourceMode': true, 51 - 'enableOfficeImport': $services.officemanager.isConnected(), 52 - 'paths': { 53 - 'js': { 54 - 'xwiki-actionButtons': "#getSkinFileWithParams('js/xwiki/actionbuttons/actionButtons.js' $jsParams)", 55 - 'xwiki-autoSave': "#getSkinFileWithParams('js/xwiki/editors/autosave.js' $jsParams)", 56 - 'xwiki-diff': $xwiki.getSkinFile('uicomponents/viewers/diff.js') 57 - }, 58 - 'css': [ 59 - "#getSkinFileWithParams('js/xwiki/actionbuttons/actionButtons.css' $cssParams)", 60 - "#getSkinFileWithParams('js/xwiki/editors/autosave.css' $cssParams)", 61 - "#getSkinFileWithParams('uicomponents/viewers/diff.css' $cssParams)" 62 - ] 63 - }, 64 - 'l10n': $l10n 9 + 'wysiwygEditor': $services.edit.syntaxContent.defaultWysiwygEditor.descriptor.id 65 65 }) 66 66 <div class="hidden" data-inplace-editing-config="$escapetool.xml($jsontool.serialize($inplaceEditingConfig))"></div> 67 67 ## We didn't move this to the file system because it uses LESS and we didn't want to include it in the skin.
- XWiki.UIExtensionClass[1]
-
- Cached
-
... ... @@ -1,0 +1,1 @@ 1 +No - Asynchronous rendering
-
... ... @@ -1,0 +1,1 @@ 1 +No - Executed Content
-
... ... @@ -1,0 +1,22 @@ 1 +{{velocity}} 2 +{{html clean="false"}} 3 +## Output the translation button if all the following conditions are met: 4 +## * multilingual is on 5 +## * we're loading the original document version 6 +## * the original document version has a locale specified (it doesn't make sense to translate technical documents) 7 +## * the current UI locale doesn't match the original document locale 8 +#if ($xwiki.isMultiLingual() && $tdoc.realLocale == $doc.realLocale && "$!doc.realLocale" != '' 9 + && $doc.realLocale != $xcontext.locale) 10 + #set ($url = $doc.getURL('edit', $escapetool.url({'language': $xcontext.locale}))) 11 + #set ($hint = $services.localization.render('edit.inplace.page.translate.hint', 12 + [$xcontext.locale.getDisplayName($xcontext.locale)])) 13 + ## We show the translate button only while editing in-place. 14 + <div class="btn-group hidden" id="tmTranslate"> 15 + <a class="btn btn-default" href="$url" role="button" title="$escapetool.xml($hint)"> 16 + $services.icon.renderHTML('translate') 17 + <span class="btn-label">$escapetool.xml($services.localization.render('edit.inplace.page.translate'))</span> 18 + </a> 19 + </div> 20 +#end 21 +{{/html}} 22 +{{/velocity}} - Extension Point ID
-
... ... @@ -1,0 +1,1 @@ 1 +org.xwiki.plaftorm.menu.content - Extension ID
-
... ... @@ -1,0 +1,1 @@ 1 +org.xwiki.plaftorm.menu.content.translate - Extension Parameters
-
... ... @@ -1,0 +1,1 @@ 1 +order=5000 - Extension Scope
-
... ... @@ -1,0 +1,1 @@ 1 +wiki