diff --git a/xwiki-platform-core/xwiki-platform-ckeditor/xwiki-platform-ckeditor-plugins/src/main/webjar/xwiki-resource/entityResourcePicker.js b/xwiki-platform-core/xwiki-platform-ckeditor/xwiki-platform-ckeditor-plugins/src/main/webjar/xwiki-resource/entityResourcePicker.js index 77153b6fe8467c0d5a7eb719ae90df541df82670..1ddc189ff7a6fe8b1197e983be891d168f103c73 100644 --- a/xwiki-platform-core/xwiki-platform-ckeditor/xwiki-platform-ckeditor-plugins/src/main/webjar/xwiki-resource/entityResourcePicker.js +++ b/xwiki-platform-core/xwiki-platform-ckeditor/xwiki-platform-ckeditor-plugins/src/main/webjar/xwiki-resource/entityResourcePicker.js @@ -28,6 +28,7 @@ define('entityResourcePicker', [ ], function($, $resource, $modal, translations) { 'use strict'; + var createdNodes = {}; var createTreeElement = function(attributes) { return $(document.createElement('div')).attr($.extend({ 'class': 'ckeditor-tree jstree-no-links', @@ -78,6 +79,42 @@ define('entityResourcePicker', [ if (validateSelection($.jstree.reference(this))) { selectButton.click(); } + }).on('select_node.jstree', function (event, data) { + if (data.node.data.type === 'addDocument') { + let inst = data.instance; + inst.edit(data.node, null, function (node) { + // jshint camelcase:false + $.jstree.reference(treeElement).deselect_all(); + }); + } + }).on('xtree.runJob', function (event, promise, action, node, params) { + if (action === 'create') { + promise.then(function (promiseData) { + if (createdNodes[params.id] === undefined) { + createdNodes[params.id] = {}; + } + if (promiseData instanceof Array) { + let newNode = promiseData[0]; + createdNodes[params.id][newNode.id] = newNode; + } + }); + } + }).on('refresh_node.jstree', function (event, data) { + let parentNodeId = data.node.id; + let nodesToCreate = createdNodes[parentNodeId]; + if (nodesToCreate) { + let tree = $.jstree.reference(treeElement); + // jshint camelcase:false + let parentNode = tree.get_node(parentNodeId); + for (let createdNodeId in nodesToCreate) { + // Check that the node is not created already. + let createdNode = nodesToCreate[createdNodeId]; + if (tree.get_node(createdNode.id) === false) { + // jshint camelcase:false + tree.create_node(parentNode, createdNode, 'last', null); + } + } + } }); } else if (openToNodeId) { // jshint camelcase:false @@ -196,7 +233,9 @@ define('entityResourcePicker', [ language: $('html').attr('lang'), showAttachments: false, showTranslations: false, - showWikis: true + showWikis: true, + showAddDocument: true, + readOnly: false })), attach: new XWiki.Document('DocumentTree', 'XWiki').getURL('get', $.param({ outputSyntax: 'plain', @@ -211,7 +250,8 @@ define('entityResourcePicker', [ 'class': 'entity-resource-picker-modal', title: title, content: createTreeElement({ - 'data-url': treeURL[resourceType] + 'data-url': treeURL[resourceType], + 'data-hasContextMenu': true }), acceptLabel: translations.get('select') }); diff --git a/xwiki-platform-core/xwiki-platform-ckeditor/xwiki-platform-ckeditor-plugins/src/main/webjar/xwiki-resource/entityResourceSuggester.js b/xwiki-platform-core/xwiki-platform-ckeditor/xwiki-platform-ckeditor-plugins/src/main/webjar/xwiki-resource/entityResourceSuggester.js index 8302db1253fdf225e71578d96455e968bb350f05..2e6d0b8684b6bb189eae3921956bbbc3a39693ec 100644 --- a/xwiki-platform-core/xwiki-platform-ckeditor/xwiki-platform-ckeditor-plugins/src/main/webjar/xwiki-resource/entityResourceSuggester.js +++ b/xwiki-platform-core/xwiki-platform-ckeditor/xwiki-platform-ckeditor-plugins/src/main/webjar/xwiki-resource/entityResourceSuggester.js @@ -19,7 +19,9 @@ */ define('entityResourceSuggesterTranslationKeys', [], [ 'doc.placeholder', - 'attach.placeholder' + 'attach.placeholder', + 'create.resolvedInSpace', + 'create.fullyResolvedInput' ]); define('entityResourceSuggester', [ @@ -35,21 +37,55 @@ define('entityResourceSuggester', [ input: input, nb: 8 }).done(function(response) { + let suggestions = []; if (response.documentElement) { - var results = response.getElementsByTagName('rs'); - var suggestions = []; + let results = response.getElementsByTagName('rs'); + let containsExactMatch = false; for (var i = 0; i < results.length; i++) { - suggestions.push(convertSearchResultToResource(results.item(i), entityType, base)); + let result = convertSearchResultToResource(results.item(i), entityType, base); + containsExactMatch = result.entityReference === input; + suggestions.push(result); + } + if (!containsExactMatch) { + suggestCreateDocument(input, base, suggestions, deferred); + } else { + deferred.resolve(suggestions); } - deferred.resolve(suggestions); } else { - deferred.resolve([]); + suggestCreateDocument(input, base, suggestions, deferred); + deferred.resolve(suggestions); } }).fail(function() { deferred.resolve([]); }); }; + var suggestCreateDocument = function(input, base, suggestions, deferred) { + $.post(new XWiki.Document('LinkNameStrategyHelper', 'CKEditor').getURL('get'), { + outputSyntax: 'plain', + input: input, + base: XWiki.Model.serialize(base) + }).done(function(data) { + data.forEach(function (item) { + suggestions.push(createDocumentFromLinkNameStrategyHelperResult(item, base)); + }); + deferred.resolve(suggestions); + }).fail(function(data) { + console.error("Error while loading creation link response", data); + deferred.resolve(suggestions); + }); + }; + + var createDocumentFromLinkNameStrategyHelperResult = function (item, base) { + var entityReference = XWiki.Model.resolve(item.reference, XWiki.EntityType.DOCUMENT); + return { + reference: $resource.convertEntityReferenceToResourceReference(entityReference, base), + entityReference: entityReference, + title: translations.get('create.' + item.type), + location: item.location + }; + }; + var convertSearchResultToResource = function(result, expectedEntityType, base) { var entityReference, location, title = result.childNodes[0].nodeValue; var entityType = result.getAttribute('type'); diff --git a/xwiki-platform-core/xwiki-platform-ckeditor/xwiki-platform-ckeditor-test/xwiki-platform-ckeditor-test-docker/src/test/it/org/xwiki/ckeditor/test/ui/LinkIT.java b/xwiki-platform-core/xwiki-platform-ckeditor/xwiki-platform-ckeditor-test/xwiki-platform-ckeditor-test-docker/src/test/it/org/xwiki/ckeditor/test/ui/LinkIT.java index aa98b2745e2b232980dfe81453b83e8428b0b961..c8b78242363db573f9eaa351fce941eb5c151462 100644 --- a/xwiki-platform-core/xwiki-platform-ckeditor/xwiki-platform-ckeditor-test/xwiki-platform-ckeditor-test-docker/src/test/it/org/xwiki/ckeditor/test/ui/LinkIT.java +++ b/xwiki-platform-core/xwiki-platform-ckeditor/xwiki-platform-ckeditor-test/xwiki-platform-ckeditor-test-docker/src/test/it/org/xwiki/ckeditor/test/ui/LinkIT.java @@ -21,16 +21,22 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.openqa.selenium.Keys; import org.xwiki.ckeditor.test.po.AutocompleteDropdown; +import org.xwiki.ckeditor.test.po.LinkDialog; +import org.xwiki.ckeditor.test.po.LinkPickerModal; +import org.xwiki.ckeditor.test.po.LinkTreeElement; import org.xwiki.model.reference.DocumentReference; import org.xwiki.model.reference.LocalDocumentReference; +import org.xwiki.model.reference.SpaceReference; import org.xwiki.test.docker.junit5.TestConfiguration; import org.xwiki.test.docker.junit5.TestReference; import org.xwiki.test.docker.junit5.UITest; import org.xwiki.test.ui.TestUtils; -import org.xwiki.test.ui.po.ViewPage; + +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Test of the CKEditor Link Plugin. @@ -39,8 +45,11 @@ * @since 15.0RC1 * @since 14.10.3 */ -@UITest(extraJARs = { - "org.xwiki.platform:xwiki-platform-search-solr-query" +@UITest(properties = { + "xwikiDbHbmCommonExtraMappings=notification-filter-preferences.hbm.xml" + }, extraJARs = { + "org.xwiki.platform:xwiki-platform-search-solr-query", + "org.xwiki.platform:xwiki-platform-notifications-filters-default" }) class LinkIT extends AbstractCKEditorIT { @@ -61,12 +70,18 @@ void afterEach(TestUtils setup, TestReference testReference) } @Test - void insertLinks(TestUtils setup, TestReference testReference, TestConfiguration testConfiguration) throws Exception + @Order(1) + void insertLinks(TestUtils setup, TestReference testReference) throws Exception { // Create a sub-page with an attachment, to have something to link to. - uploadAttachment(setup, new DocumentReference("subPage", testReference.getLastSpaceReference()), "image.gif"); + String attachmentName = "image.gif"; + setup.createPage(testReference, "", ""); + DocumentReference subPage = new DocumentReference("subPage", testReference.getLastSpaceReference()); + setup.createPage(subPage, "", ""); + setup.attachFile(subPage, attachmentName, + getClass().getResourceAsStream("/ResourcePicker/" + attachmentName), false); - edit(setup, testReference); + edit(setup, testReference, false); String spaceName = testReference.getLastSpaceReference().getParent().getName(); editor.getToolBar().insertOrEditLink() @@ -87,8 +102,8 @@ void insertLinks(TestUtils setup, TestReference testReference, TestConfiguration } @Test - void useLinkShortcutWhenTargetPageNameHasSpecialCharacters(TestUtils setup, TestReference testReference, - TestConfiguration testConfiguration) throws Exception + @Order(2) + void useLinkShortcutWhenTargetPageNameHasSpecialCharacters(TestUtils setup, TestReference testReference) { // Create the link target page with special characters in its name. LocalDocumentReference childPageReference = @@ -106,12 +121,41 @@ void useLinkShortcutWhenTargetPageNameHasSpecialCharacters(TestUtils setup, Test assertSourceEquals(String.format("[[%1$s>>%1$s]] ", childPageReference.getName())); } - private ViewPage uploadAttachment(TestUtils setup, DocumentReference testReference, String attachmentName) - throws Exception + @Test + @Order(3) + void createLinkForNotExistingPage(TestUtils setup, TestReference testReference) { - ViewPage newPage = setup.createPage(testReference, "", ""); - setup.attachFile(testReference, attachmentName, - getClass().getResourceAsStream("/ResourcePicker/" + attachmentName), false); - return newPage; + setup.createPage(testReference, "", "createLinkForNotExistingPage"); + edit(setup, testReference, false); + editor.getToolBar() + .insertOrEditLink() + .setResourceValue("Foo.Bar.Buz.Test") + .createLinkOfNewPage(true) + .submit(); + editor.getRichTextArea().sendKeys(Keys.RIGHT, Keys.ENTER); + editor.getToolBar() + .insertOrEditLink() + .setResourceValue("Fa.Fi.Foo") + .createLinkOfNewPage(false) + .submit(); + editor.getRichTextArea().sendKeys(Keys.RIGHT, Keys.ENTER); + LinkDialog linkDialog = editor.getToolBar().insertOrEditLink(); + LinkPickerModal linkPickerModal = linkDialog.openDocumentPicker(); + LinkTreeElement tree = linkPickerModal.getTree(); + assertTrue(tree.hasNewPageCreation(testReference)); + tree.createNode(testReference, "SubPage"); + SpaceReference testReferenceLastSpace = new SpaceReference("SubPage", testReference.getLastSpaceReference()); + DocumentReference subPage = new DocumentReference("WebHome", testReferenceLastSpace); + tree.createNode(subPage, "Another"); + testReferenceLastSpace = new SpaceReference("Another", testReferenceLastSpace); + tree.getNode(new DocumentReference("WebHome", testReferenceLastSpace)).select(); + linkPickerModal.select(); + linkDialog.submit(); + // Verify that the content matches what we did using CKEditor. + assertSourceEquals("[[type the link label>>doc:Foo.Bar.Buz.Test]]\n" + + "\n" + + "[[type the link label>>doc:.Fa\\.Fi\\.Foo.WebHome]]\n" + + "\n" + + "[[type the link label>>doc:.SubPage.Another.WebHome]]"); } } diff --git a/xwiki-platform-core/xwiki-platform-ckeditor/xwiki-platform-ckeditor-test/xwiki-platform-ckeditor-test-pageobjects/src/main/java/org/xwiki/ckeditor/test/po/LinkDialog.java b/xwiki-platform-core/xwiki-platform-ckeditor/xwiki-platform-ckeditor-test/xwiki-platform-ckeditor-test-pageobjects/src/main/java/org/xwiki/ckeditor/test/po/LinkDialog.java index 843f13261d51115c4a00c0cde2da749be2a2a7b1..a5d27f2a6e3e3fd567779cb03682590079f6ff38 100644 --- a/xwiki-platform-core/xwiki-platform-ckeditor/xwiki-platform-ckeditor-test/xwiki-platform-ckeditor-test-pageobjects/src/main/java/org/xwiki/ckeditor/test/po/LinkDialog.java +++ b/xwiki-platform-core/xwiki-platform-ckeditor/xwiki-platform-ckeditor-test/xwiki-platform-ckeditor-test-pageobjects/src/main/java/org/xwiki/ckeditor/test/po/LinkDialog.java @@ -24,6 +24,7 @@ import org.openqa.selenium.By; import org.openqa.selenium.WebElement; +import org.xwiki.index.tree.test.po.DocumentPickerModal; import static org.openqa.selenium.By.cssSelector; @@ -109,6 +110,23 @@ public LinkDialog selectPageItem(String hint, String label) return this; } + public LinkDialog createLinkOfNewPage(boolean exactReference) + { + String resourceLabelName = (exactReference) ? "Create with exact reference..." : "Create in current space..."; + getResourcePicker().findElements(DROPDOWN_ITEM_SELECTOR).stream() + .filter(element -> + Objects.equals(element.findElement(cssSelector(".resource-label")).getText(), resourceLabelName)) + .findFirst().orElseThrow(() -> new NoSuchElementException(String.format("%s not found", resourceLabelName))) + .click(); + return this; + } + + public LinkPickerModal openDocumentPicker() + { + getContainer().findElement(By.cssSelector("button.resourceType")).click(); + return new LinkPickerModal(By.cssSelector(".entity-resource-picker-modal.modal")); + } + /** * Select a resource type for the resource picker (e.g., {@code "doc"}, or {@code "attachment"}). * diff --git a/xwiki-platform-core/xwiki-platform-ckeditor/xwiki-platform-ckeditor-test/xwiki-platform-ckeditor-test-pageobjects/src/main/java/org/xwiki/ckeditor/test/po/LinkPickerModal.java b/xwiki-platform-core/xwiki-platform-ckeditor/xwiki-platform-ckeditor-test/xwiki-platform-ckeditor-test-pageobjects/src/main/java/org/xwiki/ckeditor/test/po/LinkPickerModal.java new file mode 100644 index 0000000000000000000000000000000000000000..62a528cef2b52d5fa3520950d3b4efda3a216045 --- /dev/null +++ b/xwiki-platform-core/xwiki-platform-ckeditor/xwiki-platform-ckeditor-test/xwiki-platform-ckeditor-test-pageobjects/src/main/java/org/xwiki/ckeditor/test/po/LinkPickerModal.java @@ -0,0 +1,48 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.xwiki.ckeditor.test.po; + +import org.openqa.selenium.By; +import org.xwiki.index.tree.test.po.DocumentPickerModal; + +/** + * Represents the tree modal opened when chosing a link. + * + * @version $Id$ + * @since 16.8.0RC1 + */ +public class LinkPickerModal extends DocumentPickerModal +{ + /** + * Default constructor. + * @param selector selector of the container. + */ + public LinkPickerModal(By selector) + { + super(selector); + } + + @Override + public LinkTreeElement getTree() + { + return new LinkTreeElement(this.container.findElement(By.className("jstree-xwiki"))); + } +} diff --git a/xwiki-platform-core/xwiki-platform-ckeditor/xwiki-platform-ckeditor-test/xwiki-platform-ckeditor-test-pageobjects/src/main/java/org/xwiki/ckeditor/test/po/LinkTreeElement.java b/xwiki-platform-core/xwiki-platform-ckeditor/xwiki-platform-ckeditor-test/xwiki-platform-ckeditor-test-pageobjects/src/main/java/org/xwiki/ckeditor/test/po/LinkTreeElement.java new file mode 100644 index 0000000000000000000000000000000000000000..f80313211b6f9362b8ff9d1cbec4c0ac598cd040 --- /dev/null +++ b/xwiki-platform-core/xwiki-platform-ckeditor/xwiki-platform-ckeditor-test/xwiki-platform-ckeditor-test-pageobjects/src/main/java/org/xwiki/ckeditor/test/po/LinkTreeElement.java @@ -0,0 +1,87 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.xwiki.ckeditor.test.po; + +import org.openqa.selenium.By; +import org.openqa.selenium.Keys; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.xwiki.index.tree.test.po.DocumentTreeElement; +import org.xwiki.model.reference.DocumentReference; +import org.xwiki.model.reference.EntityReference; +import org.xwiki.model.reference.SpaceReference; + +/** + * Represents an element in the tree modal for selecting links. + * + * @version $Id$ + * @since 16.8.0RC1 + */ +public class LinkTreeElement extends DocumentTreeElement +{ + /** + * Creates a new instance that can be used to interact with the document tree represented by the given element. + * + * @param element the element that represents the document tree + */ + public LinkTreeElement(WebElement element) + { + super(element); + } + + private String getCreateDocumentNodeId(EntityReference reference) + { + return "addDocument:" + this.entityReferenceSerializer.serialize(reference); + } + + /** + * Check if there is a node to create a new page reference. + * @param reference the parent reference of the creation node + * @return {@code true} if there's a creation node. + */ + public boolean hasNewPageCreation(EntityReference reference) + { + return this.hasNode(getCreateDocumentNodeId(reference)); + } + + /** + * Create a new page node under the given origin, using the given name, and then open the created node. + * + * @param origin the parent node where to locate the new node + * @param name the name of the node + */ + public void createNode(DocumentReference origin, String name) + { + String createDocumentNodeId = getCreateDocumentNodeId(origin); + WebElement originNode = getDriver().findElementWithoutWaiting(By.id(createDocumentNodeId)); + originNode.findElement(By.tagName("a")).click(); + // We cannot reuse the element + getDriver().findElementWithoutWaiting(By.id(createDocumentNodeId)) + .findElement(By.tagName("input")) + .sendKeys(name, Keys.ENTER); + SpaceReference targetSpace = new SpaceReference(name, origin.getLastSpaceReference()); + DocumentReference target = new DocumentReference("WebHome", targetSpace); + getDriver().waitUntilCondition( + ExpectedConditions.presenceOfElementLocated(By.id(getNodeId(target))) + ); + openTo(getNodeId(target)); + } +} diff --git a/xwiki-platform-core/xwiki-platform-ckeditor/xwiki-platform-ckeditor-ui/src/main/resources/CKEditor/LinkNameStrategyHelper.xml b/xwiki-platform-core/xwiki-platform-ckeditor/xwiki-platform-ckeditor-ui/src/main/resources/CKEditor/LinkNameStrategyHelper.xml new file mode 100644 index 0000000000000000000000000000000000000000..beef7acd87659c0c49ec49bf987996284e4ce188 --- /dev/null +++ b/xwiki-platform-core/xwiki-platform-ckeditor/xwiki-platform-ckeditor-ui/src/main/resources/CKEditor/LinkNameStrategyHelper.xml @@ -0,0 +1,91 @@ +<?xml version="1.1" encoding="UTF-8"?> + +<!-- + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. +--> + +<xwikidoc version="1.5" reference="CKEditor.LinkNameStrategyHelper" locale=""> + <web>CKEditor</web> + <name>LinkNameStrategyHelper</name> + <language/> + <defaultLanguage/> + <translation>0</translation> + <creator>xwiki:XWiki.Admin</creator> + <parent>Main.WebHome</parent> + <author>xwiki:XWiki.Admin</author> + <contentAuthor>xwiki:XWiki.Admin</contentAuthor> + <version>1.1</version> + <title>LinkNameStrategyHelper</title> + <comment/> + <minorEdit>false</minorEdit> + <syntaxId>xwiki/2.1</syntaxId> + <hidden>true</hidden> + <content>{{template name="hierarchy_macros.vm" /}} + +{{velocity output="false"}} + +#macro (computeInfoFromReference $reference $result) + #set ($title = $reference.name) + #set ($options = { + 'excludeSelf': false, + 'limit': 4 + }) + #getHierarchyPath($reference $path $options) + #getHierarchyPathLabels($path $labels $options) + #set ($location = $stringtool.join($labels, ' / ')) + #setVariable("$result" $location) +#end + +#macro (handleSuggestionRequest) + #set ($isAdvanced = $services.user.properties.type == 'ADVANCED') + #set ($result = []) + #if ($isAdvanced) + #set ($requestedReference = $services.model.resolveDocument($request.input, 'default')) + #set ($cleanRequestedReference = $services.modelvalidation.transformEntityReference($requestedReference)) + #set ($location = $NULL) + #computeInfoFromReference($cleanRequestedReference $location) + #set ($discard = $result.add({ + 'reference': $services.model.serialize($cleanRequestedReference, 'default'), + 'type': 'fullyResolvedInput', + 'location': $location + })) + #end + #set ($parentReference = $services.model.resolveDocument($request.base)) + #set ($requestedName = $request.input) + #set ($transformedName = $services.modelvalidation.transformName($requestedName)) + #set ($spaceReference = $services.model.createSpaceReference($transformedName, $parentReference.lastSpaceReference)) + #set ($documentReference = $services.model.createDocumentReference('WebHome', $spaceReference)) + #set ($location = $NULL) + #computeInfoFromReference($documentReference $location) + #set ($discard = $result.add({ + 'reference': $services.model.serialize($documentReference, 'default'), + 'type': 'resolvedInSpace', + 'location': $location + })) + #jsonResponse($result) +#end +{{/velocity}} +{{velocity}} +#if ($xcontext.action == 'get' && $request.input != '') +#handleSuggestionRequest() +#else +Technical page to help using the name strategy when suggesting links. +#end +{{/velocity}}</content> +</xwikidoc> diff --git a/xwiki-platform-core/xwiki-platform-ckeditor/xwiki-platform-ckeditor-ui/src/main/resources/CKEditor/Translations.xml b/xwiki-platform-core/xwiki-platform-ckeditor/xwiki-platform-ckeditor-ui/src/main/resources/CKEditor/Translations.xml index 3e33dd3a3849a08bba5b79883295fd4922a5c7b9..32333ffb90c219f603ec7ee62bf3abfccec44208 100644 --- a/xwiki-platform-core/xwiki-platform-ckeditor/xwiki-platform-ckeditor-ui/src/main/resources/CKEditor/Translations.xml +++ b/xwiki-platform-core/xwiki-platform-ckeditor/xwiki-platform-ckeditor-ui/src/main/resources/CKEditor/Translations.xml @@ -303,6 +303,8 @@ entityResourceSuggester.doc.placeholder=Find a page... entityResourceSuggester.attach.placeholder=Find an attachment... +entityResourceSuggester.create.resolvedInSpace=Create in current space... +entityResourceSuggester.create.fullyResolvedInput=Create with exact reference... entityResourcePicker.select=Select entityResourcePicker.doc.title=Select Page diff --git a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-tree/xwiki-platform-index-tree-api/src/main/java/org/xwiki/index/tree/internal/nestedpages/DocumentTreeNode.java b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-tree/xwiki-platform-index-tree-api/src/main/java/org/xwiki/index/tree/internal/nestedpages/DocumentTreeNode.java index bb5e06f6fb7ff038d3cee257863008b2707d1fea..36205eedb4f698b0e011fae9090da4ef2607f6a3 100644 --- a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-tree/xwiki-platform-index-tree-api/src/main/java/org/xwiki/index/tree/internal/nestedpages/DocumentTreeNode.java +++ b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-tree/xwiki-platform-index-tree-api/src/main/java/org/xwiki/index/tree/internal/nestedpages/DocumentTreeNode.java @@ -88,7 +88,8 @@ protected EntityReference getParent(DocumentReference documentReference) throws private boolean canHaveChildDocuments(EntityReference parentReference) { - return parentReference != null && parentReference.getType() == EntityType.DOCUMENT + return parentReference != null + && (parentReference.getType() == EntityType.DOCUMENT || parentReference.getType() == EntityType.WIKI) && getDefaultDocumentName().equals(parentReference.getName()); } diff --git a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-tree/xwiki-platform-index-tree-macro/src/main/resources/XWiki/DocumentTreeMacros.xml b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-tree/xwiki-platform-index-tree-macro/src/main/resources/XWiki/DocumentTreeMacros.xml index 24dea75c3f4d579fc7e871a710ceb3dbefc60829..6e54126014a892e4fd32bfdf231300952ba04633 100644 --- a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-tree/xwiki-platform-index-tree-macro/src/main/resources/XWiki/DocumentTreeMacros.xml +++ b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-tree/xwiki-platform-index-tree-macro/src/main/resources/XWiki/DocumentTreeMacros.xml @@ -20,7 +20,7 @@ * 02110-1301 USA, or see the FSF site: http://www.fsf.org. --> -<xwikidoc version="1.3" reference="XWiki.DocumentTreeMacros" locale=""> +<xwikidoc version="1.5" reference="XWiki.DocumentTreeMacros" locale=""> <web>XWiki</web> <name>DocumentTreeMacros</name> <language/> @@ -114,7 +114,11 @@ #macro (handleDocumentTreeRequest) #if ($request.action) #if ($services.csrf.isTokenValid($request.form_token)) - $response.sendError(400, 'The specified action is not supported.') + #if ($request.action == 'create' && $request.type == 'addDocument') + #handleNewNodeCreationRequest() + #else + $response.sendError(400, 'The specified action is not supported.') + #end #elseif ($isAjaxRequest) $response.sendError(403, 'The CSRF token is missing.') #else @@ -140,6 +144,22 @@ #end #end +#macro (handleNewNodeCreationRequest) + #set ($cleanId = $stringtool.substring($request.id, $stringtool.length('document:'))) + #set ($parentReference = $services.model.resolveDocument($cleanId)) + #set ($requestedName = $request.name) + #set ($transformedName = $services.modelvalidation.transformName($requestedName)) + #set ($spaceReference = $services.model.createSpaceReference($transformedName, $parentReference.lastSpaceReference)) + #set ($documentReference = $services.model.createDocumentReference('WebHome', $spaceReference)) + #set ($data = []) + #addDocumentNode($documentReference, $data) + ## We want to allow opening the node to add another hierarchy. + #set ($data[0].children = true) + ## We want to display the actual requested name as node name. + #set ($data[0].text = $requestedName) + #jsonResponse($data) +#end + #macro (postProcessDocumentTreeData $data) ## This is just a hook to allow post processing the document tree data. #end @@ -390,14 +410,18 @@ #end #macro (addAddDocumentNode $documentReference $siblings) + ## FIXME: This URL is wrong, it should use the $documentReference as the parent for creation of the node: + ## the reference is already an existing doc, so it shouldn't be the one used for creation. #set ($discard = $siblings.add({ 'id': "addDocument:$services.model.serialize($documentReference, 'default')", - 'text': 'New page...', + 'text': $services.localization.render('index.documentTree.addDocument'), 'icon': 'fa fa-plus-circle', 'children': false, 'data': { 'type': 'addDocument', - 'validChildren': [] + 'validChildren': [], + 'hasContextMenu': true, + 'canRename': true }, 'a_attr': { 'href': $xwiki.getURL($documentReference, 'create') diff --git a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-tree/xwiki-platform-index-tree-macro/src/main/resources/XWiki/DocumentTreeTranslations.xml b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-tree/xwiki-platform-index-tree-macro/src/main/resources/XWiki/DocumentTreeTranslations.xml index 0e7bee2fa164f9e3a26445bd8d202e3663086823..02675fff4023a4c6a03466c3fca216f9ac078cab 100644 --- a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-tree/xwiki-platform-index-tree-macro/src/main/resources/XWiki/DocumentTreeTranslations.xml +++ b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-tree/xwiki-platform-index-tree-macro/src/main/resources/XWiki/DocumentTreeTranslations.xml @@ -88,6 +88,7 @@ index.documentTree.empty=No pages found index.documentTree.more={0} more ... +index.documentTree.addDocument=New page... ## Deprecated translation keys diff --git a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-tree/xwiki-platform-index-tree-test/xwiki-platform-index-tree-test-pageobjects/src/main/java/org/xwiki/index/tree/test/po/DocumentPickerModal.java b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-tree/xwiki-platform-index-tree-test/xwiki-platform-index-tree-test-pageobjects/src/main/java/org/xwiki/index/tree/test/po/DocumentPickerModal.java index ca9ece4c0bb79b6855df2b9486107b4e32961d5a..9b37d8a9eed2f38b0f1e0b247411da2332fd62b1 100644 --- a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-tree/xwiki-platform-index-tree-test/xwiki-platform-index-tree-test-pageobjects/src/main/java/org/xwiki/index/tree/test/po/DocumentPickerModal.java +++ b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-tree/xwiki-platform-index-tree-test/xwiki-platform-index-tree-test-pageobjects/src/main/java/org/xwiki/index/tree/test/po/DocumentPickerModal.java @@ -35,7 +35,7 @@ */ public class DocumentPickerModal extends BaseElement { - private WebElement container; + protected WebElement container; private DocumentTreeElement tree; diff --git a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-tree/xwiki-platform-index-tree-test/xwiki-platform-index-tree-test-pageobjects/src/main/java/org/xwiki/index/tree/test/po/DocumentTreeElement.java b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-tree/xwiki-platform-index-tree-test/xwiki-platform-index-tree-test-pageobjects/src/main/java/org/xwiki/index/tree/test/po/DocumentTreeElement.java index e0212c60c7be4a5eb69baa0ebfcd8b54be50b5f3..16edcccbcf889e9d73dd7ed6d26fd6b3d2b35980 100644 --- a/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-tree/xwiki-platform-index-tree-test/xwiki-platform-index-tree-test-pageobjects/src/main/java/org/xwiki/index/tree/test/po/DocumentTreeElement.java +++ b/xwiki-platform-core/xwiki-platform-index/xwiki-platform-index-tree/xwiki-platform-index-tree-test/xwiki-platform-index-tree-test-pageobjects/src/main/java/org/xwiki/index/tree/test/po/DocumentTreeElement.java @@ -39,7 +39,7 @@ */ public class DocumentTreeElement extends TreeElement { - private DefaultStringEntityReferenceSerializer entityReferenceSerializer = + protected DefaultStringEntityReferenceSerializer entityReferenceSerializer = new DefaultStringEntityReferenceSerializer(new DefaultSymbolScheme()); /** @@ -165,6 +165,11 @@ public TreeNodeElement getSpaceNode(String... path) return getNode(getSpaceNodeId(path)); } + public TreeNodeElement getNode(EntityReference entityReference) + { + return getNode(getNodeId(entityReference)); + } + /** * @param path the path used to locate the document * @return the corresponding document node @@ -195,7 +200,7 @@ public TreeNodeElement getAttachmentNode(AttachmentReference attachmentReference return getNode(getNodeId(attachmentReference)); } - private String getNodeId(EntityReference reference) + protected String getNodeId(EntityReference reference) { return reference.getType().getLowerCase() + ":" + this.entityReferenceSerializer.serialize(reference); } diff --git a/xwiki-platform-core/xwiki-platform-tree/xwiki-platform-tree-webjar/src/main/webjar/tree.js b/xwiki-platform-core/xwiki-platform-tree/xwiki-platform-tree-webjar/src/main/webjar/tree.js index d56dfadfbdc64a0aef7970084a61f152936099b4..8567099a6da3c2b1690c37595e1a7a622f1df5f5 100644 --- a/xwiki-platform-core/xwiki-platform-tree/xwiki-platform-tree-webjar/src/main/webjar/tree.js +++ b/xwiki-platform-core/xwiki-platform-tree/xwiki-platform-tree-webjar/src/main/webjar/tree.js @@ -523,7 +523,7 @@ define([ } params.form_token = formToken; var promise = this.jobRunner.run(url, params); - this.element.trigger('xtree.runJob', promise); + this.element.trigger('xtree.runJob', [promise, action, node, params]); return promise; } };