diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/org/xwiki/internal/document/DocumentOverrideListener.java b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/org/xwiki/internal/document/DocumentOverrideListener.java new file mode 100644 index 000000000000..74c8ae3dfb4c --- /dev/null +++ b/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/org/xwiki/internal/document/DocumentOverrideListener.java @@ -0,0 +1,77 @@ +/* + * 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.internal.document; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.xwiki.component.annotation.Component; +import org.xwiki.observation.AbstractEventListener; +import org.xwiki.observation.event.CancelableEvent; +import org.xwiki.observation.event.Event; + +import com.xpn.xwiki.doc.XWikiDocument; +import com.xpn.xwiki.internal.event.UserUpdatingDocumentEvent; + +/** + * Cancel any save that tries to save a new document if the document already exists. + * + *

+ * This can happen when the context document is set to an empty document because the user doesn't have view right, + * but the user still has, e.g., edit right. In this situation, the user would still be able to to save but it is + * hard to imagine a scenario where this would make sense. This listener therefore cancels the save in this + * situation. + *

+ * + * @version $Id$ + * @since 14.10.21 + * @since 15.5.5 + * @since 15.10.6 + */ +@Component +@Singleton +@Named(DocumentOverrideListener.NAME) +public class DocumentOverrideListener extends AbstractEventListener +{ + /** + * The unique identifier of the listener. + */ + public static final String NAME = "org.xwiki.internal.document.DocumentOverrideListener"; + + /** + * The default constructor. + */ + public DocumentOverrideListener() + { + super(NAME, new UserUpdatingDocumentEvent()); + } + + @Override + public void onEvent(Event event, Object source, Object data) + { + XWikiDocument document = (XWikiDocument) source; + XWikiDocument originalDocument = document.getOriginalDocument(); + + if (document.isNew() && originalDocument != null && !originalDocument.isNew()) { + ((CancelableEvent) event).cancel( + "The document already exists but the document to be saved is marked as new."); + } + } +} diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/META-INF/components.txt b/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/META-INF/components.txt index 930029d49af0..98dd6d265a8f 100644 --- a/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/META-INF/components.txt +++ b/xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/META-INF/components.txt @@ -278,3 +278,4 @@ org.xwiki.security.authservice.internal.StandardXWikiAuthServiceComponent org.xwiki.security.authservice.script.AuthServiceScriptService org.xwiki.evaluation.internal.DefaultObjectEvaluator org.xwiki.evaluation.internal.VelocityObjectPropertyEvaluator +org.xwiki.internal.document.DocumentOverrideListener diff --git a/xwiki-platform-core/xwiki-platform-oldcore/src/test/java/org/xwiki/internal/document/DocumentOverrideListenerTest.java b/xwiki-platform-core/xwiki-platform-oldcore/src/test/java/org/xwiki/internal/document/DocumentOverrideListenerTest.java new file mode 100644 index 000000000000..e5f1f45e1268 --- /dev/null +++ b/xwiki-platform-core/xwiki-platform-oldcore/src/test/java/org/xwiki/internal/document/DocumentOverrideListenerTest.java @@ -0,0 +1,86 @@ +/* + * 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.internal.document; + +import org.junit.jupiter.api.Test; +import org.xwiki.model.reference.DocumentReference; +import org.xwiki.observation.internal.DefaultObservationManager; +import org.xwiki.test.annotation.ComponentList; + +import com.xpn.xwiki.XWikiException; +import com.xpn.xwiki.doc.XWikiDocument; +import com.xpn.xwiki.test.MockitoOldcore; +import com.xpn.xwiki.test.junit5.mockito.InjectMockitoOldcore; +import com.xpn.xwiki.test.junit5.mockito.OldcoreTest; +import com.xpn.xwiki.test.reference.ReferenceComponentList; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; + +/** + * Unit tests for {@link DocumentOverrideListener}. + * + * @version $Id$ + */ +@OldcoreTest(mockXWiki = false) +@ReferenceComponentList +@ComponentList({ DefaultObservationManager.class, DocumentOverrideListener.class }) +class DocumentOverrideListenerTest +{ + @InjectMockitoOldcore + private MockitoOldcore oldcore; + + @Test + void savingNewDocument() throws Exception + { + DocumentReference documentReference = new DocumentReference("wiki", "space", "page"); + XWikiDocument document = new XWikiDocument(documentReference); + this.oldcore.getSpyXWiki().checkSavingDocument(mock(), document, this.oldcore.getXWikiContext()); + } + + @Test + void savingExistingDocument() throws Exception + { + DocumentReference documentReference = new DocumentReference("wiki", "space", "page"); + XWikiDocument document = new XWikiDocument(documentReference); + this.oldcore.getSpyXWiki().saveDocument(document, this.oldcore.getXWikiContext()); + document = this.oldcore.getSpyXWiki().getDocument(documentReference, this.oldcore.getXWikiContext()); + assertFalse(document.isNew()); + this.oldcore.getSpyXWiki().checkSavingDocument(mock(), document, this.oldcore.getXWikiContext()); + } + + @Test + void savingOverridingExistingDocument() throws Exception + { + DocumentReference documentReference = new DocumentReference("wiki", "space", "page"); + DocumentReference userReference = new DocumentReference("wiki", "XWiki", "user"); + XWikiDocument document = new XWikiDocument(documentReference); + this.oldcore.getSpyXWiki().saveDocument(document, this.oldcore.getXWikiContext()); + XWikiDocument newDocument = new XWikiDocument(documentReference); + XWikiException exception = assertThrows(XWikiException.class, + () -> this.oldcore.getSpyXWiki() + .checkSavingDocument(userReference, newDocument, this.oldcore.getXWikiContext())); + assertEquals("Error number 9001 in 9: User [wiki:XWiki.user] has been denied the right to save the " + + "document [wiki:space.page]. Reason: [The document already exists but the document to be saved is marked " + + "as new.]", exception.getMessage()); + } +}